MEF DirectoryCatalog не переопределяет ссылки проекта во время загрузки

У меня интерфейс выглядит следующим образом:

namespace Contract
{
    [InheritedExport(typeof(ITransform))]
    public interface ITransform
    {
       string process(string name);
    }
}

Теперь у меня есть два класса:

using Contract;
namespace ProjectA
{
    public class ProjectA:ITransform
    {

        public string process(string name)
        {
            ProjectXYZ.ProjectXYZ obj = new ProjectXYZ.ProjectXYZ();
            return obj.process("Project A calling");
        }
    }
}

А также

using Contract;
namespace ProjectB
{
    public class Datawarehouse:ITransform
    {

        public string process(string name)
        {
            ProjectXYZ.ProjectXYZ obj = new ProjectXYZ.ProjectXYZ();
            return obj.process("Project B calling");
        }
    }
}

У меня есть другой проект ProjectXYZ(автоматически сгенерированный сторонним инструментом (Altova Mapforce 2012 SP1)).

Для ProjectA настроенный автоматически сгенерированный код из altova mapforce 2012:

namespace ProjectXYZ
{
    public class ProjectXYZ
    {
        public string process(string name)
        {
            name = "This is for Project A :: "+name;
            return name;
        }
    }
}

Для ProjectB настроенный автоматически сгенерированный код из altova mapforce 2012:

namespace ProjectXYZ
{
    public class ProjectXYZ
    {
        public string process(string name)
        {
            string n = "This is for Project B ::"+Result();
            return n;
        }
        public string Result()
        { 
            int op1 = 1;
            int op2 = op1+3;
            return op2.ToString();
        }
    }
}

Автоматически сгенерированные сторонние коды не экспортируются, но его двоичные файлы я использовал как ссылку на ProjectA.Transform и ProjectB.Transform. Поэтому я использую [DirectoryCatalog] для загрузки всех двоичных файлов ProjectA.Transform и ProjectB.Transform в CompositionContainer MEF. Каждый проект компилируется, и его расположение в двоичных файлах (вывод сборки) задается в качестве входных данных для DirectoryCatalog

для дальнейшей композиции.

using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;
namespace AppConsole
{       
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            p.Run();
        }
        public void Run() {

            List<string> extensionPath = new List<string>();
            //Change the extension Path
            extensionPath.Add(@"E:\MEF\MEFForProjectA\ProjectA\bin\Debug");
            extensionPath.Add(@"E:\MEF\MEFForProjectB\ProjectB\bin\Debug");
            foreach (var extension in extensionPath)
            {
                ITransform transform = GetExtension(extension);
                Console.WriteLine("Extension Loaded :{0}", transform.process(extension));

            }
            Console.ReadLine();
        }
        private ITransform GetExtension(string extensionPath)
        {            
            IEnumerable<ITransform> extensions = null;          
            try
            {                
                AggregateCatalog catalog = new AggregateCatalog();
                catalog.Catalogs.Add(new DirectoryCatalog(extensionPath));      
                CompositionContainer container = new CompositionContainer(catalog);
                container.ComposeParts(catalog);
                extensions = container.GetExportedValues<ITransform>();
                return extensions.FirstOrDefault();
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
            return extensions.FirstOrDefault(); 
        }        
    }
}

ProjectA.Transform использует ProjectXYZ.ClassA, тогда как ProjectB.Transform использует ProjectXYZ.ClassB из другой реализации ProjectXYZ. Реализация и классы

ProjectXYZ различается для разных реализаций ITransform. Классы в ProjectXYZ автоматически генерируются с помощью сторонних инструментов, которые я

нужно использовать напрямую. Поэтому я не могу вносить какие-либо изменения в ProjectXYZ.

Таким образом, когда MEF загружает ProjectA.Transform в первый раз, он также загружает ProjectXYZ для использования в качестве ссылки для ProjectA. Когда ProjectB.Transform загружается / экспортируется,

затем, поскольку сборки ProjectXYZ уже находятся в памяти MEF, он использует ссылку на сборки ProjectXYZ, доступную из "C:\ProjectDemo\ ProjectA.Transform \ Bin \ Debug". Таким образом, когда выполняется ProjectB.Transform, он ищет сборки ProjectXYZ из "C:\ProjectDemo\ ProjectB.Transform \ Bin \ Debug", которые он не получает, так как MEF имеет загруженную ссылку ProjectXYZ Assemblies, доступную в "C:\ProjectDemo\". ProjectA.Transform \ Bin \ Debug ".

Как решить эту проблему. MEF загружает части правильно, но не загружает ссылки на вспомогательные DLL желаемым образом. Я также пытался

Атрибут PartCreationPolicy, но результаты совпадают.

Expected Result :
         Extension Loaded :This is for Project A :: Project A calling
         Extension Loaded :This is for Project B :: 4

Actual Result: 
         Extension Loaded :This is for Project A :: Project A calling
         Extension Loaded :This is for Project A :: Project B calling

2 ответа

Решение

Это не проблема MEF. Проблема в загрузочной модели.NET. (или лучше то, как вы загружаете объекты в.net)

Когда MEF загружается, он возвращает правильные объекты. Но при поиске класса ProjectXYZ при загрузке projectB уже есть dll ProjectXYZ, загруженный с правильным именем сборки, на которое ссылается projectB. И загрузчик dll, на который фактически ссылается projectB, не загружается.

Вы можете попробовать сами, просто изменив последовательность добавленных папок в

extensionPath.Add(@"E:\MEF\MEFForProjectB\ProjectB\ Bin\Debug"); extensionPath.Add(@"E:\MEF\MEFForProjectA\ProjectA\ Bin \ Debug");

Тогда вы получите

Расширение загружено: это для проекта B:: 4 Расширение загружено: это для проекта B:: 4

Решение вашей проблемы - переименование сборки. Когда все сборки ProjectXYZ имеют собственное имя файла, вы получите ожидаемый результат.

С уважением, Пит

Я думаю, что это будет случай для метаданных. MEF не может определить, какой экземпляр ITransform использовать, потому что вы всегда используете GetExportedValues<ITransform>().FirstOrDefault(), Если вы предоставили метаданные для ваших частей, например:

Во-первых, определите интерфейс метаданных:

public interface ITransformMetadata
{
    string Name { get; }
}

И пользовательский атрибут экспорта:

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

    public string Name { get; set; }
}

Затем вы можете начать обогащать свой экспорт дополнительными метаданными, которые вы можете запросить позже, например:

[ExportTransform("ClassB")]
public class ClassBTransform : ITransform { }

И с запросом:

var part = container.GetExports<ITransform, ITransformMetadata>()
    .Where(e => e.Metadata.Name.Equals("value"))
    .FirstOrDefault();
return part.Value;

редактировать: когда экспортируется тип, предоставляется специальный фрагмент метаданных, называемый ExportTypeIdentity, который использует пространство имен + имя экспортируемого типа.

В вашем коде у вас есть две части на двух сборках с одинаковым пространством имен и именем. ProjectXYZ.ProjectXYZ. Объединение этого с вашим FirstOrDefault, вероятно, будет вашей проблемой.

Другие вопросы по тегам