我有一个.NET应用程序,它具有用于Debug和Release版本的不同配置文件。例如。 debug app.config文件指向一个开发SQL Server,它启用了调试,发布目标指向实时SQL Server。还有其他设置,其中一些在调试/发布中有所不同。
我目前使用两个单独的配置文件(debug.app.config和release.app.config)。我在项目上有一个构建事件,它说如果这是一个发布版本,那么将release.app.config复制到app.config,否则将debug.app.config复制到app.config。
问题是应用程序似乎从settings.settings文件中获取其设置,因此我必须在Visual Studio中打开settings.settings,然后提示我设置已更改,因此我接受更改,保存settings.settings并具有重建以使其使用正确的设置。
是否有更好/推荐/首选的方法来实现类似的效果?或者同样,我接近这个完全错误并且有更好的方法吗?
任何可能因环境而异的配置应存储在计算机级别,而不是应用程序级别。 (More info on configuration levels.)
这些是我通常在机器级别存储的配置元素:
当每个环境(开发人员,集成,测试,阶段,实时)在c:\ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG目录中都有自己的唯一设置时,您可以在环境之间升级应用程序代码而无需任何环境构建后的修改。
很明显,机器级CONFIG目录的内容在不同的存储库或与您的应用程序不同的文件夹结构中进行版本控制。您可以通过智能使用configSource使您的.config文件更加源代码控制。
我已经这样做了7年,在25个以上不同公司的200多个ASP.NET应用程序上。 (不要试图吹嘘,只是想让你知道我从未见过这种方法不起作用的情况。)
我们的项目有同样的问题,我们必须维护dev,qa,uat和prod的配置。以下是我们遵循的内容(仅适用于您熟悉MSBuild):
将MSBuild与MSBuild社区任务扩展一起使用。它包括'XmlMassUpdate'任务,一旦您为其提供正确的节点,它就可以“批量更新”任何XML文件中的条目。
实施:
1)你需要有一个配置文件,它将包含你的dev env条目;这是解决方案中的配置文件。
2)您需要有一个'Substitutions.xml'文件,该文件仅包含每个环境中不同的条目(主要是appSettings和ConnectionStrings)。不要跨环境更改的条目不需要放在此文件中。它们可以存在于解决方案的web.config文件中,并且不会被任务触及
3)在构建文件中,只需调用XML批量更新任务并提供正确的环境作为参数。
见下面的例子:
Best way to switch configuration between Development/UAT/Prod environments in ASP.NET?
根据env将'$ Environment'替换为'QA'或'Prod'。你正在建设。请注意,您应该处理配置文件的副本而不是实际的配置文件本身,以避免任何可能的不可恢复的错误。
只需运行构建文件,然后将更新的配置文件移动到部署环境即可完成!
要获得更好的概述,请阅读:
<!-- Actual Config File -->
<appSettings>
<add key="ApplicationName" value="NameInDev"/>
<add key="ThisDoesNotChange" value="Do not put in substitution file" />
</appSettings>
<!-- Substitutions.xml -->
<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
<substitutions>
<QA>
<appSettings>
<add xmu:key="key" key="ApplicationName" value="NameInQA"/>
</appSettings>
</QA>
<Prod>
<appSettings>
<add xmu:key="key" key="ApplicationName" value="NameInProd"/>
</appSettings>
</Prod>
</substitutions>
</configuration>
<!-- Build.xml file-->
<Target Name="UpdateConfigSections">
<XmlMassUpdate ContentFile="Path\of\copy\of\latest web.config" SubstitutionsFile="path\of\substitutionFile" ContentRoot="/configuration" SubstitutionsRoot="/configuration/substitutions/$(Environment)" />
</Target>
和你一样,我也设置了'multi'app.config - 例如app.configDEV,app.configTEST,app.config.LOCAL。我看到了一些很好的替代建议,但是如果你喜欢它适合你的方式,我会添加以下内容:
我有一个
http://blogs.microsoft.co.il/blogs/dorony/archive/2008/01/18/easy-configuration-deployment-with-msbuild-and-the-xmlmassupdate-task.aspx
每个应用程序的<appSettings>
我将其添加到标题栏中的UI:来自ConfigurationManager.AppSettings.Get(“Env”);
我只是将配置重命名为我正在瞄准的那个(我有一个包含8个应用程序的项目,其中包含大量数据库/ wcf配置,针对4个扩展)。要使用clickonce进行部署,我会更改项目中的4个seetings并继续。 (我喜欢自动化)
我唯一的问题是在更改后记得“全部清理”,因为旧的配置在手动重命名后“卡住”。 (我认为会解决你的设置问题。)
我觉得这个效果很好(有一天我会抽空看看MSBuild / NAnt)
它上面说asp.net,为什么不在数据库中保存你的设置并使用自定义缓存来检索它们呢?
我们在这里做的原因是因为更新连续数据库比获得持续更新生产文件的权限更容易(对我们而言)。
自定义缓存的示例:
<add key = "Env" value = "[Local] "/>
如果要在IIS上托管应用程序,则需要Web.config。 Web.config是IIS的强制配置文件,用于配置它在Kestrel前面作为反向代理的行为方式。如果要在IIS上托管它,则必须维护web.config。
对于与IIS无关的所有其他内容,您使用AppSetting.json。 AppSetting.json用于Asp.Net Core托管。 ASP.NET Core使用“ASPNETCORE_ENVIRONMENT”环境变量来确定当前环境。默认情况下,如果在未设置此值的情况下运行应用程序,它将自动默认为生产环境并使用“AppSetting.production.json”文件。当您通过Visual Studio进行调试时,它将环境设置为Development,因此它使用“AppSetting.json”。请参阅此网站以了解如何在Windows上设置托管环境变量。
App.config是.NET使用的另一个配置文件,主要用于Windows窗体,Windows服务,控制台应用程序和WPF应用程序。当您通过控制台应用程序启动Asp.Net Core托管时,也会使用app.config。
配置文件的选择取决于您为服务选择的托管环境。如果您使用IIS来托管服务,请使用Web.config文件。如果您使用的是任何其他托管环境,请使用App.config文件。查看public enum ConfigurationSection
{
AppSettings
}
public static class Utility
{
#region "Common.Configuration.Configurations"
private static Cache cache = System.Web.HttpRuntime.Cache;
public static String GetAppSetting(String key)
{
return GetConfigurationValue(ConfigurationSection.AppSettings, key);
}
public static String GetConfigurationValue(ConfigurationSection section, String key)
{
Configurations config = null;
if (!cache.TryGetItemFromCache<Configurations>(out config))
{
config = new Configurations();
config.List(SNCLavalin.US.Common.Enumerations.ConfigurationSection.AppSettings);
cache.AddToCache<Configurations>(config, DateTime.Now.AddMinutes(15));
}
var result = (from record in config
where record.Key == key
select record).FirstOrDefault();
return (result == null) ? null : result.Value;
}
#endregion
}
namespace Common.Configuration
{
public class Configurations : List<Configuration>
{
#region CONSTRUCTORS
public Configurations() : base()
{
initialize();
}
public Configurations(int capacity) : base(capacity)
{
initialize();
}
public Configurations(IEnumerable<Configuration> collection) : base(collection)
{
initialize();
}
#endregion
#region PROPERTIES & FIELDS
private Crud _crud; // Db-Access layer
#endregion
#region EVENTS
#endregion
#region METHODS
private void initialize()
{
_crud = new Crud(Utility.ConnectionName);
}
/// <summary>
/// Lists one-to-many records.
/// </summary>
public Configurations List(ConfigurationSection section)
{
using (DbCommand dbCommand = _crud.Db.GetStoredProcCommand("spa_LIST_MyConfiguration"))
{
_crud.Db.AddInParameter(dbCommand, "@Section", DbType.String, section.ToString());
_crud.List(dbCommand, PopulateFrom);
}
return this;
}
public void PopulateFrom(DataTable table)
{
this.Clear();
foreach (DataRow row in table.Rows)
{
Configuration instance = new Configuration();
instance.PopulateFrom(row);
this.Add(instance);
}
}
#endregion
}
public class Configuration
{
#region CONSTRUCTORS
public Configuration()
{
initialize();
}
#endregion
#region PROPERTIES & FIELDS
private Crud _crud;
public string Section { get; set; }
public string Key { get; set; }
public string Value { get; set; }
#endregion
#region EVENTS
#endregion
#region METHODS
private void initialize()
{
_crud = new Crud(Utility.ConnectionName);
Clear();
}
public void Clear()
{
this.Section = "";
this.Key = "";
this.Value = "";
}
public void PopulateFrom(DataRow row)
{
Clear();
this.Section = row["Section"].ToString();
this.Key = row["Key"].ToString();
this.Value = row["Value"].ToString();
}
#endregion
}
}
并查看Configuring Services Using Configuration Files documentation
这可能对某些处理Settings.settings和App.config的人有所帮助:在编辑Visual Studio中的Settings.settings网格中的任何值(在我的情况下为Visual Studio 2008)时,请注意“属性”窗格中的GenerateDefaultValueInCode属性。
如果将GenerateDefaultValueInCode设置为True(True是默认值!),则默认值将编译到EXE(或DLL)中,当您在纯文本编辑器中打开它时,可以在文件中找到它。
我正在研究控制台应用程序,如果我在EXE中有默认值,应用程序总是忽略配置文件位于同一目录中!相当噩梦,并没有在整个互联网上的这方面的信息。
这里有一个相关的问题:
配置文件有一种覆盖设置的方法:
<appSettings file="Local.config">
您只需签入默认配置文件,然后在每台目标计算机上放置一个Local.config,而只包含具有该特定计算机覆盖的appSettings部分,而不是签入两个(或更多)文件。
如果您使用的是配置部分,则等效于:
configSource="Local.config"
当然,从其他机器制作所有Local.config文件的备份副本并在某处检查它们是个好主意,但不能作为实际解决方案的一部分。每个开发人员在Local.config文件上放置一个“忽略”,因此它不会被检入,这会覆盖其他人的文件。
(你实际上不必称它为“Local.config”,这就是我使用的)
从我正在阅读的内容来看,听起来您正在使用Visual Studio进行构建过程。您是否考虑过使用MSBuild和Nant?
Nant的xml语法有点奇怪但是一旦你理解了它,做你提到的就变得非常微不足道了。
<target name="build">
<property name="config.type" value="Release" />
<msbuild project="${filename}" target="Build" verbose="true" failonerror="true">
<property name="Configuration" value="${config.type}" />
</msbuild>
<if test="${config.type == 'Debug'}">
<copy file=${debug.app.config}" tofile="${app.config}" />
</if>
<if test="${config.type == 'Release'}">
<copy file=${release.app.config}" tofile="${app.config}" />
</if>
</target>
对我来说,似乎你可以受益于Visual Studio 2005 Web Deployment Projects。
有了它,您可以告诉它根据构建配置更新/修改web.config文件的各个部分。
请查看this blog entry from Scott Gu以获得快速概览/样本。
我们曾经使用过Web部署项目,但后来迁移到了NAnt。我们目前将配置值直接嵌入到构建脚本中,并通过xmlpoke任务将它们注入到我们的配置文件中,而不是分支和复制不同的设置文件:
<xmlpoke
file="${stagingTarget}/web.config"
xpath="/configuration/system.web/compilation/@debug"
value="true"
/>
在任何一种情况下,您的配置文件都可以包含您想要的任何开发人员值,并且它们将在您的开发环境中正常工作,而不会破坏您的生产系统。我们发现开发人员在测试时不太可能随意更改构建脚本变量,因此偶然的错误配置比我们尝试过的其他技术更少见,尽管仍然需要在过程的早期添加每个var,以便默认情况下,dev值不会被推送到prod。
我当前的雇主首先将dev级别(debug,stage,live等)放在machine.config文件中解决了这个问题。然后他们编写代码来选择并使用正确的配置文件。这解决了部署应用程序后错误连接字符串的问题。
他们最近写了一个中央Web服务,它从machine.config值中的值发回正确的连接字符串。
这是最好的解决方案吗?可能不是,但它适用于他们。
其中一个对我有用的解决方案是使用WebDeploymentProject。我在我的站点中有2/3个不同的web.config文件,并且在发布时,取决于所选的配置模式(发布/暂存/等...),我将复制Web.Release.config并将其重命名为web。在AfterBuild事件中配置,并删除我不需要的那些(例如Web.Staging.config)。
<Target Name="AfterBuild">
<!--Web.config -->
<Copy Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Release.config" DestinationFiles="$(OutputPath)\Web.config" />
<Copy Condition=" '$(Configuration)|$(Platform)' == 'Staging|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Staging.config" DestinationFiles="$(OutputPath)\Web.config" />
<!--Delete extra files -->
<Delete Files="$(OutputPath)\Web.Release.config" />
<Delete Files="$(OutputPath)\Web.Staging.config" />
<Delete Files="@(ProjFiles)" />
</Target>
你会在这里找到另一个解决方案:qazxsw poi,它使用XSLT转换web.config。
使用NAnt也有一些很好的例子。