2011-01-06 12 views
6

Używam MEF jako kontener DI i problem polega na tym, że chcę zaimportować określoną część z wielu części.Jak zaimportować określoną część z wielu części w MEF?

Na przykład, mam następujące kody:

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 

Czy istnieje rozwiązanie?

Dzięki z góry

Odpowiedz

13

można wyeksportować metadane ze swojej klasy, oto przykład:

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); 
    } 
} 

Zważywszy, że umowa i te przykładowe implementacje możemy importować z typów jak Lazy<T, TMetadata> którym możemy zdefiniować umowę Metadane:

public interface INamedMetadata 
{ 
    string Name { get; } 
} 

Nie musisz martwić się o tworzenie implementacji metadanych, ponieważ MEF wyświetli wartości atrybutów ExportMetadata jako konkretną implementację TMetadata, która w naszym przykładzie to INamedMetadata. Z powyższego można utworzyć następujący przykład:

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(); 
    } 
} 

W tej klasie próbki, jestem importowania wielu przypadkach jako Lazy<ILogger, INamedMetadata> przypadkach. Korzystanie z Lazy<T,TMetadata> pozwala nam uzyskać dostęp do metadanych przed uzyskaniem dostępu do wartości. W powyższym przykładzie używam argumentu name, aby wybrać odpowiedni rejestrator do użycia.

Jeśli niewłaściwe jest tworzenie klasy podczas importu, można użyć narzędzia ExportFactory<T,TMetadata>, które pozwala na uruchamianie instancji typów na żądanie. (ExportFactory jest wliczone w wersji Silverlight z .NET 4.0, ale nie rzucać Glenn Blok kodu źródłowego on codeplex do pulpitu użytkowania/Web.

Mam nadzieję, że pomaga.

3

Można używać przeciążenie Export, która przyjmuje nazwę zamówienia, jak również. Następnie zaimportuj go za pomocą nazwy kontraktu.

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

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

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

Dzięki Josh, starałem się używać nazwy umowne i rozwiązał mój problem, ale nie mogę użyć ImportMany, jeśli używam nazwy kontraktu w Exports Parts.Czy jest jakiś sposób na użycie ImportMany z importowaniem konkretnej części? – Ray

+0

Niezupełnie, ogólnie rzecz biorąc nie można w łatwy sposób łączyć importu z funkcją ImportMany. Przyczyna, jeśli masz import i istnieje więcej niż jeden eksport, trafisz w ten sam wyjątek. Możesz jednak importować dowolnie w konstruktorze, a następnie wybrać jeden z importowanych na podstawie innej logiki. – Josh

0

Możesz dodać metadane eksportu, aby odróżnić od siebie różne eksporty. Następnie po stronie importowania musisz użyć funkcji ImportMany, a następnie przefiltrować ją w oparciu o metadane, aby znaleźć żądaną.

Powiązane problemy