【问题标题】:How to import specific part from multi parts in MEF?如何从 MEF 中的多部分导入特定部分?
【发布时间】:2011-01-06 04:20:42
【问题描述】:

我使用 MEF 作为 DI 容器,问题是我想从多个部件中导入特定部件。

例如,我有以下代码:

public interface IService
{
    void Send();
}

[Export(typeof(IService))]
public class Service : IService
{
    public void Send()
    {
        Console.WriteLine("Service.Send");
    }
}

[Export(typeof(IService))]
public class FakeService : IService
{
    public void Send()
    {
        Console.WriteLine("FakeService.Send");
    }
}

[Import]
public IService Service { get; set; } // ---> let's say I want to use FakeService

有什么解决办法吗?

提前致谢

【问题讨论】:

    标签: mef


    【解决方案1】:

    您可以将元数据与您的班级一起导出,这是一个示例:

    public interface ILogger
    {
      void Log(string message);
    }
    
    [Export(typeof(ILogger)), ExportMetadata("Name", "Console")]
    public class ConsoleLogger : ILogger
    {
      public void Log(string message)
      {
        Console.WriteLine(message);
      }
    }
    
    [Export(typeof(ILogger)), ExportMetadata("Name", "Debug")]
    public class DebugLogger : ILogger
    {
      public void Log(string message)
      {
        Debug.Print(message);
      }
    }
    

    鉴于该合约和那些示例实现,我们可以将类型导出为Lazy<T, TMetadata>,从而我们可以定义元数据合约:

    public interface INamedMetadata
    {
      string Name { get; }
    }
    

    您无需担心创建元数据的实现,因为 MEF 会将任何ExportMetadata 属性值投影为TMetadata 的具体实现,在我们的示例中为INamedMetadata。通过以上,我可以创建以下示例:

    public class Logger
    {
      [ImportMany]
      public IEnumerable<Lazy<ILogger, INamedMetadata>> Loggers { get; set; }
    
      public void Log(string name, string message)
      {
        var logger = GetLogger(name);
        if (logger == null)
          throw new ArgumentException("No logger exists with name = " + name);
    
        logger.Log(message);
      }
    
      private ILogger GetLogger(string name)
      {
        return Loggers
          .Where(l => l.Metadata.Name.Equals(name))
          .Select(l => l.Value)
          .FirstOrDefault();
      }
    }
    

    在那个示例类中,我导入了许多实例,例如 Lazy&lt;ILogger, INamedMetadata&gt; 实例。使用Lazy&lt;T,TMetadata&gt; 允许我们在访问值之前访问元数据。在上面的示例中,我使用name 参数来选择要使用的适当记录器。

    如果在导入时实例化类是不正确的,您可以使用ExportFactory&lt;T,TMetadata&gt;,它允许您按需启动类型的实例。 (ExportFactory 包含在 Silverlight 版本的 .NET 4.0 中,但 Glenn Block 确实抛出了源代码 on codeplex 供桌面/Web 使用。

    希望对你有帮助。

    【讨论】:

      【解决方案2】:

      您也可以使用带有合同名称的 Export 重载。然后将其与合约名称一起导入。

      [Export("Service", typeof(IService))]
      public class Service : IService {
      }
      
      [Export("FakeService", typeof(IService))]
      public class FakeService : IService {
      }
      
      [Import("FakeService")]
      public IService Service { get; set; }
      

      【讨论】:

      • 感谢 Josh,我尝试使用合同名称,它解决了我的问题,但如果我在出口零件中使用合同名称,我将无法使用 ImportMany。有什么方法可以将 ImportMany 与 Import 特定部分一起使用?
      • 并非如此。一般来说,无论如何您都不能轻松地将 Import 与 ImportMany 结合起来。因为如果你有一个 Import 并且有多个 export,你会遇到同样的异常。但是,您可以在构造函数中使用 ImportMany,然后根据其他逻辑选择导入的其中一个。
      【解决方案3】:

      您可以添加导出元数据以区分不同的导出。然后在导入方面,您将需要使用 ImportMany,然后根据元数据对其进行过滤以找到您想要的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-04
        • 1970-01-01
        • 2011-06-18
        • 1970-01-01
        • 2023-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多