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, вероятно, будет вашей проблемой.