开发者

How to use WixSharp to install a website and associate an AppPool

I am trying to find examples of how to use WixSharp (managed code interface to WiX) to install a website and associate an AppPool.

The steps I want to achieve are:

  1. If the website exists in IIS 6, delete it.
  2. If the AppPool exists in IIS 6, delete it.
  3. Delete the application artifacts from the destination directory.
  4. Copy the new application artifacts to the destination directory.
  5. Create the AppPool.
  6. Create the Website开发者_运维问答, linking it to the AppPool.

I have achieved this in MSBuild but that is not as useful as an MSI. Hence I am trying to "rewrite" the above in WixSharp syntax.

WixSharp apparently supports WIXIISExtension but Google has not yielded any examples yet.

How would I code the above in WixSharp?


I am using WIX for the same purpose. The application I am trying to deploy is around 300 MB and I need to create virtual directory for same, app pool, etc.

I think your requirement is same.

I would suggest WIX is really good for this. You can have screens asking user virtual directory name, Application Pool, etc.

WIX code works perfectly for IIS 5.1, 6, 7. For 7.5 you need to create a customaction. As such you can use wix to create virtual directory even for IIS 7.5 if IIS 6 compatibility mode is installed.

Uptill now I haven't faced any errors using WIX to deploy web applications.


Good question.

I am using WixSharp for my current project and I am really happy with it. It is awesome how you can avoid writing XML files by doing all with C# syntax. By the way, I don't think you are reinventing the wheel... you are speeding up the wheel with WixSharp.

As WixSharp version 1.9.6 doesn't provide creating a WebSite with an associated WebAppPool, I did it by creating a CustomWebSite.cs file. In this way, I created my Web Application using this code:

...

var project = new ManagedProject("My Project",
                       new InstallDir(@"c:\my_tool",

                            new Dir("my_frontend",
                                new Files($"{frontendDir}\\app\\*.*"),

                                    new CustomWebSite("GateKeeper", "*:31515")
                                    {
                                        WebApplication = new CustomWebApplication("DemoApp")
                                        {
                                            WebAppPool = new WebAppPool("DemoApp", "ManagedPipelineMode=Integrated;Identity=applicationPoolIdentity"),
                                        },
                                        InstallWebSite = true
                                    }
                                )
                            ),
...

Here is my CustomWebSite.cs file that I only used to create one WebSite and I am sure it could be better:

using System;
using System.Collections.Generic;
using System.Xml.Linq;
using WixSharp;
using WixSharp.CommonTasks;
using static WixSharp.WebSite;

namespace ToolBox.WixSharp
{
    /// <summary>
    /// Defines the WebSite element to be created associated to a Dir element.
    /// </summary>
    ///<example>The following is an example of associating a CustomWebSite to a Dir element.
    ///
    ///<code>
    /// var project =
    ///     new Project("My Product",
    ///         new Dir(@"%ProgramFiles%\My Company\My Product",
    ///             new Dir(@"some_dir",
    ///                 new CustomWebSite("MyApp", "*:81")
    ///                 {
    ///                     WebApplication = new CustomWebApplication("DemoApp")
    ///                     {
    ///                         WebAppPool = new WebAppPool("DemoApp", "ManagedPipelineMode=Integrated;Identity=applicationPoolIdentity")
    ///                     }
    ///                     InstallWebSite = true
    ///                 }
    ///         ...
    ///
    /// Compiler.BuildMsi(project);
    ///</code>
    /// 
    /// This code will generate something like this:
    ///<code>
    ///     <Component Id="DemoApp_WebSite" Guid="a6896bba-1818-43e0-824f-9c585b3e366b" KeyPath="yes" Win64="yes">
    ///         <iis:WebSite Id = "DemoApp_WebSite" Description="DemoApp_WebSite" Directory="INSTALLDIR.some_dir">
    ///             <iis:WebAddress Id = "WebSite_Address1" IP="*" Port="31515" />
    ///             <iis:WebApplication Id = "DemoApp_WebApplication" Name="DemoApp" WebAppPool="DemoApp_AppPool"/>
    ///         </iis:WebSite>
    ///         <iis:WebAppPool Id = "DemoApp_AppPool" Name="DemoApp" ManagedPipelineMode="Integrated" Identity="applicationPoolIdentity" />
    ///
    ///         <CreateFolder />
    ///         <RemoveFolder Id = "INSTALLDIR.some_dir" On="uninstall" />
    ///     </Component>
    /// </code>
    /// </example>
    public class CustomWebSite : WixEntity, IGenericEntity
    {
        /// <summary>
        /// Indicates if the WebSite is to be installed (created on IIS) or existing WebSite should be used to install the corresponding
        /// WebApplication. The default <see cref="InstallWebSite"/> value is <c>false</c>
        /// <para>Developers should be aware of the WebSite installation model imposed by WiX/MSI and use <see cref="InstallWebSite"/> carefully.</para>
        /// <para>If <see cref="InstallWebSite"/> value is set to <c>false</c> the parent WebApplication (<see cref="T:WixSharp.IISVirtualDir"/>)
        /// will be installed in the brand new (freshly created) WebSite or in the existing one if a site with the same address/port combination already exists
        /// on IIS). The undesirable side affect of this deployment scenario is that if the existing WebSite was used to install the WebApplication it will be
        /// deleted on IIS during uninstallation even if this WebSite has other WebApplications installed.</para>
        /// <para>The "safer" option is to set <see cref="InstallWebSite"/> value to <c>true</c> (default value). In this case the WebApplication will
        /// be installed in an existing WebSite with matching address/port. If the match is not found the installation will fail. During the uninstallation
        /// only installed WebApplication will be removed from IIS.</para>
        /// </summary>
        public bool InstallWebSite = false;

        /// <summary>
        /// Initializes a new instance of the <see cref="WebSite" /> class.
        /// </summary>
        public CustomWebSite()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomWebSite"/> class.
        /// </summary>
        /// <param name="description">The description of the web site (as it shows up in the IIS manager console).</param>
        /// <param name="addressDefinition">The address definition.</param>
        public CustomWebSite(string description, string addressDefinition)
        {
            this.Id = $"{description}_WebSite";
            this.Description = description;
            this.AddressesDefinition = addressDefinition;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomWebSite"/> class.
        /// </summary>
        /// <param name="id">The id</param>
        /// <param name="description">The description of the web site (as it shows up in the IIS manager console).</param>
        /// <param name="addressDefinition">The address definition.</param>
        public CustomWebSite(Id id, string description, string addressDefinition)
        {
            this.Id = id;
            this.Description = description;
            this.AddressesDefinition = addressDefinition;
        }

        internal void ProcessAddressesDefinition()
        {
            if (!AddressesDefinition.IsEmpty())
            {
                List<WebAddress> addressesToAdd = new List<WebAddress>();

                foreach (string addressDef in AddressesDefinition.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
                {
                    try
                    {
                        string[] tokens = addressDef.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        string address = tokens[0];
                        string port = tokens[1];
                        if (tokens[1].ContainsWixConstants())
                        {
                            addressesToAdd.Add(new WebAddress { Address = address, AttributesDefinition = "Port=" + port });
                        }
                        else
                        {
                            addressesToAdd.Add(new WebAddress { Address = address, Port = Convert.ToInt32(port) });
                        }
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Invalid AddressesDefinition", e);
                    }
                }

                this.addresses = addressesToAdd.ToArray();
            }

        }

        /// <summary>
        /// References a WebAppPool instance to use as the application pool for this application in IIS 6 applications.
        /// </summary>
        public string WebAppPool; //WebApplication element attribute

        /// <summary>
        /// Specification for auto-generating the <see cref="T:WebSite.WebAddresses"/> collection.
        /// <para>If <see cref="AddressesDefinition"/> is specified, the existing content of <see cref="Addresses"/> will be ignored
        /// and replaced with the auto-generated one at compile time.</para>
        /// </summary>
        /// <example>
        /// <c>webSite.AddressesDefinition = "*:80;*90";</c> will be parsed and converted to an array of <see cref="T:WixSharp.WebSite.WebAddress"/> as follows:
        /// <code>
        /// ...
        /// webSite.Addresses = new []
        ///     {
        ///         new WebSite.WebAddress
        ///         {
        ///             Address = "*",
        ///             Port = 80
        ///         },
        ///         new WebSite.WebAddress
        ///         {
        ///             Address = "*",
        ///             Port = 80
        ///         }
        ///     }
        /// </code>
        /// </example>
        public string AddressesDefinition = "";

        //// The iis:WebSite/@Directory attribute must be specified when the element has a Component as an ancestor..
        //public string Directory = "";


        /// <summary>
        /// Reference to a WebApplication that is to be installed as part of this web site.
        /// </summary>
        public CustomWebApplication WebApplication = null;

        /// <summary>
        /// Collection of <see cref="T:WebSite.WebAddresses"/> associated with website.
        /// <para>
        /// The user specified values of <see cref="Addresses"/> will be ignored and replaced with the
        /// auto-generated addresses if <see cref="AddressesDefinition"/> is specified either directly or via appropriate <see cref="WebSite"/> constructor.
        /// </para>
        /// </summary>
        public WebAddress[] Addresses
        {
            get
            {
                ProcessAddressesDefinition();
                return addresses;
            }
            set
            {
                addresses = value;
            }
        }

        /// <summary>
        /// This class defines WebAppPool WiX element. It is used to specify the application pool for this application in IIS 6 applications.
        /// </summary>
        public partial class CustomWebApplication : WixEntity
        {
            /// <summary>
            /// References a WebAppPool instance to use as the application pool for this application in IIS 6 applications.
            /// </summary>
            public WebAppPool WebAppPool; //WebApplication element attribute

            /// <summary>
            /// Initializes a new instance of the <see cref="WebApplication"/> class.
            /// </summary>
            /// <param name="name">The name.</param>
            /// <param name="attributesDefinition">The attributes definition. This parameter is used to set encapsulated <see cref="T:WixSharp.WixEntity.AttributesDefinition"/>.</param>
            public CustomWebApplication(string name, string attributesDefinition)
            {
                base.Id = $"{name}_WebApplication";
                base.Name = name;
                base.AttributesDefinition = attributesDefinition;
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="WebAppPool"/> class.
            /// </summary>
            /// <param name="name">The name.</param>
            public CustomWebApplication(string name)
            {
                base.Id = $"{name}_WebApplication";
                base.Name = name;
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="WebAppPool"/> class.
            /// </summary>
            public CustomWebApplication()
            {
            }
        }

        WebAddress[] addresses = new WebAddress[0];

        /// <summary>
        /// Primary key used to identify this particular entry.
        /// </summary>
        [Xml]
        public new string Id
        {
            get
            {
                return base.Id;
            }
            set
            {
                base.Id = value;
            }
        }

        /// <summary>
        /// The value to set into the environment variable. If this attribute is not set, the environment variable is removed
        /// during installation if it exists on the machine.
        /// </summary>
        [Xml]
        public string Description;

        /// <summary>
        /// Defines the installation <see cref="Condition"/>, which is to be checked during the installation to
        /// determine if the registry value should be created on the target system.
        /// </summary>
        public Condition Condition;

        /// <summary>
        /// Adds itself as an XML content into the WiX source being generated from the <see cref="WixSharp.Project"/>.
        /// See 'Wix#/samples/Extensions' sample for the details on how to implement this interface correctly.
        /// </summary>
        /// <param name="context">The context.</param>
        public void Process(ProcessingContext context)
        {
            // IIS namespace
            XNamespace ns = WixExtension.IIs.ToXNamespace();

            XElement component = this.CreateAndInsertParentComponent(context);
            component.Add(this.ToXElement(ns + "WebSite"));

            XElement webSiteElement = component.FindAll("WebSite")[0];

            if (webSiteElement.Parent.Name == "Component" && webSiteElement.Parent.Parent.Name == "Directory")
            {
                // Add attributes for WebSite element
                webSiteElement.AddAttributes($"Directory={webSiteElement.Parent.Parent.Attribute("Id").Value}");
            }

            if (Addresses != null)
            {
                int index = 1;

                // Generates the XML fragment for WebAddress element
                foreach (WebAddress address in Addresses)
                {
                    webSiteElement.AddElement(new XElement(ns + "WebAddress",
                                            new XAttribute("Id", $"WebSite_Address{index}"),
                                            new XAttribute("IP", "*"),
                                            new XAttribute("Port", address.Port)));
                    index++;
                }
            }

            if (WebApplication != null)
            {
                // Generates the XML fragment for WebApplication element
                XElement webApplicationElement = new XElement(ns + "WebApplication",
                                        new XAttribute("Id", WebApplication.Id),
                                        new XAttribute("Name", this.WebApplication.Name));

                webSiteElement.AddElement(webApplicationElement);

                if (WebApplication.WebAppPool != null)
                {
                    WebApplication.WebAppPool.Id = $"{WebApplication.WebAppPool.Name}_WebAppPool";
                    webApplicationElement.SetAttribute($"WebAppPool={WebApplication.WebAppPool.Id}");

                    // Generates the XML fragment for WebAppPool element
                    webSiteElement.Parent.AddElement(new XElement(ns + "WebAppPool",
                                            new XAttribute("Id", WebApplication.WebAppPool.Id),
                                            new XAttribute("Name", WebApplication.WebAppPool.Name),
                                            new XAttribute("ManagedPipelineMode", "Integrated"),
                                            new XAttribute("Identity", "applicationPoolIdentity")));
                }
            }

            if (Condition != null)
            {
                component.AddElement(new XElement("Condition", Condition.ToXValue())
                         .AddAttributes(Condition.Attributes));
            }
        }
    }
}

You also have other way to solve your problem, by generating the WIX xml file following WebSite definitions and using XML injection as indicated in WixSharp IIS Sample with XMLInjection where you can subscribe to the WixSourceGenerated event.

project.WixSourceGenerated += Compiler_WixSourceGenerated;

Remember that WixSharp generates the Wix XML definitifion file, and you can modify this XML file after the WixSourceGenerated event.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜