具有自己配置文件的MEF插件?

7
我正在尝试在运行时加载插件并访问它们的配置文件。它们配置文件中的配置部分映射到从ConfigurationElementCollection、ConfigurationElement和ConfigurationSection派生的类。这些插件及其配置文件位于名为“Plugins”的子文件夹中。
问题在于我似乎无法正确地加载插件配置数据并将其反序列化为它们各自的类。
以下是EmailPlugin.dll的插件配置示例:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="EmailConfigurationSection" type="Foo.Plugins.EmailConfigurationSection, EmailPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" allowDefinition="Everywhere" allowExeDefinition="MachineToApplication" restartOnExternalChanges="true"/>
  </configSections>
  <EmailConfigurationSection server="192.168.0.10">
    <EmailSettings>
      <add  keyword="ERROR"
                    sender="error@error.com"
                    recipients="foo@bar.com, wiki@waki.com"
                    subject = "Error occurred"
                    body = "An error was detected"
            />
    </EmailSettings>
  </EmailConfigurationSection>
</configuration>

我使用以下代码加载它:
    private static System.Configuration.Configuration config = null;
    public static System.Configuration.Configuration CurrentConfiguration
    {
        get
        {
            if (config == null)
            {
                Assembly assembly = Assembly.GetAssembly(typeof(EmailPlugin));
                string directory = Path.GetDirectoryName(assembly.CodeBase);
                string filename = Path.GetFileName(assembly.CodeBase);
                string assemblyPath = Path.Combine(directory, filename);
                config = ConfigurationManager.OpenExeConfiguration(new Uri(assemblyPath).LocalPath);
            }

            return config;
        }
    }

这会导致错误:

这会导致错误:

An error occurred creating the configuration section handler for EmailConfigurationSection: Could not load file or assembly 'EmailPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

我把以下内容添加到配置文件的顶部:

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

所以 DLL 被找到了,但在我尝试检索它时没有被转换为适当的类:

EmailConfigurationSection defaults = CurrentConfiguration.Sections["EmailConfigurationSection"] as EmailConfigurationSection;

它总是返回 null。我知道它正在查看正确的位置和配置文件,因为我可以使用以下代码检索 XML:

var section = CurrentConfiguration.Sections["EmailConfigurationSection"];
string configXml = section.SectionInformation.GetRawXml();

然而,当我尝试使用以下代码进行反序列化时:
var serializer = new XmlSerializer(typeof(EmailConfigurationSection));
object result;

EmailConfigurationSection defaults;
using (TextReader reader = new StringReader(configXml))
{
    defaults = (EmailConfigurationSection)serializer.Deserialize(reader);
}

当我运行程序时,出现了一个异常:

There was an error reflecting type 'Foo.Plugins.EmailConfigurationSection'.

以下是InnerException的内容:

You must implement a default accessor on System.Configuration.ConfigurationLockCollection because it inherits from ICollection.

我猜它指的是EmailConfigElementCollection类,但是这个消息没有意义,因为这个类确实有一个默认访问器:
    public EmailConfigElement this[int index]
    {
        get
        {
            return (EmailConfigElement)BaseGet(index);
        }
        set
        {
            if (BaseGet(index) != null)
            {
                BaseRemoveAt(index);
            }
            BaseAdd(index, value);
        }
    }

我以前在其他项目中成功地使用过这段代码(甚至使用了单独的DLLs/configs),但这是我第一次尝试在MEF中使用它。有人知道问题出在哪里,或者有合适的解决方法吗?

我正在使用.NET 4.5


我在某处读到,你可以简单地在OpenExeConfiguration中使用ExecutingAssebly.Location。 - Luke
可能是MEF导出程序集中的自定义配置部分的重复问题。 - Steven Jeuris
这不是一个重复,而是 http://social.msdn.microsoft.com/Forums/en-US/16df81ea-270b-47e2-bd30-877e0e32c88c/mef-plugins-with-their-own-configuration-files 的直接复制。 - oɔɯǝɹ
那是因为我在两个论坛上都发了这个问题... - ilitirit
1个回答

5
我用以下修改解决了这个问题:
public static System.Configuration.Configuration CurrentConfiguration
{
    get
    {
        if (config == null)
        {
            // Added the next bit
            AppDomain.CurrentDomain.AssemblyResolve += (o, args) =>
            {
                var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
                return loadedAssemblies.Where(asm => asm.FullName == args.Name)
                                           .FirstOrDefault();
            };

            Assembly assembly = Assembly.GetAssembly(typeof(EmailPlugin));
            string directory = Path.GetDirectoryName(assembly.CodeBase);
            string filename = Path.GetFileName(assembly.CodeBase);
            string assemblyPath = Path.Combine(directory, filename);
            config = ConfigurationManager.OpenExeConfiguration(new Uri(assemblyPath).LocalPath);
        }

        return config;
    }
}

我从这个问题中得到了以下信息:Custom configuration sections in MEF exporting assemblies。我之前尝试过,但没有成功。
诀窍在于我必须将运行时标签移到XML配置的底部:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="EmailConfigurationSection" type="Foo.Plugins.EmailConfigurationSection, EmailPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" allowDefinition="Everywhere" allowExeDefinition="MachineToApplication" restartOnExternalChanges="true"/>
  </configSections>
  <EmailConfigurationSection server="255.255.255.1">
    <EmailSettings>
      <clear />
      <add  keyword="FOO"
                    sender="foo@foo.com"
                    recipients="me@you.com"
                    subject = "Foo occurred"
                    body = "Hello"
            />
    </EmailSettings>
  </EmailConfigurationSection>
    <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <probing privatePath="Plugins"/>
      </assemblyBinding>
   </runtime>
</configuration>

你甚至不需要在配置文件中添加运行时部分。 - Pavan Josyula

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接