Пользовательский путь к user.config - PullRequest
27 голосов
/ 15 февраля 2010

Я управляю настройками приложения с помощью конструктора настроек в VS2008.

"Точный путь к user.config файлы выглядят примерно так: "

<Profile Directory>\<Company Name>\
 <App Name>_<Evidence Type>_<Evidence Hash>\<Version>\user.config

Есть ли способ настроить этот путь? Я бы предпочел что-то вроде этого:

<Profile Directory>\<Company Name>\
 <App Name>\<Version>\user.config

Я заметил, что пробелы были заменены подчеркиванием в «Название компании» в новой созданной папке («Тестовая компания» -> «Test_Company»). Я действительно хочу отключить это поведение.

Знаете, я мог бы написать новый обработчик настроек на основе XML, но я бы хотел использовать конструктор настроек.

Ответы [ 4 ]

35 голосов
/ 09 июля 2012

Непросто было найти хорошую информацию о реализации провайдера пользовательских настроек, поэтому я включаю полную реализацию ниже (внизу). Сохраняется формат файла user.config, а также функциональность в .settings дизайнер. Я уверен, что есть детали, которые можно немного почистить, так что не беспокойтесь:)

Как и другие, я хотел изменить расположение файла user.config и по-прежнему получать удовольствие от работы с файлами .settings в конструкторе, включая создание значений по умолчанию для новых установок. Важно отметить, что в нашем приложении уже есть другие сохраненные объекты настроек по пути (appData \ local \ etc), по которому мы уже определились, и нам не нужны артефакты в нескольких местах.

Код гораздо длиннее, чем мне хотелось бы, но я не нашел короткого ответа. Хотя кажется довольно болезненным просто иметь возможность контролировать путь, создание поставщика пользовательских настроек само по себе все еще довольно мощно. Можно изменить следующую реализацию, чтобы хранить данные практически в любом месте, включая пользовательский зашифрованный файл, базу данных или взаимодействовать с веб-сервисом.

Из того, что я прочитал, Microsoft не собирается настраивать путь к файлу конфигурации. Я поверю им на слово, когда они скажут, что это будет страшно. Смотрите ( этот ) пост. Увы, если вы хотите сделать это самостоятельно, вы должны реализовать свой собственный SettingsProvider .

Здесь идет ..

Добавьте ссылку в своем проекте в System.Configuration , она понадобится вам для реализации SettingsProvider.

Easy bit ... Создайте класс, который реализует SettingsProvider, используйте ctrl +. , чтобы помочь вам.

class CustomSettingsProvider : SettingsProvider

Еще один легкий бит ... Перейдите к коду позади вашего файла .settings ( в конструкторе есть кнопка ) и украсьте класс, чтобы он указывал на вашу реализацию , Это необходимо сделать, чтобы переопределить встроенную функциональность, но это не меняет работу дизайнера. (Извините, странное форматирование)

[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();
}

Полное решение. Итак, вот весь класс, который включает методы constructor, Initialize и helper. Не стесняйтесь вырезать / вставлять ломтик и кости.

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;
    }
}    
7 голосов
/ 15 февраля 2010

Вам потребуется реализовать свой собственный SettingsProvider , чтобы настроить путь.

См. FAQ по настройкам клиента

В: Почему путь такой неясный? Есть ли способ изменить / настроить его?

A: Алгоритм построения пути должен отвечать определенным строгим требованиям с точки зрения безопасности, изоляции и надежности. Несмотря на то, что мы пытались сделать путь как можно более простым для обнаружения, используя дружественные строки, предоставляемые приложением, невозможно сохранить путь простым, не сталкиваясь с такими проблемами, как коллизии с другими приложениями, подмена и т. Д.

LocalFileSettingsProvider не предоставляет способ изменить файлы, в которых хранятся настройки. Обратите внимание, что сам провайдер в первую очередь не определяет местоположение файла конфигурации - это система конфигурации. Если по какой-то причине вам необходимо сохранить настройки в другом месте, рекомендуется написать свой собственный поставщик настроек. Это довольно просто реализовать, и вы можете найти примеры в .NET 2.0 SDK, которые показывают, как это сделать. Имейте в виду, однако, что вы можете столкнуться с теми же проблемами изоляции, упомянутыми выше.

1 голос
/ 07 августа 2017

Вот более простая, более короткая альтернатива созданию класса пользовательских настроек: измените свидетельство вашего приложения так, чтобы часть «url» была константой, а не базировалась на местоположении исполняемого файла. Для этого вам нужно изменить AppDomain по умолчанию при запуске программы. Он состоит из двух частей: настройка app.config для использования вашего AppDomainManager и создание AppDomainManager и HostSecurityManager для настройки свидетельства URL. Звучит сложно, но это намного проще, чем создавать класс пользовательских настроек. Это относится только к неподписанным сборкам. Если у вас есть подписанная сборка, она будет использовать эти доказательства вместо 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>
0 голосов
/ 16 декабря 2016

Опираясь на Чака, отличный ответ:

Реализация нового частичного класса на основе Settings.Designer.cs , поэтому Дизайнер настроек не стирает атрибут при внесении изменений:

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;


///1993966/polzovatelskii-put-k-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
...