Uzyskanie tylko niezbędnych wtyczek z MEF in.NET

Mam interfejs IMessageSender.

using System.ComponentModel.Composition;

public interface IMessageSender
{
    void Send(string message);
}

I mam dwie wtyczki, które implementują ten interfejs. To jest wtyczka.cs.

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;

[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine(message);
    }
}

A to jest plugin2.cs

[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine(message + "!!!!");
    }
}

I mam ten kod, aby uruchomić te wtyczki z MEF.

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Collections.Generic;
using System;

public class Program
{
    [ImportMany]
    public IEnumerable<IMessageSender> MessageSender { get; set; }

    public static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();

        foreach (var message in p.MessageSender) {
            message.Send("hello, world");
        }
    }

    public void Run()
    {
      Compose();
    }

    private void Compose()
    {
        var catalog = new AggregateCatalog(); 
        catalog.Catalogs.Add(new DirectoryCatalog(@"./"));

        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }
}

Po kompilacji dostaję to, czego chcę.

> mono program.exe 
hello, world
hello, world!!!!

Moje pytanie brzmi, jak Mogę selektywnie wyczerpać wiele wtyczek. Ten przykład po prostu pobiera wszystkie dostępne wtyczki, aby uruchomić je wszystkie, ale co powinienem zrobić, gdy po prostu chcę Uruchom pierwszą wtyczkę lub drugą wtyczkę?

Na przykład, mogę uruchomić tylko plugin2.dll w następujący sposób?

public static void Main(string[] args)
{
    Program p = new Program();
    p.Run();

    var message = messageSender.GetPlugin("plugin"); // ???
    message.Send("hello, world");
}

Rozwiązany

Na podstawie tej strony i odpowiedzi Matthew Abbotta. Mógłbym wymyślić ten działający kod.

Interface code (interface.cs)

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;

public interface IMessageSender
{
    void Send(string message);
}

public interface IMessageSenderMetadata
{
    string Name {get; }
    string Version {get; }
}

[MetadataAttribute]  
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MessageMetadataAttribute : ExportAttribute, IMessageSenderMetadata
{
    public MessageMetadataAttribute( string name, string version)  
            : base(typeof(IMessageSender))  
        {  
            Name = name;  
            Version = version;  
        }  

    public string Name { get; set; }  
    public string Version { get; set; }  
}

Plugin code (Plugin.cs ...)

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;

[MessageMetadataAttribute("EmailSender1", "1.0.0.0")]
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine(message + "????");
    }
}

Program.cs

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Collections.Generic;
using System;
using System.Linq;

public class Program
{
    [ImportMany(typeof(IMessageSender), AllowRecomposition = true)]  
    public IEnumerable<Lazy<IMessageSender, IMessageSenderMetadata>> Senders { get; set; }

    public static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();

        var sender1 = p.GetMessageSender("EmailSender1","1.0.0.0");
        sender1.Send("hello, world");
        sender1 = p.GetMessageSender("EmailSender2","1.0.0.0");
        sender1.Send("hello, world");
    }

    public void Run()
    {
      Compose();
    }

    public IMessageSender GetMessageSender(string name, string version)
    {
      return Senders
        .Where(l => l.Metadata.Name.Equals(name) && l.Metadata.Version.Equals(version))
        .Select(l => l.Value)
        .FirstOrDefault();
    }

    private void Compose()
    {
        var catalog = new AggregateCatalog(); 
        catalog.Catalogs.Add(new DirectoryCatalog(@"./"));

        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
    }
}
Author: prosseek, 2011-06-15

1 answers

MEF obsługuje eksportowanie niestandardowych metadanych towarzyszących eksportowanym typom. Musisz najpierw zdefiniować interfejs, którego MEF będzie używał do tworzenia obiektu proxy zawierającego Twoje metadane. W twoim przykładzie prawdopodobnie będziesz potrzebował unikalnej nazwy dla każdego eksportu, abyśmy mogli zdefiniować:

public interface INameMetadata
{
  string Name { get; }
}

To, co musisz zrobić, to upewnić się, że przypisujesz te metadane dla każdego wymaganego eksportu:

[Export(typeof(IMessageSender)), ExportMetadata("Name", "EmailSender1")]
public class EmailSender : IMessageSender
{
  public void Send(string message)
  {
    Console.WriteLine(message);
  }
}

Co zrobi MEF, to wygeneruje projekt implementacja twojego interfejsu, INameMetadata przy użyciu wartości zapisanej w ExportMetadata("Name", "EmailSender1") atrribute.

Gdy już to zrobisz, możesz zrobić małe filtrowanie, więc przedefiniuj swój [Import] na coś w stylu:

[ImportMany]
public IEnumerable<Lazy<IMessageSender, INameMetadata>> Senders { get; set; }

To, co stworzy MEF, jest wyliczalne z Lazy<T, TMetadata> instancji, które obsługują odroczoną instancję Twojego typu instancji. Możemy zapytać jako:

public IMessageSender GetMessageSender(string name)
{
  return Senders
    .Where(l => l.Metadata.Name.Equals(name))
    .Select(l => l.Value)
    .FirstOrDefault();
}

Uruchomienie tego z argumentem "EmailSender1" dla parametru name spowoduje zwrócenie naszej instancji EmailSender. Na należy zwrócić uwagę na to, w jaki sposób wybraliśmy konkretną instancję do użycia na podstawie zapytania metadanych powiązanych z typem.

Możesz pójść dalej i możesz połączyć atrybuty Export i ExportMetadata w jeden atrybut, taki jak:

[AttributeUsage(AttributeTargets.Class, AllowMultiple=false), MetadataAttribute]
public class ExportMessageSenderAttribute : ExportAttribute, INameMetadata
{
  public ExportMessageSenderAttribute(string name)
    : base(typeof(IMessageSender))
  {
    Name = name;
  }

  public string Name { get; private set; }
}

Pozwala nam to użyć jednego atrybutu do eksportu typu, jednocześnie dostarczając dodatkowe metadane:

[ExportMessageSender("EmailSender2")]
public class EmailSender : IMessageSender
{
  public void Send(string message)
  {
    Console.WriteLine(message);
  }
}

Oczywiście zapytanie w ten sposób daje Ci decyzję projektową. Używanie instancji Lazy<T, TMetadata> oznacza że będziesz w stanie odroczyć instancję instancji, ale to oznacza, że tylko jedna instancja może być utworzona na leniwego. Wariant Silverlight frameworka MEF obsługuje również typ ExportFactory<T, TMetadata>, który pozwala za każdym razem uruchamiać nowe instancje T, zapewniając jednocześnie bogaty mechanizm metadanych.

 19
Author: Matthew Abbott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-15 00:19:00