Arquivo de configuração DLL do C #

191

Estou tentando adicionar um arquivo app.config à minha DLL, mas todas as tentativas falharam.

De acordo com o MusicGenesis em ' Colocando informações de configuração em uma DLL ', isso não deve ser um problema. Então, obviamente, estou fazendo algo errado ...

O código a seguir deve retornar meu ConnectionString da minha DLL:

return ConfigurationManager.AppSettings["ConnectionString"];

No entanto, quando copio o arquivo app.config para o aplicativo do console, ele funciona bem.

Alguma ideia?

Megabyte
fonte
De acordo com o post referido: se o nome da dll for MyDll.dll, o arquivo de configuração deverá ser MyDLL.dll.config. Então, se você ler as definições de configuração de dentro da DLL, ele deverá se referir à sua própria configuração, certo?
MegaByte
11
Não importa o que o código pergunta - ele está procurando o arquivo especificado para a configuração AppDomain: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
Marc Gravell
Uma observação: a pergunta "colocando informações de configuração em uma DLL" é sobre a separação do código de configuração do aplicativo em uma biblioteca para mantê-lo separado do código principal do aplicativo. Isso é muito diferente de um arquivo de configuração separado e especial para uma DLL por si só.
22630 Chris Ammerman
veja este post [digite a descrição do link aqui] [1], foi a solução para mim [1]: stackoverflow.com/questions/2389290/…
dhailis
veja este post [Como carregar um arquivo de Configurações do aplicativo separados de forma dinâmica e mesclar com as configurações atuais?] [1] pode ser helpfu [1]: stackoverflow.com/questions/2389290/...
dhailis

Respostas:

277

Não é trivial criar um arquivo de configuração do .NET para uma DLL e por um bom motivo. O mecanismo de configuração do .NET possui muitos recursos incorporados para facilitar a atualização / atualização fácil do aplicativo e para proteger os aplicativos instalados contra os arquivos de configuração.

Há uma grande diferença entre como uma DLL é usada e como um aplicativo é usado. É improvável que você tenha várias cópias de um aplicativo instaladas na mesma máquina para o mesmo usuário. Mas você pode muito bem ter 100 aplicativos ou bibliotecas diferentes, todos usando alguma DLL do .NET.

Embora raramente seja necessário rastrear configurações separadamente para cópias diferentes de um aplicativo em um perfil de usuário, é muito improvável que você deseje que todos os diferentes usos de uma DLL compartilhem a configuração entre si. Por esse motivo, quando você recupera um objeto de Configuração usando o método "normal", o objeto retornado é vinculado à configuração do Domínio do Aplicativo em que você está executando, em vez do assembly específico.

O domínio do aplicativo está vinculado ao assembly raiz que carregou o assembly no qual seu código está realmente. Na maioria dos casos, esse é o assembly do seu principal .exe, que foi o que carregou o .dll. É possível ativar outros domínios de aplicativo em um aplicativo, mas você deve fornecer explicitamente informações sobre qual é o conjunto raiz desse domínio de aplicativo.

Por tudo isso, o procedimento para criar um arquivo de configuração específico da biblioteca não é tão conveniente. É o mesmo processo que você usaria para criar um arquivo de configuração portátil arbitrário não vinculado a nenhum assembly específico, mas para o qual você deseja usar o esquema XML do .NET, a seção de configuração e os mecanismos do elemento de configuração, etc. Isso implica a criação de um ExeConfigurationFileMapobjeto , carregando os dados para identificar onde o arquivo de configuração será armazenado e depois chamando ConfigurationManager. OpenMappedExeConfigurationpara abri-lo em uma nova Configurationinstância. Isso impedirá a proteção da versão oferecida pelo mecanismo de geração automática de caminho.

Estatisticamente falando, você provavelmente está usando esta biblioteca em uma configuração interna e é improvável que você tenha vários aplicativos usando-a em qualquer máquina / usuário. Mas se não, há algo que você deve ter em mente. Se você usar um único arquivo de configuração global para sua DLL, independentemente do aplicativo que está fazendo referência a ele, precisará se preocupar com conflitos de acesso. Se dois aplicativos que fazem referência à sua biblioteca estiverem em execução ao mesmo tempo, cada um com seu próprio Configurationobjeto aberto, quando um salvar alterações, causará uma exceção na próxima vez que você tentar recuperar ou salvar dados no outro aplicativo.

A maneira mais simples e segura de contornar isso é exigir que o assembly que está carregando sua DLL também forneça algumas informações sobre si mesmo ou para detectá-lo examinando o domínio de aplicativo do assembly de referência. Use isso para criar algum tipo de estrutura de pastas para manter arquivos de configuração do usuário separados para cada aplicativo que faz referência à sua DLL.

Se você tiver certeza de que deseja ter configurações globais para sua DLL, independentemente de onde ela é referenciada, será necessário determinar sua localização, em vez de o .NET descobrir automaticamente uma apropriada. Você também precisará ser agressivo ao gerenciar o acesso ao arquivo. Você precisará armazenar em cache o máximo possível, mantendo a Configurationinstância apenas o tempo necessário para carregar ou salvar, abrindo imediatamente antes e descartando imediatamente depois. E, finalmente, você precisará de um mecanismo de bloqueio para proteger o arquivo enquanto ele está sendo editado por um dos aplicativos que usam a biblioteca.

Chris Ammerman
fonte
eu acho que o mecanismo de sincronização deve ser um "evento chamado" etc, porque é todo-processos
Jacob
8
: / Meh. O nosso é um aplicativo corporativo monstruoso com o principal .exe escrito por caras em um fuso horário diferente e os módulos representados por várias DLLs e vinculados dinamicamente através de uma estrutura de plug-in personalizada. Tudo isso "você precisará para garantir que vários aplicativos possam usar sua DLL simultaneamente"; pomposidade está exatamente errada.
precisa saber é o seguinte
Além disso, em grande parte da minha carreira, eu vi esses mecanismos genéricos de objetos compartilhados adoráveis ​​completamente ignorados, com equipes criando DLLs (ou JARs) que só podem ser usadas em um contexto (e devem estar presentes ou o aplicativo falha ) Eles podem muito bem ser estaticamente vinculados, mas isso é ultrapassado.
precisa saber é o seguinte
1
"Estatisticamente falando, você provavelmente está usando esta biblioteca em um ambiente interno, e é improvável que você tenha vários aplicativos usando-a em qualquer máquina / usuário". A diferença entre teoria e prática me deixa de mau humor algumas vezes.
JohnL4
1
@Panzercrisis, o recurso Settings.settings do Visual Studio cria automaticamente caminhos específicos da versão para todas as configurações do usuário. Veja: stackoverflow.com/questions/35778528/…
Deantwo 23/01
101

se você quiser ler as configurações do arquivo de configuração da DLL, mas não dos aplicativos raiz web.config ou app.config, use o código abaixo para ler a configuração na dll.

var appConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
string dllConfigData = appConfig.AppSettings.Settings["dllConfigData"].Value;
Morbia
fonte
No C ++ gerenciado para o VS 2008 System :: Configuration :: Configuration ^ appConfig = ConfigurationManager :: OpenExeConfiguration (Assembly :: GetExecutingAssembly () -> Location); String ^ name = appConfig-> AppSettings-> Settings ["name"] -> Value;
Hans
Obrigado, isso realmente resolveu o meu problema. Estou lidando com esse problema há cerca de dois dias e não o fiz funcionar até agora. Como depuração de um teste, o ConfigurationManager estava lendo no machine.config - eu acho -, já que as cadeias de conexão extraídas eram sobre SQLExpress - cadeia de conexão que eu não tinha listado -.
precisa saber é
19

Eu tive o mesmo problema e procurei na web por várias horas, mas não consegui encontrar nenhuma solução, então criei a minha. Eu me perguntava por que o sistema de configuração .net é tão inflexível.

Background: Desejo que meu DAL.dll tenha seu próprio arquivo de configuração para o banco de dados e as configurações do DAL. Também preciso do app.config da Enterprise Library e de suas próprias configurações. Então, eu preciso do app.config e dll.config.

O que eu não queria fazer era passar todas as propriedades / configurações do aplicativo para a minha camada DAL!

dobrar o "AppDomain.CurrentDomain.SetupInformation.ConfigurationFile" não é possível porque eu preciso dele para o comportamento normal do app.config.

Meus requisitos / pontos de vista foram:

  • Nenhuma cópia manual de qualquer coisa, de ClassLibrary1.dll.config a WindowsFormsApplication1.exe.config, porque isso não pode ser reproduzido por outros desenvolvedores.
  • reter o uso da digitação forte "Properties.Settings.Default.NameOfValue" (comportamento das configurações) porque acho que esse é um recurso importante e não queria perdê-lo
  • Descobri a falta de ApplicationSettingsBase para injetar seu próprio arquivo de configuração / gerenciamento ou gerenciamento personalizado (todos os campos necessários são particulares nessas classes)
  • o uso do redirecionamento de arquivo "configSource" não é possível porque teríamos que copiar / reescrever o ClassLibrary1.dll.config e fornecer vários arquivos XML para várias seções (eu também não gostei disso)
  • Eu não gostava de escrever meu próprio SettingsProvider para esta tarefa simples, como sugere o MSDN, porque pensei que isso seria demais
  • Eu só preciso das seções applicationSettings e connectionStrings do arquivo de configuração

Eu vim modificando o arquivo Settings.cs e implementei um método que abre o ClassLibrary1.dll.config e lê as informações da seção em um campo particular. Depois disso, substituí "this [string propertyName]" para que o Settings.Desginer.cs gerado chame minha nova propriedade em vez da classe base. Lá, a configuração é lida na lista.

Finalmente, há o seguinte código:

internal sealed partial class Settings
{
    private List<ConfigurationElement> list;

    /// <summary>
    /// Initializes a new instance of the <see cref="Settings"/> class.
    /// </summary>
    public Settings()
    {
        this.OpenAndStoreConfiguration();
    }

    /// <summary>
    /// Opens the dll.config file and reads its sections into a private List of ConfigurationElement.
    /// </summary>
    private void OpenAndStoreConfiguration()
    {
        string codebase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
        Uri p = new Uri(codebase);
        string localPath = p.LocalPath;
        string executingFilename = System.IO.Path.GetFileNameWithoutExtension(localPath);
        string sectionGroupName = "applicationSettings";
        string sectionName = executingFilename + ".Properties.Settings";
        string configName = localPath + ".config";
        ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
        fileMap.ExeConfigFilename = configName;
        Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

        // read section of properties
        var sectionGroup = config.GetSectionGroup(sectionGroupName);
        var settingsSection = (ClientSettingsSection)sectionGroup.Sections[sectionName];
        list = settingsSection.Settings.OfType<ConfigurationElement>().ToList();

        // read section of Connectionstrings
        var sections = config.Sections.OfType<ConfigurationSection>();
        var connSection = (from section in sections
                           where section.GetType() == typeof(ConnectionStringsSection)
                           select section).FirstOrDefault() as ConnectionStringsSection;
        if (connSection != null)
        {
            list.AddRange(connSection.ConnectionStrings.Cast<ConfigurationElement>());
        }
    }

    /// <summary>
    /// Gets or sets the <see cref="System.Object"/> with the specified property name.
    /// </summary>
    /// <value></value>
    public override object this[string propertyName]
    {
        get
        {
            var result = (from item in list
                         where Convert.ToString(item.ElementInformation.Properties["name"].Value) == propertyName
                         select item).FirstOrDefault();
            if (result != null)
            {
                if (result.ElementInformation.Type == typeof(ConnectionStringSettings))
                {
                    return result.ElementInformation.Properties["connectionString"].Value;
                }
                else if (result.ElementInformation.Type == typeof(SettingElement))
                {
                    return result.ElementInformation.Properties["value"].Value;
                }
            }
            return null;
        }
        // ignore
        set
        {
            base[propertyName] = value;
        }
    }

Você apenas precisará copiar o ClassLibrary1.dll.config do diretório de saída ClassLibrary1 para o diretório de saída do aplicativo. Talvez alguém ache útil.

Sven
fonte
14

Ao usar o ConfigurationManager, tenho certeza de que está carregando o AppDomainarquivo de processo / configuração (app.config / web.config). Se você deseja carregar um arquivo de configuração específico, precisará solicitá-lo especificamente por nome ...

Você poderia tentar:

var config = ConfigurationManager.OpenExeConfiguration("foo.dll");
config.ConnectionStrings. [etc]
Marc Gravell
fonte
De acordo com o post referido: se o nome da dll for MyDll.dll, o arquivo de configuração deverá ser MyDLL.dll.config. Então, se você ler as definições de configuração de dentro da DLL, ele deverá se referir à sua própria configuração, certo?
MegaByte
1
Não, eu não penso assim. "from with the dll" não faz diferença; por padrão, ele está olhando para o arquivo de configuração definido para o AppDomain: my.exe.config
Marc Gravell
1
Em particular, a configuração AppDomain.CurrentDomain.SetupInformation.ConfigurationFile.
Marc Gravell
nota: experimentei o OpenExeConfiguration e também não tenho certeza de que funcione. Talvez apenas mescle a configuração com o app.config?
Marc Gravell
Isso pode ser feito ... mas não com o mesmo tipo de suporte e segurança que o arquivo app.config para um EXE. Veja minha resposta.
22630 Chris Ammerman
13

ConfigurationManager.AppSettings retorna as configurações definidas para o aplicativo, não para a DLL específica, você pode acessá-los, mas são as configurações do aplicativo que serão retornadas.

Se você estiver usando a dll de outro aplicativo, o ConnectionString estará nas app.settings do aplicativo.

Jorge Córdoba
fonte
6

Eu sei que isso é tarde para a festa, no entanto, pensei em compartilhar a solução que uso para DLLs.

Eu sou mais da escola de pensamento do KISS, então, quando eu tenho uma DLL do .NET que deseja armazenar pontos de dados externos que controlam como ela funciona ou para onde vai, etc. Eu simplesmente crio uma classe "config" que possui apenas propriedades públicas que armazenam todos os pontos de dados necessários e que eu gostaria de poder ter um controle externo à DLL para impedir a recompilação para fazer as alterações. Então eu uso o XML Serializing do .Net para salvar e carregar a representação de objeto da classe em um arquivo.

Existem várias maneiras de lidar com a leitura e o acesso a ela, de um Singleton, uma classe de utilitário estático, a métodos de extensão etc. Isso depende de como sua DLL está estruturada e de qual método se ajustará melhor à sua DLL.

Rodney S. Foley
fonte
Também uso essa abordagem e estou feliz com a maneira como ela foi desenvolvida até agora.
22411 Dave
4

você está correto, você pode ler o arquivo de configuração de uma dll. Eu lutei com isso por um dia até descobrir que o meu arquivo de configuração era o problema. Veja meu código abaixo. foi capaz de correr.

        ExeConfigurationFileMap map = new ExeConfigurationFileMap();
        map.ExeConfigFilename = Assembly.GetExecutingAssembly().Location + ".config";
        Configuration libConfig = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        AppSettingsSection section = (libConfig.GetSection("appSettings") as AppSettingsSection);
        Console.WriteLine(section.Settings["dnd_shortcodes"].Value);

meu Plugin1.dll.configolhou como abaixo;

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <appSettings>
  <add key="cmd_location" value="http://..."/>
  <add key="dnd_shortcodes" value="142,145,146,157,165,167,168,171,173,176,178,404,40"/>
 </appSettings>
</configuration>

Descobri que meu arquivo de configuração não possuía a <appSettings>tag. Portanto, olhe em volta, seu problema pode ter sido diferente, mas não tão distante do meu.

mugume david
fonte
3

Como o assembly reside em um cache temporário, você deve combinar o caminho para obter a configuração da dll:

var appConfig = ConfigurationManager.OpenExeConfiguration(
    Path.Combine(Environment.CurrentDirectory, Assembly.GetExecutingAssembly().ManifestModule.Name));
Lin Song Yang
fonte
em vez de "Path.Combine (Environment.CurrentDirectory, Assembly.GetExecutingAssembly (). ManifestModule.Name)", você pode usar "Assembly.GetExecutingAssembly (). Location"
Cadburry
3

Se você estiver usando bibliotecas que pesquisam uma grande quantidade de configuração nos bastidores, como o WCF, considere fazer o seguinte:

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "MyWcfClientWrapper.dll.config");

Ou no PowerShell:

[AppDomain]::CurrentDomain.SetData("APP_CONFIG_FILE", "MyWcfClientWrapper.dll.config")

Na IMO, essa técnica é um cheiro de código e é realmente adequada apenas para uso em scripts ad hoc. Se você deseja fazer isso no código de produção, talvez seja hora de fazer uma revisão da arquitetura.

NÃO é recomendado o seguinte:
Como curiosidade técnica, veja uma variação do tema. Você pode criar um construtor estático dentro de uma das classes armazenadas na DLL e fazer essa chamada a partir daí. Eu não recomendaria fazer isso, exceto como último recurso.

Paul Williams
fonte
3

A solução completa nem sempre é encontrada em um único local ...

1) Crie um arquivo de configuração de aplicativo e denomine "yourDllName.dll.config"
2) Clique com o botão direito do mouse no arquivo de configuração criado acima no VS Solution Explorer, clique em Propriedades
--- set "Build Action" = Content
--- set "Copy To Output Directory" = Sempre
3) Adicione uma seção appSettings ao arquivo de configuração (yourDllName.dll.config) com yourKeyName e yourKeyValue

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="yourKeyName" value="yourKeyValue"/>
  </appSettings>
</configuration>

4) Adicione System.Configuration às suas referências de DLL / classe / projeto.
5) Adicione as instruções using ao seu código onde você pretende acessar a configuração

using System.Configuration;
using System.Reflection;

6) Para acessar o valor

string keyValue = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings.Settings["yourKeyName"].Value;

7) alegra-se, funciona

IMHO, isso só deve ser usado no desenvolvimento de uma nova dll / biblioteca.

#if (DEBUG && !FINALTESTING)
   string keyValue = ConfigurationManager.OpenExeConfiguration...(see 6 above)
#else
   string keyValue = ConfigurationManager.AppSettings["yourKeyName"];
#endif

O arquivo de configuração acaba sendo uma ótima referência, pois quando você adiciona o appSettings da DLL ao seu aplicativo real.

David C Fuchs
fonte
3

Parece que esses arquivos de configuração são realmente confusos para esclarecer à medida que seu comportamento muda do ambiente de desenvolvimento para a implantação. Aparentemente, uma DLL pode ter seu próprio arquivo de configuração, mas depois que você copia e cola a dll (junto com o arquivo de configuração) em outro local, tudo parou de funcionar. A única solução é mesclar manualmente os arquivos app.config em um único arquivo, que será usado apenas pelo exec. Por exemplo, myapp.exe terá um arquivo myapp.exe.config que contém todas as configurações de todas as DLLs usadas pelo myapp.exe. Estou usando o VS 2008.

kenny
fonte
2

Encontrei o que parece ser uma boa solução para esse problema. Estou usando o VS 2008 C #. Minha solução envolve o uso de namespaces distintos entre vários arquivos de configuração. Publiquei a solução no meu blog: http://tommiecarter.blogspot.com/2011/02/how-to-access-multiple-config-files-in.html .

Por exemplo:

Este namespace lê / grava configurações de DLL:

var x = company.dlllibrary.Properties.Settings.Default.SettingName;
company.dlllibrary.Properties.Settings.Default.SettingName = value;

Este espaço para nome lê / grava as configurações de exe:

company.exeservice.Properties.Settings.Default.SettingName = value;
var x = company.exeservice.Properties.Settings.Default.SettingName;

Existem algumas ressalvas mencionadas no artigo. HTH

Tommie C.
fonte
1

Como Marc diz, isso não é possível (embora o Visual Studio permita adicionar um arquivo de configuração de aplicativo em um projeto de biblioteca de classes).

Você pode querer verificar a classe AssemblySettings, que parece tornar possível os arquivos de configuração da montagem.

Gerrie Schenck
fonte
0

Para uma dll, ela não deve depender da configuração, pois a configuração pertence ao aplicativo e não à dll.

Isso é explicado aqui

Saravanan
fonte
0

você pode usar este código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace GClass1
{
[Guid("D6F88E95-8A27-4ae6-B6DE-0542A0FC7039")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface _GesGasConnect
{
    [DispId(1)]
    int SetClass1Ver(string version);


}

[Guid("13FE32AD-4BF8-495f-AB4D-6C61BD463EA4")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("InterfacesSMS.Setting")]
public class Class1 : _Class1
{
    public Class1() { }


    public int SetClass1(string version)
    {
        return (DateTime.Today.Day);
    }
}
}
David Lopes
fonte