デバッグビルドとリリースビルド用に異なる構成ファイルを持つ.NETアプリケーションがあります。例えば。デバッグapp.configファイルは開発を指します SQL Server デバッグが有効になっており、リリースターゲットはライブSQL Serverを指します。他の設定もあり、その一部はデバッグ/リリースで異なります。
現在、2つの個別の構成ファイル(debug.app.configおよびrelease.app.config)を使用しています。プロジェクトにビルドイベントがあり、これがリリースビルドの場合はrelease.app.configをapp.configにコピーし、そうでない場合はdebug.app.configをapp.configにコピーします。
問題は、アプリケーションがsettings.settingsファイルから設定を取得しているように見えるため、Visual Studioでsettings.settingsを開き、設定が変更されたことを通知するため、変更を受け入れ、settings.settingsを保存して再構築して正しい設定を使用するようにします。
同様の効果を達成するためのより良い/推奨/好ましい方法はありますか?または同様に、私はこれに完全に間違ったアプローチをしましたか、そしてより良いアプローチがありますか?
環境によって異なる可能性のある構成は、アプリケーションレベルではなく、マシンレベルに保存する必要があります。 (設定レベルの詳細)
これらは、マシンレベルで通常保存する構成要素の種類です。
各環境(開発者、統合、テスト、ステージ、ライブ)がc:\ Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIGディレクトリに独自の設定を持つ場合、昇格できますビルド後の変更なしの環境間でのアプリケーションコード。
そして明らかに、マシンレベルのCONFIGディレクトリのコンテンツは、アプリとは異なるリポジトリまたは異なるフォルダー構造でバージョン管理されます。 configSource をインテリジェントに使用することにより、.configファイルをよりソース管理に適したものにすることができます。
私はこれを7年以上にわたり、25以上の企業の200を超えるASP.NETアプリケーションで行ってきました。 (自慢しようとせず、このアプローチが機能しない状況を見たことがないことを知らせたいだけです。)
これは、Settings.settingsおよびApp.configを処理する一部の人々に役立つ場合があります。VisualStudio(私の場合はVisual Studio 2008)のSettings.settingsグリッドの値を編集するときに、[プロパティ]ペインのGenerateDefaultValueInCode属性に注意してください。
GenerateDefaultValueInCodeをTrue(ここではTrueがデフォルトです!)に設定すると、デフォルト値がEXE(またはDLL)にコンパイルされ、プレーンテキストエディターで開くとファイルに埋め込まれていることがわかります。
コンソールアプリケーションで作業していましたが、EXEにデフォルトがある場合、アプリケーションは常に同じディレクトリ内の構成ファイルの場所を無視しました!非常に悪夢であり、インターネット全体でこれに関する情報はありません。
関連する質問がここにあります:
構成ファイルには、設定をオーバーライドする方法が付属しています。
<appSettings file="Local.config">
2つ(またはそれ以上)のファイルをチェックインする代わりに、デフォルトの構成ファイルのみをチェックインし、各ターゲットマシンで、特定のマシンのオーバーライドを含むappSettingsセクションのみを含むLocal.configを配置します。
構成セクションを使用している場合、同等のものは次のとおりです。
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 Project sの恩恵を受けることができるようです。
これにより、ビルド構成に応じてweb.configファイルのセクションを更新/変更するように指示できます。
簡単な概要/サンプルについては、 Scott Guのこのブログエントリ をご覧ください。
以前はWeb Deploymentプロジェクトを使用していましたが、その後NAntに移行しました。異なる設定ファイルを分岐してコピーする代わりに、現在、ビルドスクリプトに構成値を直接埋め込み、xmlpokeタスクを介して構成ファイルにそれらを挿入します。
<xmlpoke
file="${stagingTarget}/web.config"
xpath="/configuration/system.web/compilation/@debug"
value="true"
/>
どちらの場合でも、設定ファイルには開発者が望む値を何でも設定でき、本番システムを壊すことなく開発環境内で問題なく機能します。開発者が物事をテストするときにビルドスクリプト変数を勝手に変更する可能性は低いことがわかったため、プロセスの早い段階で各変数を追加する必要がありますが、偶然の誤った設定は他の手法よりもまれですデフォルトでは、dev値はprodにプッシュされません。
私の現在の雇用主は、まずdev。レベル(デバッグ、ステージ、ライブなど)をmachine.configファイルに入れることでこの問題を解決しました。その後、彼らはそれを拾って正しい設定ファイルを使用するコードを書きました。これにより、アプリが展開された後の間違った接続文字列の問題が解決しました。
彼らは最近、machine.config値の値から正しい接続文字列を送り返す中央Webサービスを作成しました。
これが最善の解決策ですか?おそらくそうではありませんが、彼らにとってはうまくいきます。
私がうまくいったソリューションの1つは、WebDeploymentProjectを使用することでした。私のサイトには2/3のweb.configファイルがあり、選択した構成モード(リリース/ステージング/ etc ...)に応じて公開時に、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>
Projには、dev、qa、uat、prodの構成を維持する必要があるのと同じ問題があります。ここに我々が従ったものがあります(MSBuildに精通している場合にのみ適用されます):
MSBuildをMSBuild Communityタスク拡張機能とともに使用します。開始する正しいノードを指定すると、XMLファイルのエントリを「一括更新」できる「XmlMassUpdate」タスクが含まれます。
実装する:
1)dev envエントリを持つ1つの設定ファイルが必要です。これは、ソリューションの構成ファイルです。
2)「Substitutions.xml」ファイルが必要です。このファイルには、環境ごとに異なる(appSettingsおよびConnectionStringsがほとんど)エントリのみが含まれています。環境全体で変化しないエントリは、このファイルに入れる必要はありません。ソリューションのweb.configファイルに存在し、タスクに影響されません
3)ビルドファイルで、XML一括更新タスクを呼び出して、適切な環境をパラメーターとして提供します。
以下の例を参照してください。
<!-- 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>
環境に応じて、「$ Environment」を「QA」または「Prod」に置き換えます。あなたのために構築しています。回復不能な間違いを避けるために、実際の構成ファイル自体ではなく、構成ファイルのコピーで作業する必要があることに注意してください。
ビルドファイルを実行し、更新された構成ファイルを展開環境に移動するだけで完了です。
より良い概要については、これを読んでください:
ここに別のソリューションがあります: ASP.NETでDevelopment/UAT/Prod環境間で構成を切り替える最適な方法? XSLTを使用してWebを変換.config。
NAntの使用に関する良い例もいくつかあります。
あなたのように、私は「マルチ」app.configも設定しました-例えば、app.configDEV、app.configTEST、app.config.LOCAL。私はいくつかの優れた代替案を提案していますが、あなたがそれがあなたのために働く方法が好きなら、私は以下を追加します:
私は<appSettings>
<add key = "Env" value = "[Local] "/>
各アプリについて、タイトルバーのUIにこれを追加します。ConfigurationManager.AppSettings.Get( "Env");から
Configの名前をターゲットの名前に変更します(4つのイベントに対する多くのデータベース/ wcf構成を持つ8つのアプリを含むプロジェクトがあります)。それぞれにclickonceを使用してデプロイするには、プロジェクトの4つのシーティングを変更してから行ってください。 (これを自動化したい)
私の唯一の落とし穴は、変更後に「すべて削除」することを覚えておくことです。古い設定は手動で名前を変更した後に「スタック」するからです。 (あなたがsetting.settingの問題を修正すると思います)。
私はこれが本当にうまくいくと思います(いつかMSBuild/NAntを見る時間ができます)
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を介してデバッグすると、「AppSetting.json」を使用するように環境が開発に設定されます。 Windowsでホスティング環境変数を設定する方法を理解するには、このWebサイトを参照してください。
App.configは、主にWindowsフォーム、Windowsサービス、コンソールアプリ、およびWPFアプリケーションに使用される.NETで使用される別の構成ファイルです。コンソールアプリケーションを介してAsp.Net Coreホスティングを開始すると、app.configも使用されます。
構成ファイルの選択は、サービス用に選択したホスティング環境によって決まります。 IISを使用してサービスをホストしている場合は、Web.configファイルを使用します。他のホスティング環境を使用している場合は、App.configファイルを使用します。 構成ファイルを使用したサービスの構成ドキュメント を参照してください ASP.NET Coreでの構成 も確認してください
上記のasp.netと書かれているので、データベースに設定を保存し、カスタムキャッシュを使用してそれらを取得してみませんか?
ここで行った理由は、実稼働ファイルを継続的に更新する許可を取得するよりも、継続的にデータベースを更新する方が簡単だからです。
カスタムキャッシュの例:
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
}
}