【问题标题】:Custom path of the user.configuser.config 的自定义路径
【发布时间】:2011-01-16 22:34:50
【问题描述】:

我使用 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”-->“Test_Company”)中,“Company Name”中的空格被下划线取代。我真的很想关闭这种行为。

你知道,我可以编写一个新的基于 XML 的设置处理程序,但我想使用设置设计器。

【问题讨论】:

    标签: c# visual-studio settings


    【解决方案1】:

    要找到有关实现自定义设置提供程序的好信息并不容易,因此我在下面包含了一个完整的实现(底部)。保留了 user.config 文件的格式以及其中的功能.settings 设计师。我确定有些部分可以清理一下,所以不要打扰我:)

    和其他人一样,我想更改 user.config 文件的位置,并且仍然可以在设计器中使用 .settings 文件,包括为新安装创建默认值。重要的是,我们的应用还已经在我们已经决定的路径 (appData\local\etc) 中保存了其他设置对象,并且我们不希望在多个位置存在工件。

    代码比我希望的要长得多,但我找不到简短的答案。尽管仅仅能够控制路径似乎有些痛苦,但创建自定义设置提供程序本身仍然非常强大。可以更改以下实现以将数据存储在几乎任何地方,包括自定义加密文件、数据库或与 Web 服务交互。

    据我所知,微软不打算让配置文件的路径可配置。当他们说允许这样做会很可怕时,我会相信他们的话。请参阅 (this) 帖子。唉,如果你想自己做,你必须实现自己的SettingsProvider

    来了..

    在你的项目中添加一个引用System.Configuration,你需要它来实现SettingsProvider。

    简单...创建一个实现 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 个方法,GetPropertyValuesSetPropertyValues。 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;
        }
    }    
    

    【讨论】:

    • 感谢您,这对入门很有帮助。我发现的一件事是 Type.GetType() 和 Convert.ChangeType() 函数不适用于 System.Drawing.Point 和 System.Drawing.Size 以及其他一些函数。作为一种解决方法,我只使用成对的整数,但它们仍然是设置设计器中的选项。
    • GetType 和 ChangeType 也有问题。我正在尝试存储/加载ListSortDirection,但 GetType 返回 null 并抛出 ChangeType。该字符串被序列化为“升序”,但无法反序列化。作为一种解决方法,我尝试使用 this GetType method,它确实给了我正确的 ListSortDirection 类型,但 ChangeType 仍然抛出,说 Invalid cast from 'System.String' to 'System.ComponentModel.ListSortDirection'
    • 如果我使用自定义类并检查文件会出现空错误,并且只显示我的类路径,例如:属性标签中的name_space.ClassName
    • Enums 和类类型似乎也失败了,否则是一个好的开始
    • GUID 也存在问题,但这个答案提供了解决方案或至少提供了一种方法:stackoverflow.com/questions/393731/…
    【解决方案2】:

    您必须实现自己的SettingsProvider 才能自定义路径。

    看到这个Client Settings FAQ

    问:为什么路径如此模糊?有什么方法可以更改/自定义它吗?

    A:路径构建算法在安全性、隔离性和健壮性方面必须满足一定的严格要求。虽然我们试图通过使用友好的、应用程序提供的字符串来使路径尽可能容易被发现,但不可能在不遇到与其他应用程序冲突、欺骗等问题的情况下保持路径完全简单。

    LocalFileSettingsProvider 不提供更改存储设置的文件的方法。请注意,提供者本身并不会首先确定配置文件的位置——它是配置系统。如果出于某种原因需要将设置存储在不同的位置,推荐的方法是编写自己的 SettingsProvider。这实现起来相当简单,您可以在 .NET 2.0 SDK 中找到演示如何执行此操作的示例。但请记住,您可能会遇到上述相同的隔离问题。

    【讨论】:

      【解决方案3】:

      这是创建自定义设置类的更简单、更简洁的替代方法:更改应用程序的证据,使“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>
      

      【讨论】:

      • 这很有趣。知道改变证据的生成方式是否有潜在的副作用吗?
      【解决方案4】:

      以 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
      

      【讨论】:

        猜你喜欢
        • 2013-12-05
        • 2020-08-14
        • 1970-01-01
        • 2022-01-26
        • 2017-06-19
        • 1970-01-01
        • 2012-02-21
        • 2021-04-07
        • 2013-04-24
        相关资源
        最近更新 更多