2011-01-06 40 views
6

我正在使用MEF作爲DI容器,問題是我想從多個部分導入特定部分。如何從MEF中的多個零件導入特定零件?

例如,我有以下代碼:

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 

有沒有什麼解決辦法嗎?

在此先感謝

回答

13

可以導出元數據類,這裏有一個例子:

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<ILogger, INamedMetadata>實例。使用Lazy<T,TMetadata>允許我們在訪問該值之前訪問元數據。在上面的示例中,我使用name參數來選擇要使用的適當記錄器。

如果在導入時實例化該類是不對的,可以使用ExportFactory<T,TMetadata>,它允許您按需添加類型的實例。 (ExportFactory被包括在.NET 4.0中的Silverlight版本,但格倫座沒有拋出的源代碼on codeplex桌面/網絡使用。

我希望幫助。

3

您也可以使用採用合同名稱的導出的重載。然後用合同名稱導入它。

[Export("Service", typeof(IService))] 
public class Service : IService { 
} 

[Export("FakeService", typeof(IService))] 
public class FakeService : IService { 
} 

[Import("FakeService")] 
public IService Service { get; set; } 
+0

由於喬希,我試圖用合同名稱並且它解決了我的問題,但是如果我在出口部件中使用合同名稱,則不能使用ImportMany。有沒有辦法將ImportMany與Import特定部件一起使用? – Ray 2011-01-06 04:35:40

+0

並非如此。總之,無論如何您都不能輕易地將Import和ImportMany結合使用。因爲如果你有一個導入並且有多個導出,你將會遇到同樣的異常,然而你可能會在構造函數中導入ImportMany,然後根據其他邏輯選擇一個導入。 – Josh 2011-01-06 05:18:41

0

您可以添加導出元數據來區分不同的導出。然後在導入端,您將需要使用ImportMany,然後根據元數據對其進行過濾以找到您想要的。