I'm attempting to build up an MSBuild file to deploy a set of several websites to multiple environments. At the moment, I've built the project and have it dumped into a directory.
I'm using the variable $(AdminConsoleConfigFilePath) to refer to the path of the web.config file in this directory. My substitutions file is denoted by the variable $(AdminConsoleConfigReplacementFile). I've set up the following task in my msbuild file for the project:
<Target Name="AdminConsoleConfig" DependsOnTargets="BuildAdminConsole">
<Message Text="Beginning configuration for AdminConsole at $(AdminConsoleConfigFilePath) using $(AdminConsoleConfigReplacementFile)" Importance="high"/>
<XmlMassUpdate ContentFile="$(AdminConsoleConfigFilePath)" SubstitutionsFile="$(AdminConsoleConfigurationReplacementPath)"
ContentRoot="$(ConfigurationContentReplacementXPathRoot)" SubstitutionsRoot="$(SubstitutionsXPathRoot)"/>
<Message Text="Finished configuration for AdminConsole using $(AdminConsoleConfigReplacementFile)" Importance="high"/>
`
The values of the other variables here are declared as follows:
<ConfigurationContentReplacementXPathRoot>/configuration</ConfigurationContentReplacementXPathRoot>
<SubstitutionsXPathRoot>/configuration/substitutions/$(Environment)</SubstitutionsXPathRoot>
Environment is the name of where the build is targeted. In this case, it's "qa".
My web.config has the typical hibernate-config section as follows:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.isolation">ReadCommitted</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">[redacted]</property>
<property name="show_sql">false</property>
<property name="generate_statistics">true</property>
<property name="current_session_context_class">web</property>
<property name="adonet.batch_size">250</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<mapping assembly="[redacted]"/>
</session-factory>
</hibernate-configuration>
My replacements file looks like the following:
<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
<substitutions>
<dev></dev>
<qa>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property xmu:key="name" xmu:Transform="Replace" xmu:Locator="Match(name)" name="connection.connection_string">[redacted-qa]</property>
</session-factory>
</hibernate-configuration>
</qa>
Now, the connection.connection_string property is not being replaced (in the above code, I also could never get the closing tags for configuration and substitution to show in the editor on this site - might be a firefox issue). Any ideas? I've tried the following:
- removing the namespace declaration for nhibernate-configuration from the web.config in question and the replacement file (both together and individually).
- Adding a namespace prefix to the nhibernate declaration and prefixing the relevant nodes and attributes with it. Again, tried on both the web.config and the replacement file, both individually and in combination.
- Attempted to use the latest nightly build from the MSBuild contrib project. Not only did this not work, it broke other things as well.
- Tried using an XPath-based xmu:Locator. Also tried both with and without the xmu:key value (and even tried replacing it with xmu:Key).
I'm absolutely stumped. Does the XMLMassUpdate thing work at all? We'd like to keep the qa and production versions of the web.config (or at least the 开发者_运维问答various sensitive bits) out of the main body of the code in case we get a junior dev or contractor in, so the current method of the web.qa.config and web.prod.config isn't really viable at this point. Any thoughts on how to implement this (besides the obvious one of just keeping copies of the full web.config for each environment and copying over after the build)?
Thanks, Will
I haven't tried using the xmlmassupdate approach to configuration substitution myself. But there is an alternate approach which is to specify each configuration update individually in your target using the XmlFile/UpdateElement method, e.g.
<MSBuild.ExtensionPack.Xml.XmlFile
TaskAction="UpdateElement"
File="$(AdminConsoleConfigFilePath)"
XPath="/hibernate-configuration/session-factory/property[@name='connection.connection_string']"
InnerText="Initial Catalog=MyDatabase;Data Source=$(DatabaseServerInstance)"
/>
You then put your configurations in the header of your targets file, e.g.
<Choose>
<When Condition="$(Environment)=='DEV'">
<PropertyGroup>
<DatabaseServerInstance>DEVSERVER</DatabaseServerInstance>
</PropertyGroup>
</When>
<When Condition="$(Environment)=='QA'">
<PropertyGroup>
<DatabaseServerInstance>QASERVER</DatabaseServerInstance>
</PropertyGroup>
</When>
<When Condition="$(Environment)=='PROD'">
<PropertyGroup>
<DatabaseServerInstance>PRODSERVER</DatabaseServerInstance>
</PropertyGroup>
</When>
</Choose>
You can also put the "Choose" block into a separate file and include it from your targets file. I personally prefer this approach because it has allowed us to centralise the list of environment-specific properties, which can then be used by multiple targets files (e.g. if you have multiple applications to deploy that use the same database server in each environment).
精彩评论