#c# #autofac #aspose
#c# #autofac #аспозе
Вопрос:
Мы используем Autofac, и некоторые из наших компонентов используют библиотеки Aspose, для которых требуется лицензия. Лицензия загружается следующим образом:
public class MyAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
// [Register stuff]
LoadAsposeLicense();
}
private void LoadAsposeLicense()
{
// Load the Aspose license.
string licenseFile = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", "Aspose.Total.lic");
Aspose.Slides.License license = new Aspose.Slides.License();
license.SetLicense(licenseFile);
}
}
Я думал, что это хорошая идея, пока не захотел повторно использовать DLL из некоторого кода, который не нуждается в функциональности Aspose. У нас есть лицензия на все продукты Aspose, но прямо сейчас мы используем только материалы PowerPoint. Однако в будущем это может измениться, и у нас могут появиться другие компоненты, которым потребуется функциональность Word. Лицензия будет такой же, но я предполагаю, что ее нужно будет инициализировать отдельно.
Мне интересно, имеет ли смысл вводить такой простой лицензионный компонент, который был бы зарегистрирован как одноэлементный (или я мог бы быть еще ленивее и поместить код в статический конструктор):
class AsposeSlidesLicense
{
public AsposeSlidesLicense()
{
string licenseFile = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", "Aspose.Total.lic");
Aspose.Slides.License license = new Aspose.Slides.License();
license.SetLicense(licenseFile);
}
}
Затем я мог бы использовать его следующим образом:
class MyComponentThatUsesAspose : ISlidesCreatorService
{
public MyComponentThatUsesAspose(AsposeSlidesLicense license)
{
// Dummy constructor to make sure the license is loaded
}
public void CreateSlide()
{
// Use Aspose library
}
}
Но поскольку license
зависимость (параметр) не имеет другой функции, кроме запуска загрузки лицензии, я немного беспокоюсь, что она может быть оптимизирована либо компилятором, либо Resharper, либо другим разработчиком.
Итак, мой вопрос:
Как лучше всего разместить подобный код инициализации, который может потребоваться нескольким компонентам для запуска один раз? И только тогда, когда эти службы необходимы (создаются экземпляры компонентов)?
Ответ №1:
Для запуска кода при запросе компонента вы можете использовать событие OnActivating
autofac lifetime
Вы можете прослушивать такое событие при регистрации, и каждый раз, когда запрашивается компонент, это событие будет запускаться.
builder.RegisterType<LicenseVerifier>()
.As<ILicenseVerifier>()
.SingleInstance()
builder.RegisterType<YourComponent>()
.OnActivating(e => e.Context.Resolve<ILicenseVerifier>().EnsureLicense())
в вашем EnsureLicense
вы можете делать все, что хотите, чтобы убедиться, что лицензия применяется.
Если у вас много компонентов, может быть хорошей идеей перенести OnActivating
вызов в расширение пользовательского метода и написать что-то вроде :
builder.RegisterType<YourComponent>()
.WithLicense()
другим вариантом было бы добавить атрибут к YourComponent
[RequireLicense]
public class YourComponent {}
и создайте модуль autofac, который добавит OnActivating
часть ко всем компонентам, имеющим атрибут.
Если вы хотите запускать код при запуске приложения, Autofac имеет 2 механизма.
- Вы можете реализовать
IStartable
интерфейс Autofac, который запускается при сборке контейнера.public class StartupMessageWriter : IStartable { public void Start() { Console.WriteLine("App is starting up!"); } }
и зарегистрируйте его следующим образом :
builder .RegisterType<StartupMessageWriter>() .As<IStartable>() .SingleInstance();
- Вы можете зарегистрировать компонент с
AutoActivate
помощью . В таком случае компонент будет собран при сборке контейнераbuilder .RegisterType<TypeRequiringWarmStart>() .AsSelf() .AutoActivate();
- вы также можете создать свой собственный интерфейс и разрешить их после инициализации контейнера.
вы можете найти более подробную информацию об autofac в рабочем коде в документации по сборке контейнера.
Комментарии:
1. Спасибо за ваш ответ, это может быть полезно для других сценариев. Извините, мой вопрос, очевидно, был сформулирован неправильно: теперь мой код запускается при запуске, и он работает просто отлично. Но я хочу, чтобы код запускался только после создания экземпляра одного или нескольких конкретных компонентов. В этом смысле это одноэлементная зависимость, разделяемая несколькими компонентами. Прямо сейчас моему модулю всегда требуется наличие этой лицензии, даже если вызывающий код не нуждается в конкретных службах.
2. Спасибо! Решение атрибута выглядит очень красиво. Каково ваше мнение об использовании «фиктивной зависимости», как я предложил (зависимость, которая включается в конструктор только для обеспечения загрузки лицензии)?
3. Я избегаю наличия бизнес-логики в конструкторе ( blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple ). Вы можете перенести свою логику в
EnsureLicense
метод и вызвать этот метод в некоторых методах ваших компонентов. Но я предпочитаю решение с использованием атрибута, которое является более кратким, и ваш компонент не должен знать о лицензировании. Вы также можете расширить это, используя castle dynamic proxy для генерации прокси ( AOP ), чтобы добавить лицензионный материал для некоторых методов.4. В конструкторе не было бы никакой логики — совсем наоборот: для службы лицензий был бы, казалось бы, неиспользуемый параметр конструктора. «Логика» будет обрабатываться Autofac, а именно созданием только одноэлементного экземпляра этой службы. Таким образом, реализующий компонент просто загрузит лицензию, и Autofac убедится, что она не будет загружена дважды. Таким образом, я бы добился того же. Но это выглядело бы так, как если бы зависимость ILicenseService можно было удалить.
5. В конструкторе LicenseService будет логика. Даже если это простая логика (загрузка файла?) логика все еще существует, и, возможно, было бы лучше поместить эту логику в метод вместо конструктора