VS2008のsetting-designerを使用してアプリケーション設定を管理します。
「user.configファイルの正確なパスは次のようになります。」
<Profile Directory>\<Company Name>\
<App Name>_<Evidence Type>_<Evidence Hash>\<Version>\user.config
このパスをカスタマイズする方法はありますか?私はこのようなものを好むでしょう:
<Profile Directory>\<Company Name>\
<App Name>\<Version>\user.config
新しく作成したフォルダ(「TestCompany」->「Test_Company」)の「CompanyName」で空白がアンダースコアに置き換えられていることに気付きました。私は本当にこの動作をオフにしたいと思います。
ご存知のとおり、新しいXMLベースの設定ハンドラーを作成することはできますが、設定デザイナーを使用したいと思います。
カスタム設定プロバイダーの実装に関する適切な情報を見つけるのは簡単ではなかったので、以下に完全な実装を含めます(下)。user.configファイルの形式と、.settingsデザイナー内の機能は保持されます。 。少し片付けられる部分があると思いますので、気にしないでください:)
他の人と同じように、user.configファイルの場所を変更し、新しいインストールのデフォルト値の作成など、デザイナーで.settingsファイルを操作する楽しさと空想を手に入れたいと思いました。重要なのは、私たちのアプリには、すでに決定したパス(appData\local\etc)に他の保存された設定オブジェクトもあり、複数の場所にアーティファクトが必要ないことです。
コードは私が望むよりもはるかに長いですが、私が見つけることができる短い答えはありません。パスを制御できるようにするだけでもやや苦痛に思えますが、カスタム設定プロバイダーを作成すること自体は非常に強力です。次の実装を変更して、カスタム暗号化ファイルやデータベースなど、ほぼどこにでもデータを保存したり、Webサービスとやり取りしたりできます。
私が読んだところによると、Microsoftは構成ファイルへのパスを構成可能にするつもりはありません。彼らがそれを許すのは怖いと言うとき、私は彼らの言葉を受け入れます。 ( this )の投稿を参照してください。残念ながら、自分でやりたい場合は、自分で実装する必要があります SettingsProvider 。
ここに行きます。
プロジェクトの参照をSystem.Configurationに追加します。これは、SettingsProviderを実装するために必要です。 。
Easy bit... SettingsProviderを実装するクラスを作成し、 ctrl +。 を使用して支援します。
_class CustomSettingsProvider : SettingsProvider
_
もう1つの簡単なビット... .settingsファイルの背後にあるコードに移動します(ボタンがありますDesigner)そして、実装を指すようにクラスを装飾します。これは、組み込みの機能をオーバーライドするために実行する必要がありますが、デザイナの動作は変わりません(ここでのフォーマットがおかしいのは残念です)
[System.Configuration.SettingsProvider(typeof(YourCompany.YourProduct.CustomSettingsProvider))]
_public sealed partial class Settings
{
//bla bla bla
}
_
パスの取得:「SettingsKey」というプロパティがあります(例:Properties.Settings.Default.SettingsKey)これを使用してパスを保存しました。以下の物件を作りました。
_/// <summary>
/// The key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
return Properties.Settings.Default.SettingsKey;
}
}
_
設定値の保存。辞書を使うことにしました。これは少しで広く使われるでしょう。ヘルパーとして構造体を作成しました。
_/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
_
魔法。2つのメソッド、GetPropertyValuesとSetPropertyValues。 GetPropertyValuesは、デザイナに表示されるものをパラメータとして受け取ります。値を更新して新しいコレクションを返す機会が必要です。 SetPropertyValuesは、ユーザーが実行時に行われた値への変更を保存するときに呼び出されます。ここで、ディクショナリを更新してファイルを書き出します。
_/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
_
完全なソリューション。これが、コンストラクター、Initialize、およびヘルパーメソッドを含むクラス全体です。スライスとダイスを自由にカットアンドペーストしてください。
_using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
namespace YourCompany.YourProduct
{
class CustomSettingsProvider : SettingsProvider
{
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string USER_SETTINGS = "userSettings";
const string SETTING = "setting";
/// <summary>
/// Loads the file into memory.
/// </summary>
public CustomSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName
{
get
{
return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
}
set
{
//do nothing
}
}
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
if (!File.Exists(UserConfigPath))
{
//if the config file is not where it's supposed to be create a new one.
CreateEmptyConfig();
}
//load the xml
var configXml = XDocument.Load(UserConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach (var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value,
value = element.Value ?? String.Empty
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig()
{
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(USER_SETTINGS);
var group = new XElement(typeof(Properties.Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(UserConfigPath);
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(UserConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach (var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if (setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(UserConfigPath);
}
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
return Properties.Settings.Default.SettingsKey;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
bool _loaded;
}
}
_
パスをカスタマイズするには、独自の SettingsProvider を実装する必要があります。
これを参照してください クライアント設定FAQ
Q:なぜパスがそれほどあいまいなのですか?それを変更/カスタマイズする方法はありますか?
A:パス構築アルゴリズムは、セキュリティ、分離、および堅牢性の観点から、特定の厳格な要件を満たす必要があります。アプリケーションが提供するわかりやすい文字列を使用して、パスをできるだけ簡単に見つけられるようにしましたが、他のアプリとの衝突やなりすましなどの問題が発生しない限り、パスを完全に単純に保つことはできません。
LocalFileSettingsProviderは、設定が保存されているファイルを変更する方法を提供していません。プロバイダー自体が最初に構成ファイルの場所を決定しないことに注意してください-それは構成システムです。何らかの理由で設定を別の場所に保存する必要がある場合は、独自のSettingsProviderを作成することをお勧めします。これは実装が非常に簡単で、これを行う方法を示すサンプルが.NET 2.0SDKにあります。ただし、上記と同じ分離の問題が発生する可能性があることに注意してください。
カスタム設定クラスを作成する代わりに、より簡単で簡単な方法を次に示します。アプリの証拠を変更して、実行可能ファイルの場所に基づくのではなく、「url」部分が定数になるようにします。これを行うには、プログラムの起動時にデフォルトのAppDomainを変更する必要があります。 AppDomainManagerを使用するようにapp.configを設定することと、URLエビデンスをカスタマイズするためにAppDomainManagerとHostSecurityManagerを作成することの2つの部分があります。複雑に聞こえますが、カスタム設定クラスを作成するよりもはるかに簡単です。これは、署名されていないアセンブリにのみ適用されます。署名されたアセンブリがある場合は、URLの代わりにその証拠を使用します。しかし、幸いなことに、パスは常に一定になります(署名キーが変更されない限り)。
以下のコードをコピーして、YourAppName
ビットを置き換えることができます。
DefaultAppDomainManager.cs:
using System;
using System.Security;
using System.Security.Policy;
namespace YourAppName
{
/// <summary>
/// A least-evil (?) way of customizing the default location of the application's user.config files.
/// </summary>
public class CustomEvidenceHostSecurityManager : HostSecurityManager
{
public override HostSecurityManagerOptions Flags
{
get
{
return HostSecurityManagerOptions.HostAssemblyEvidence;
}
}
public override Evidence ProvideAssemblyEvidence(System.Reflection.Assembly loadedAssembly, Evidence inputEvidence)
{
if (!loadedAssembly.Location.EndsWith("YourAppName.exe"))
return base.ProvideAssemblyEvidence(loadedAssembly, inputEvidence);
// override the full Url used in Evidence to just "YourAppName.exe" so it remains the same no matter where the exe is located
var zoneEvidence = inputEvidence.GetHostEvidence<Zone>();
return new Evidence(new EvidenceBase[] { zoneEvidence, new Url("YourAppName.exe") }, null);
}
}
public class DefaultAppDomainManager : AppDomainManager
{
private CustomEvidenceHostSecurityManager hostSecurityManager;
public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
{
base.InitializeNewDomain(appDomainInfo);
hostSecurityManager = new CustomEvidenceHostSecurityManager();
}
public override HostSecurityManager HostSecurityManager
{
get
{
return hostSecurityManager;
}
}
}
}
app.config excerpt:
<runtime>
<appDomainManagerType value="YourAppName.DefaultAppDomainManager" />
<appDomainManagerAssembly value="DefaultAppDomainManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</runtime>
Chucksの優れた答えに基づいて構築する:
Settings.Designer.csに基づいて新しい部分クラスを実装し、変更時にSettings Designerが属性を消去しないようにします作られています:
namespace Worker.Properties
{
[System.Configuration.SettingsProvider(
typeof(SettingsProviders.DllFileSettingsProvider<Settings>))]
internal sealed partial class Settings
{
}
}
外部プロジェクトに入れることができる次のクラスを作成しました。これにより、構成をproject.dll.configにすることができます。継承とオーバーライドConfigPathにより、任意のパスが許可されます。また、読み取りのサポートを追加しましたSystem.Collections.Specialized.StringCollection。 このバージョンはアプリケーション設定用です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
//http://stackoverflow.com/questions/2265271/custom-path-of-the-user-config
namespace SettingsProviders
{
public class DllFileSettingsProvider<Properties_Settings> : SettingsProvider where Properties_Settings : new()
{
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string APPLICATION_SETTINGS = "applicationSettings";
const string SETTING = "setting";
/// <summary>
/// Loads the file into memory.
/// </summary>
public DllFileSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName
{
get
{
return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
}
set
{
//do nothing
}
}
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (setting.PropertyType == typeof(System.Collections.Specialized.StringCollection))
{
var xml = SettingsDictionary[setting.Name].value;
var stringReader = new System.IO.StringReader(xml);
var xmlreader = System.Xml.XmlReader.Create(stringReader);
var ser = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Specialized.StringCollection));
var obj = ser.Deserialize(xmlreader);
var col = (System.Collections.Specialized.StringCollection)obj;
value.PropertyValue = col;
}
else if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
if (!File.Exists(ConfigPath))
{
//if the config file is not where it's supposed to be create a new one.
throw new Exception("Config file not found: " + ConfigPath);
}
//load the xml
var configXml = XDocument.Load(ConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(APPLICATION_SETTINGS).Element(typeof(Properties_Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach (var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value ,
value = element.Value ?? String.Empty
};
if (newSetting.serializeAs == "Xml")
{
var e = (XElement)element.Nodes().First();
newSetting.value = e.LastNode.ToString() ?? String.Empty;
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig()
{
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(APPLICATION_SETTINGS);
var group = new XElement(typeof(Properties_Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(ConfigPath);
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(ConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(APPLICATION_SETTINGS).Element(typeof(Properties_Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach (var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if (setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(ConfigPath);
}
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
public virtual string ConfigPath
{
get
{
var name = new Properties_Settings().GetType().Module.Name + ".config";
if (System.IO.File.Exists(name)==false)
{
System.Diagnostics.Trace.WriteLine("config file NOT found:" + name);
}
System.Diagnostics.Trace.WriteLine("config file found:" + name);
return name;
// return Properties.Settings.Default.SettingsKey;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
internal Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
bool _loaded;
}
}
ビルド後のイベントを追加して、依存プロジェクトにproject.dll.configファイルが含まれていることを確認します。
copy $(SolutionDir)Worker\$(OutDir)Worker.dll.config $(TargetDir) /y