MEF 无属性方法:按条件延迟初始化

本文关键字:条件 延迟 初始化 属性 方法 MEF | 更新日期: 2023-09-27 17:55:12

我之前的帖子包含尝试使用无属性(基于约定)方法来配置 MEF: MEF 2:导入许多。
但它包含按条件(特定名称、版本)延迟初始化插件所需的类 PluginMetadataAttribute 中的导出元数据属性用法。
如何摆脱导出属性依赖?

MEF 无属性方法:按条件延迟初始化

我找到了三个解决方案。
解决方案 1(使用类常量字段,解决方案不佳):

public class Plugin1 : IPlugin
{
    public const string Name = "Plugin1";
    public const string Version = "1.0.0.0";
    public void Run()
    {
        Console.WriteLine("Plugin1 runed");
    }
}
// ...
var builder = new RegistrationBuilder();
builder
    .ForTypesDerivedFrom<IPlugin>()
    .Export<IPlugin>(exportBuilder => {
        exportBuilder.AddMetadata("Name", t => t.GetField("Name").GetRawConstantValue());
        exportBuilder.AddMetadata("Version", t => t.GetField("Version").GetRawConstantValue());
    });

解决方案 2(使用类属性,解决方案不佳):

public interface IPluginMetadata
{
    string Name { get; }
    string Version { get; }
}
public interface IPlugin : IPluginMetadata
{
    void Run();
}
public class Plugin1 : IPlugin
{
    public string Name { get { return "Plugin 1"; } }
    public string Version { get { return "1.0.0.0"; } }
    public void Run()
    {
        Console.WriteLine("Plugin1 runed");
    }
}

并通过描述如下的方法获取属性值: https://stackoverflow.com/a/11162876/1986524

解决方案3(使用属性,更好但并非全部满意):

using System;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
using System.Reflection;
namespace MEF2
{
    public interface IPluginMetadata
    {
        string Name { get; }
        string Version { get; }
    }
    public interface IPlugin
    {
        void Run();
    }
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class PluginMetadataAttribute : Attribute, IPluginMetadata
    {
        public string Name { get; set; }
        public string Version { get; set; }
        public PluginMetadataAttribute(string name, string version)
        {
            Name = name;
            Version = version;
        }
    }
    [PluginMetadata("Plugin1", "1.0.0.0")]
    public class Plugin1 : IPlugin
    {
        public void Run()
        {
            Console.WriteLine("Plugin1 runed");
        }
    }
    [PluginMetadata("Plugin2", "2.0.0.0")]
    public class Plugin2 : IPlugin
    {
        public void Run()
        {
            Console.WriteLine("Plugin2 runed");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var builder = new RegistrationBuilder();
            builder
                .ForTypesDerivedFrom<IPlugin>()
                .Export<IPlugin>(exportBuilder => {
                    exportBuilder.AddMetadata("Name", t => t.GetCustomAttribute<PluginMetadataAttribute>().Name);
                    exportBuilder.AddMetadata("Version", t => t.GetCustomAttribute<PluginMetadataAttribute>().Version);
                });
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder);
            using (var container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection)) {
                var plugins = container.GetExports<IPlugin, IPluginMetadata>();
                foreach (var plugin in plugins) {
                    Console.WriteLine("{0}, {1}", plugin.Metadata.Name, plugin.Metadata.Version);
                    plugin.Value.Run();
                }
            }
        }
    }
}

解决方案 3 包含以下代码中的问题:

.Export<IPlugin>(exportBuilder => {
    exportBuilder.AddMetadata("Name", t => t.GetCustomAttribute<PluginMetadataAttribute>().Name);
    exportBuilder.AddMetadata("Version", t => t.GetCustomAttribute<PluginMetadataAttribute>().Version);
})

问题:

  1. 如果缺少元数据,则无法取消添加元数据
  2. 重复的代码t.GetCustomAttribute<PluginMetadataAttribute>()
  3. Export<>未提供过滤器

如果有人知道其他解决方案,请写信。

您在其他问题中引用的配置 MEF 的无属性方法一文包括一个有关如何在不使用属性的情况下添加元数据的示例。

该示例显示了 PartBuilder.ExportProperties 重载的用法,该重载将Action<PropertyInfo, ExportBuilder>作为参数,并使用其中一个ExportBuilder.AddMetadata重载为特定导出添加元数据。

这不是添加元数据的唯一方法。PartBuilder 的所有导出方法都有一个重载,该重载使用 ExportBuilder 参数进行Action<>(或 Action<,>)。您可以使用这些重载并以类似的方式添加元数据。