2010-02-15 19 views
24

Administro la configuración de la aplicación utilizando el programa de configuración en VS2008.Ruta de acceso personalizada del archivo user.config

"La ruta exacta de los archivos user.config se ve algo como esto:"

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

está ahí para una forma de personalizar este camino? Yo preferiría algo como esto:

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

me di cuenta de que los espacios en blanco fueron sustituidos por guiones en el campo "Nombre de empresa" en la nueva carpeta creada ("Sociedad de prueba" -> "Test_Company"). Realmente deseo desactivar este comportamiento.

Ya sabes, podría escribir un nuevo manejador de configuración basado en XML, pero me gustaría utilizar el configurador-diseñador.

Respuesta

7

Debería implementar su propio SettingsProvider para personalizar la ruta.

ver este Client Settings FAQ

Q: ¿Por qué es el camino tan oscuro? ¿Hay alguna manera de cambiarlo/personalizarlo?

A: El algoritmo de construcción de rutas debe cumplir ciertos requisitos rigurosos en términos de seguridad, aislamiento y robustez. Si bien tratamos de hacer que la ruta sea lo más fácil posible de descubrir mediante el uso de cadenas amigables proporcionadas por la aplicación, no es posible mantener la ruta totalmente simple sin toparse con problemas como colisiones con otras aplicaciones, spoofing, etc.

LocalFileSettingsProvider no proporciona una forma de cambiar los archivos en los que se almacenan las configuraciones. Tenga en cuenta que, en primer lugar, el proveedor no determina las ubicaciones de los archivos de configuración: es el sistema de configuración. Si necesita almacenar la configuración en una ubicación diferente por algún motivo, la forma recomendada es escribir su propio Proveedor de Configuraciones. Esto es bastante simple de implementar y puede encontrar muestras en el .NET 2.0 SDK que muestran cómo hacerlo. Sin embargo, tenga en cuenta que puede encontrarse con los mismos problemas de aislamiento mencionados anteriormente.

34

No ha sido fácil encontrar una buena información sobre la implementación de un proveedor de configuraciones personalizadas, por lo que incluyo una implementación completa debajo (abajo). Se conserva el formato del archivo user.config, así como funcionalidad dentro del diseñador .settings. Estoy seguro de que hay partes que se pueden limpiar un poco, así que no me moleste :)

Al igual que en otros, quería cambiar la ubicación del archivo user.config y seguir obteniendo la diversión y la imaginación de trabajar con los archivos .settings en el diseñador, incluida la creación de valores predeterminados para nuevas instalaciones. Es importante destacar que nuestra aplicación también ya tiene otros objetos de configuración guardados en una ruta (appData \ local \ etc) en la que ya hemos decidido, y no queríamos artefactos en múltiples ubicaciones.

El código es mucho más largo de lo que me gustaría, pero no hay una respuesta CORTA que pueda encontrar. Aunque parece un tanto doloroso solo poder controlar el camino, la creación de un proveedor de configuraciones personalizadas en sí misma todavía es bastante poderosa. Se podría modificar la implementación siguiente para almacenar datos en cualquier lugar, incluido un archivo encriptado personalizado, una base de datos o interactuar con un serivicio web.

Por lo que he leído, Microsoft no tiene la intención de configurar la ruta del archivo de configuración configurable. Tomaré su palabra cuando digan que permitir eso sería aterrador. Ver (this) publicación. Por desgracia, si quieres hacerlo tú mismo, debes implementar tu propio SettingsProvider.

Aquí va ..

agregar una referencia en su proyecto para System.Configuration, lo necesitará para implementar el SettingsProvider.

Easy bit ... Cree una clase que implemente SettingsProvider, use ctrl+. para ayudarlo.

class CustomSettingsProvider : SettingsProvider 

Otra parte fácil ... Ir al código detrás de su archivo .settings (hay un botón en el diseñador) y decorar la clase de señalar a su aplicación. Esto se debe hacer para anular el construido en la funcionalidad, pero no cambia la forma en que funciona el diseñador (lo siento el formato aquí es raro)

[System.Configuration.SettingsProvider(typeof(YourCompany.YourProduct.CustomSettingsProvider))]

public sealed partial class Settings 
{ 
    //bla bla bla 
} 

Conseguir el camino:. Hay una propiedad llamada "SettingsKey" (por ejemplo, Properties.Settings.Default.SettingsKey) Utilicé esto para almacenar la ruta. Hice la siguiente propiedad.

/// <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; 
    } 

} 

Almacenamiento de los valores de los ajustes. Elegí usar un diccionario. Esto se usará ampliamente en un momento. Creé una estructura como ayudante.

/// <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; 
} 

The magic. Debe anular 2 métodos, GetPropertyValues ​​ y SetPropertyValues ​​. GetPropertyValues ​​recibe como parámetro lo que ve en el diseñador, tiene que oportunidad de actualizar los valores y devolver una nueva colección. Se llama a SetPropertyValues ​​cuando el usuario guarda cualquier cambio en los valores realizados en tiempo de ejecución, aquí es donde yo actualizo el diccionario y escribo el archivo.

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

solución completa. Así que aquí está toda la clase que incluye los métodos constructor, Initialize y helper. Siéntase libre de cortar/pegar rebanadas y dados.

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; 
    } 
}  
+0

Gracias por esto, que fue una gran ayuda para empezar. Sin embargo, una cosa que encontré es que las funciones Type.GetType() y Convert.ChangeType() no funcionan con System.Drawing.Point y System.Drawing.Size y probablemente con algunas otras. Como solución alternativa, solo estoy usando pares de entradas, pero siguen siendo opciones en el diseñador de configuraciones. – Troyen

+0

También tenía un problema con GetType y ChangeType. Estoy intentando almacenar/cargar un [ListSortDirection] (https://msdn.microsoft.com/en-us/library/system.componentmodel.listsortdirection (v = vs.110) .aspx), pero GetType devuelve null y ChangeType arroja. La cadena se serializó como "Ascendente", pero no se pudo deserializar. Como solución temporal, traté de usar [este método GetType] (http://stackoverflow.com/a/11811046/466011), que me da el tipo correcto de ListSortDirection, pero ChangeType aún arroja, diciendo 'Cast no válido de 'System.String 'a' System.ComponentModel.ListSortDirection''. – epalm

+0

Obteniendo errores nulos si uso una clase personalizada y comprobando que el archivo muestra solo mi ruta de clase como: 'nombre_espacio.nombreClase' en la etiqueta de propiedad. – Matheus

0

Sobre la base de Mandriles excelente respuesta:

implementar una nueva clase parcial basado en Settings.Designer.cs, por lo que los ajustes Diseñador no elimina el atributo cuando se realizan cambios:

namespace Worker.Properties 
{ 
    [System.Configuration.SettingsProvider(
     typeof(SettingsProviders.DllFileSettingsProvider<Settings>))] 
    internal sealed partial class Settings 
    { 

    } 
} 

creé la siguiente clase que se puede poner en un proyecto externo. Permite que la configuración sea project.dll.config. Al heredar y anular ConfigPath permite cualquier ruta que desee. También agregué soporte para leer System.Collections.Specialized.StringCollection. Esta versión es para la configuración de la aplicación.

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; 
    } 
} 

hacen que los proyectos dependientes de seguro incluyen la project.dll.config archivo por añadir un evento posterior a la generación:

copy $(SolutionDir)Worker\$(OutDir)Worker.dll.config $(TargetDir) /y 
0

Aquí es una alternativa más breve más fácil de crear una clase de configuración personalizada : cambie la evidencia de su aplicación para que la parte "url" sea una constante en lugar de basarse en la ubicación del archivo ejecutable. Para hacer esto, necesita modificar el AppDomain predeterminado cuando se inicia el programa. Hay dos partes: configuración de app.config para usar su AppDomainManager, y creación de un AppDomainManager y HostSecurityManager para personalizar la evidencia de Url. Suena complicado, pero es mucho más simple que crear una clase de configuración personalizada. Esto solo se aplica a conjuntos sin signo. Si tiene un ensamblado firmado, usará esa evidencia en lugar de la Url. Pero la buena noticia es que tu camino siempre será constante de todos modos (siempre que la clave de firma no cambie).

Puede copiar el siguiente código y simplemente reemplazar los bits 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> 
Cuestiones relacionadas