Плагин должен использовать старую версию сборки с использованием MEF в. NET Core 3.1 - PullRequest
0 голосов
/ 18 июня 2020

Используя эту статью Используя MEF в. NET Core в качестве отправной точки, я пытаюсь загрузить плагин, который ссылается на старую версию моей воображаемой библиотеки MessageQueue, которая отличается от версии, которая приложение основной консоли использует.

Версия 1.0.0 моего «MessageQueue» возвращает значение «Сообщение из v1.0». Версия 1.1.0 моего "MessageQueue" возвращает значение "Сообщение из v1.1".

Мое главное консольное приложение ссылается на v1.1, тогда как мой плагин ссылается на v1.0. Однако, когда я вызываю плагин, сообщение, возвращаемое из его «MessageQueue», является тем же сообщением, которое возвращает «MessageQueue» консольного приложения, а именно «Сообщение из v1.1».

Я ожидаю, что плагин будет использовать свою версию «MessageQueue» и вместо этого вернет «Сообщение из v1.0».

У меня мое решение разбито на 4 проекта:

  1. ConsoleMEF - приложение основной консоли
  2. Pluginable - библиотека класса, содержащая интерфейс IMessageSender
  3. MefPlugin - библиотека класса, содержащая EmailSender, реализующий IMessageSender, и помечена как [Exported]. Ссылка на жесткую сборку сообщений v1.0
  4. Messages - класс lib, содержащий «MessageQueue» (код проекта - версия 1.1, скомпилированная сборка v1.0 скопирована в путь к плагину)

Код плагина здесь:

[Export(typeof(IMessageSender))] // Socket
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        var messages = new MessageQueue();
        Console.WriteLine($"App Message Queue From Plugin: {messages.GetMessage()}");
        Console.WriteLine($"EMAIL: {message}");
    }
}

Вот как я загружаю плагин:

[Import] // Hook
public IMessageSender MessageSender
{
    get;
    set;
}

void Compose()
{
    // pluginPath contains:
    // - MefPlugin.dll
    // - Pluginable.dll
    // - Messages.dll, Version 1.0
    var pluginPath = Path.GetFullPath(@"c:\temp\plugins");

    var assemblies = Directory
        .GetFiles(pluginPath, "*.dll")
        .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath)
        .ToList();

    var configuration = new ContainerConfiguration()
        .WithAssemblies(assemblies);

    using var container = configuration.CreateContainer();
    MessageSender = container.GetExport<IMessageSender>();
}

Вот как я вызываю плагин:

void Run()
{

    Compose();
    var messages = new MessageQueue();
    // CORRECT: Prints "Message from v1.1"
    Console.WriteLine($"App Message Queue From Console: {messages.GetMessage()}"); 

    // MessageSender.Send also calls the following:
    //Console.WriteLine($"App Message Queue From Plugin: {messages.GetMessage()}"); 
    // WRONG: Prints "Message from v1.1"
    // EXPECTED: "Message from v1.0"
    MessageSender.Send("Hello MEF");
}

MessageQueue v1.0 имеет следующий код:

public class MessageQueue
{
    public string GetMessage()
    {
        return "Message from v1.0";
    }
}

MessageQueue v1.1 имеет следующий код:

public class MessageQueue
{
    public string GetMessage()
    {
        return "Message from v1.1";
    }
}

Раньше я легко делал это, используя AppDomain и BasePath. Но я не могу понять, что делать с AssemblyLoadContext.

Где я ошибаюсь?

1 Ответ

0 голосов
/ 20 июня 2020

Решение выглядит двояким.

  1. Создайте новый AssemblyLoadContext.
  2. Удалите Pluginable.dll из каталога подключаемых модулей.

Compose теперь выглядит так:

void Compose()
{
    // pluginPath contains:
    // - MefPlugin.dll
    // - DELETED: Pluginable.dll.renamed
    // - Messages.dll, Version 1.0
    var pluginPath = Path.GetFullPath(@"c:\temp\plugins");

    //var loadContext = AssemblyLoadContext.Default;
    var loadContext = new AssemblyLoadContext("plugin"); // new context

    var assemblies = Directory
        .GetFiles(pluginPath, "*.dll")
        .Select(loadContext.LoadFromAssemblyPath)
        .ToList();

    var configuration = new ContainerConfiguration()
        .WithAssemblies(assemblies);

    using var container = configuration.CreateContainer();
    MessageSender = container.GetExport<IMessageSender>();
}

Если Pluginable.dll не удален из каталога плагинов, возникает следующее исключение:

System.Composition.Hosting.CompositionFailedException: 'No export was found for the contract 'IMessageSender'.'

Я полагаю, это вызвано, потому что resolver рассматривает Pluginable.dll плагина как отдельный и отличный от Pluginable.dll основного приложения. Как только он будет удален из каталога плагинов, я предполагаю, что преобразователь теперь должен быть вынужден искать ссылку в другом месте.

...