Лучшая практика для инициализации в масштабе всего приложения, совместно используемой несколькими компонентами Autofac? (В данном случае в качестве лицензии.)

#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 будет логика. Даже если это простая логика (загрузка файла?) логика все еще существует, и, возможно, было бы лучше поместить эту логику в метод вместо конструктора