Внедрение конструктора MEF
-
18-09-2019 - |
Вопрос
Я пытаюсь выяснить атрибут внедрения конструктора MEF.Я понятия не имею, как мне приказать ему загрузить параметры конструктора.
Это свойство, которое я пытаюсь загрузить
[ImportMany(typeof(BUsers))]
public IEnumerable<BUsers> LoadBUsers { get; set; }
Вот код, который я использую для импорта сборок.
try
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog("DI"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
Вот класс, который я пытаюсь загрузить
[Serializable]
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
Решение
Когда вы используете атрибут ImportingConstructor, параметры конструктора становятся импортируемыми.По умолчанию то, что вы импортируете (имя контракта), зависит от типа параметра или свойства, в которое вы импортируете.Таким образом, в этом случае тип контракта для обоих ваших импортов — строковый, и между первым и вторым параметром нет реальной разницы.
Похоже, вы пытаетесь использовать импорт для предоставления значений конфигурации, а это не обязательно то, для чего он был разработан.Чтобы заставить его делать то, что вы хотите, вам следует переопределить имя контракта для каждого из параметров, например:
[ImportingConstructor]
public EditProfile([Import("Method")] string Method, [Import("Version")] string Version)
{ }
Затем вам понадобится экспортировать метод и версию в ваш контейнер.Один из способов сделать это — просто добавить их напрямую:
var container = new CompositionContainer(catalog);
container.ComposeExportedValue("Method", "MethodValue");
container.ComposeExportedValue("Version", "2.0");
container.ComposeParts(this);
(Обратите внимание, что ComposeExportedValue на самом деле является методом расширения, определенным в статическом классе AttributedModelServices.)
Если вы хотите прочитать эти значения из какого-либо файла конфигурации, вы можете создать свой собственный поставщик экспорта, который считывает конфигурацию и предоставляет содержащиеся в ней значения в качестве экспорта в контейнер.
Альтернативный способ справиться с этим — просто импортировать интерфейс, который обеспечивает доступ к значениям конфигурации по имени, и получать нужные значения из тела конструктора.
Другие советы
Мне нравится решение Дэниела;однако я вижу только одно — это тесную связь имен параметров между актером (который создает CompopositionContrainer()) и частью экспорта с [ImportingConstructor] для настроенного CTOR.Например, «Метод» имеет два совпадения в обоих местах.Трудно поддерживать экспортную часть, если актер и экспортная часть находятся в разных проектах.
Если это возможно, я бы добавил второй CTOR в класс экспортной части.Например:
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(EditProfileParameters ctorPars)
: this(ctorPars.Method, ctorPars.Version) {}
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
Класс EditProfileParameters должен быть простым:два свойства метода и версии:
[Export]
public class EditProfileParameters{
public string Method { get; set; }
public string Version { get; set; }
}
Ключевым моментом является добавление атрибута Export в класс.Тогда MEF сможет сопоставить этот класс с параметром CTOR EditProfile.
Вот пример добавления части экспорта в контейнер:
var container = new CompositionContainer(catalog);
var instance1 = new EditProfileParameters();
// set property values from config or other resources
container.ComposeExportedValue(instance1);
container.ComposeParts(this);
Хоть и поздно, но вот еще один подход, использующий менее известную особенность MEF:Экспорт недвижимости
public class ObjectMother
{
[Export]
public static EditProfile DefaultEditProfile
{
get
{
var method = ConfigurationManager.AppSettings["method"];
var version = ConfigurationManager.AppSettings["version"];
return new EditProfile(method,version);
}
}
}
Чтобы это работало, для ObjectMother не требуется никаких использований, а для EditProfile не требуются никакие атрибуты.