2009-11-05 13 views
20

Mam bardzo trudny czas, próbując uzyskać dostęp do sekcji konfiguracji niestandardowej w moim pliku konfiguracyjnym.Niestandardowa sekcja konfiguracji: Nie można załadować pliku lub zespołu

Plik konfiguracyjny jest czytany z pliku .dll załadowanego jako wtyczka. Utworzono konfigurację i niezbędny kod przy użyciu dodatku VS Configuration Section Designer.

Przestrzeń nazw to "ImportConfiguration". Klasa ConfigurationSection to "ImportWorkflows". Zestawem jest ImportEPDMAddin.

xml:

<configSections> 
    <section name="importWorkflows" type="ImportConfiguration.ImportWorkflows, ImportEPDMAddin"/> 
    </configSections> 

Ilekroć próbuję czytać w config, pojawia się błąd:

Wystąpił błąd podczas tworzenia programu obsługi sekcji konfiguracji importWorkflows: Nie można załadować pliku lub zestawu ' ImportEPDMAddin.dll "lub jedną z jego zależności. System nie może odnaleźć określonego pliku.

Dll nie będzie znajdować się w tym samym katalogu, w którym znajduje się plik wykonywalny, ponieważ oprogramowanie ładujące wtyczkę umieszcza dll i jego zależności w swoim własnym katalogu. (Nie mogę kontrolować tego.)

edytowany kod singleton przykład na następujące kwestie:

string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase; 
path = path.Replace("file:///", ""); 
System.Configuration.Configuration configuration = System.Configuration.ConfigurationManager.OpenExeConfiguration(path); 
return configuration.GetSection(ImportWorkflowsSectionName) as ImportConfiguration.ImportWorkflows; 

Próbowałem również za pomocą prostego NameValueFileSectionHandler jak dobrze, ale pojawia się wyjątek mówiący, że nie może załadować pliku lub zespołu "System".

Przeczytałem wiele postów na blogu i artykułów i brzmi to, jak można odczytać plik konfiguracyjny dla dll, ale po prostu nie mogę go uruchomić. Jakieś pomysły? Dzięki.

+0

Czy skopiować 'ImportEPDMAddin.dll.config' na tym samym miejscu też? – ephemient

+0

Konfiguracja jest tam na pewno, ponieważ próbowałem używać DictionarySectionHandler z innej klasy i to działa. – ehcanadian

Odpowiedz

34

Niestety, trzeba będzie albo mieć ImportEPDMAddin montaż przebywających w tym samym folderze co plik wykonywalny, zamieszkały w folderze .NET Framework związaną z .NET Framework, którego używasz (np C: \ Windows \ Microsoft.NET \ Framework \ v2.0.50727) lub zarejestrowane w pamięci podręcznej Global Assembly Cache.

Jedynym rozwiązaniem jest, jeśli znasz drogę do zespołu, który zawiera określającą klasę uchwyt konfiguracyjny, w tym przypadku można załadować go bez odniesienia z czymś takim:

//Class global 
private Assembly configurationDefiningAssembly; 

protected TConfig GetCustomConfig<TConfig>(string configDefiningAssemblyPath, 
    string configFilePath, string sectionName) where TConfig : ConfigurationSection 
{ 
    AppDomain.CurrentDomain.AssemblyResolve += new 
     ResolveEventHandler(ConfigResolveEventHandler); 
    configurationDefiningAssembly = Assembly.LoadFrom(configDefiningAssemblyPath); 
    var exeFileMap = new ExeConfigurationFileMap(); 
    exeFileMap.ExeConfigFilename = configFilePath; 
    var customConfig = ConfigurationManager.OpenMappedExeConfiguration(exeFileMap, 
     ConfigurationUserLevel.None); 
    var returnConfig = customConfig.GetSection(sectionName) as TConfig; 
    AppDomain.CurrentDomain.AssemblyResolve -= ConfigResolveEventHandler; 
    return returnConfig; 
} 

protected Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args) 
{ 
    return configurationDefiningAssembly; 
} 

Upewnij się obsługiwać Zdarzenie AssemblyResolve, ponieważ spowoduje to zgłoszenie wyjątku bez niego.

+0

Dziękuję bardzo, bardzo. To działało w 100% !!. – ehcanadian

+0

To działało jak zaklęcie dostępu i rzutowania typu niestandardowej sekcji w pliku T4. Dzięki! – Matt

+0

@AJ. Przepraszam, czy możesz mi wytłumaczyć, co to jest 'configDefiningAssemblyPath' .. czy jest to plik .exe? – Ciccio

0

Czy masz pewność, że plik DLL jest ładowany jako pierwszy? Być może z Assembly.LoadFile("PATH")?

Jeśli nie można uzyskać klas w System.Configuration do prawidłowego działania, zawsze można skorzystać z XmlDocument, aby ręcznie przeanalizować plik konfiguracyjny. Użyj XPaths, aby ułatwić pobieranie danych. Na przykład (zakładając, że zmienna ścieżki powyżej):

var document = new XmlDocument(); 
document.Load(path); 
var node = document.SelectSingleNode("configuration/importWorkflows/add[@name='KEY']"); 
// Do whatever with node 
+0

Ta metoda działa, ale miałem nadzieję trzymać się klas konfiguracji. Jeśli wszystko inne zawiedzie, będę musiał pójść z twoją sugestią. – ehcanadian

1

Czy można sprawdzić, czy ścieżki próbkowania są poprawnie skonfigurowane w pliku konfiguracyjnym aplikacji hosta? Możliwe, że potrzebne referencje nie są ładowane do bieżącej domeny aplikacji.

Assembly Binding ->Probing

5

w głównym pliku aplikacji config, dodać następujące (gdzie wtyczki to folder dla Twojego zespołu, aby załadować z. Można użyć wielu ścieżek średnik rozdzielone.

<runtime> 
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
     <probing privatePath=".;.\Plugins"/> 
    </assemblyBinding> 
</runtime> 

Zrobione z http://msdn.microsoft.com/en-us/library/823z9h8w%28v=vs.90%29.aspx

+1

Pamiętaj, że użycie sondowania do rozwiązania problemu OP zadziała tylko wtedy, gdy folder wtyczek określony w atrybucie privatePath jest podkatalogiem katalogu głównego aplikacji. Zobacz [msdn docs] (http://msdn.microsoft.com/en-us/library/15hyw9x3 (v = vs.110) .aspx) – BitMask777

4

aby rozwinąć na doskonałą odpowiedź AJ, oto jest klasa zwyczaju pomagać z napowietrznej rejestracji i usuwanie globalne wydarzenie.

public sealed class AddinCustomConfigResolveHelper : IDisposable 
{ 
    public AddinCustomConfigResolveHelper(
     Assembly addinAssemblyContainingConfigSectionDefinition) 
    { 
     Contract.Assert(addinAssemblyContainingConfigSectionDefinition != null); 

     this.AddinAssemblyContainingConfigSectionDefinition = 
      addinAssemblyContainingConfigSectionDefinition; 

     AppDomain.CurrentDomain.AssemblyResolve += 
      this.ConfigResolveEventHandler; 
    } 

    ~AddinCustomConfigResolveHelper() 
    { 
     this.Dispose(false); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool isDisposing) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve -= this.ConfigResolveEventHandler; 
    } 

    private Assembly AddinAssemblyContainingConfigSectionDefinition { get; set; } 

    private Assembly ConfigResolveEventHandler(object sender, ResolveEventArgs args) 
    { 
     // often the name provided is partial...this will match full or partial naming 
     if (this.AddinAssemblyContainingConfigSectionDefinition.FullName.Contains(args.Name)) 
     { 
      return this.AddinAssemblyContainingConfigSectionDefinition; 
     } 

     return null; 
    } 
} 

Proponuję utworzenie instancji w użyciu instrukcji, tak jak poniżej:

// you'll need to populate these two variables 
var configuration = GetConfiguration(); 
var assembly = GetAssemblyContainingConfig(); 

using(new AddinCustomConfigResolveHelper(assembly)) 
{ 
    return (MyConfigSection)configuration.GetSection("myConfigSection"); 
} 
0

Próbowałem odpowiedź AJ, z dodatkiem rileywhite, ale stwierdziliśmy, że nie działa dla mnie.

W moim scenariuszu niestandardowa klasa ConfigurationSection była już w aktualnie uruchomionym złożeniu, a próba jej załadowania powoduje przepełnienie stosu. Nie chciałem również umieścić go w GAC, mimo że rozwiązał problem zgłoszony przez PO.

W końcu znalazłem, że działa to wystarczająco dobrze dla mojego celu. Może inni będą się przydać:

public class CustomConfigurationSection : ConfigurationSection { 
    public CustomConfigurationSection() 
    { 
    var reader = XmlReader.Create(<path to my dll.config>); 
    reader.ReadToDescendant("CustomConfigurationSection"); 
    base.DeserializeElement(reader,false); 
    } 

    // <rest of code> 
} 
0

musieli użyć pełną typu ciąg mojego montażu modułu/plugin, który jest w katalogu sondowania, więc to może być zlokalizowany. Korzystanie EntityFramework jako przykład ...

Niepoprawnie:

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework" 

Prawidłowe

type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"