Использование разных реализаций для одного интерфейса в Autofac

Я хочу сделать следующее.

Я реализовал планировщик с Quartz.Net, а для IOC использую Autofac. Теперь я хочу запускать одно задание несколько раз, но база данных для каждого задания разная. Мой репозиторий принимает параметр, который оборачивает мою строку подключения. Параметр имеет тип IOptions<T> и вводится в конструктор репозитория. Репозиторий также внедряется в различные сервисы.

Проблема в том, что я не могу получить доступ к репозиторию напрямую, потому что задействовано много служб, которые используют один и тот же репозиторий.

Репозиторий реализован кем-то другим и содержит несколько запросов и одно свойство строки подключения. У меня есть разные базы данных для разных стран (система продаж), но они все одинаковые, поэтому у меня просто один репозиторий для всех баз данных.

Строки подключения считываются из файла JSON:

{
  "PriceListSettings": [
    {
      "Country": "DE",
      "ConnectionString": "Initial Catalog=Catalog_DE;Data Source=.\\SQLEXPRESS;Integrated Security=true;",
      "ExecutionTime": "0 0 0 1/1 * ? *"
    },
    {
      "Country": "AT",
      "ConnectionString": "Initial Catalog=Catalog_AT;Data Source=.\\SQLEXPRESS;Integrated Security=true;",
      "ExecutionTime": "0 0 0 1/1 * ? *"
    }
  ]
}

Я хочу иметь область для каждого задания, чтобы я мог вводить или устанавливать другую строку подключения.

С Quarz вы можете установить триггер. Проблема в том, что все задания запускаются в настраиваемое время и должны выполняться один раз за ночь. Но я не знаю точно, когда.

я пытался использовать

using (var scope = Program.Container.BeginLifetimeScope())
{
    //Does not seem to work this way
    //var repo = scope.Resolve<IRepository>();
    //repo.PricelistServiceConfig = priceListConfig;            
}

Я также пытался запустить Quartz в одном потоке, но, похоже, я не могу правильно установить свойство своего репо.

Используемые фреймворки:

  • .Net Framework 4.6 (невозможно обновить)
  • Автофак 4.8.1
  • Autofac.Extras.Quartz 4.2.0
  • Кварц 3.0.6

person Aeneas Motsch    schedule 12.09.2018    source источник
comment
Итак, вы хотите иметь несколько репозиториев с разными объектами подключения, а затем получить их все? Это похоже на шаблон адаптера (репозиторий адаптирует строку подключения). Затем вы сможете разрешить все адаптированные объекты подключения.   -  person Dennis Kuypers    schedule 12.09.2018
comment
Репозиторий реализован кем-то другим и содержит несколько запросов и одно свойство строки подключения. У меня разные базы данных для разных стран (системы продаж), но они все одинаковые, поэтому у меня просто один репозиторий для всех баз данных. Я обновлю свой вопрос   -  person Aeneas Motsch    schedule 12.09.2018
comment
Но у вас по-прежнему есть один экземпляр репозитория для каждого задания (каждый из которых имеет свою строку подключения), правильно? Если да, то как представлены эти строки подключения? У вас есть массив или список из них? Словарь? Пользовательский класс? (как получить правильную строку для задания?)   -  person Dennis Kuypers    schedule 12.09.2018
comment
да это правильно. Я прочитал строки подключения из файла json. Я инициализирую задания на основе количества списков в моем json. Итак, я знаю, какая работа должна подключаться к какой базе данных. я просто не умею их ставить   -  person Aeneas Motsch    schedule 12.09.2018
comment
Затем возможные инструменты: разрешение параметров и /или внедрение свойства , если вы предоставляете автономный код с примерами классов и/или интерфейсов, возможно, я смогу сформулировать ответ на эту обобщенную проблему. (сведены к базовым классам без кварца)   -  person Dennis Kuypers    schedule 12.09.2018
comment
Другое решение: создайте класс Job, который берет пустой экземпляр репозитория и параметры конфигурации, а затем загружает репозиторий.   -  person Dennis Kuypers    schedule 12.09.2018
comment
Ваши ссылки заставили меня работать, спасибо. Я вспомнил, что есть JobDataMap, и с его помощью я могу установить строку подключения в своем репо. Я больше не ввожу его, поэтому мне нужно создать запасной вариант. Со временем я присмотрюсь к разрешению параметров во время выполнения.   -  person Aeneas Motsch    schedule 12.09.2018


Ответы (1)


Кажется, вы почти угадали. Я не знаю, как именно выглядят ваши классы, поэтому я сделал пример с некоторой реализацией заглушки для классов Job, Reposiroty и сервисов. Есть задание, которое получает два сервиса в конструктор. Обеим службам требуется репозиторий в качестве параметра для своих конструкторов:

[Test]
public void AutofacLifetimeScope()
{
    // Arrange
    var builder = new ContainerBuilder();
    builder.RegisterType<Job>();
    builder.RegisterType<AnotherService>();
    builder.RegisterType<SomeService>();
    builder.RegisterType<Repository>().InstancePerLifetimeScope().WithProperty(ResolvedParameter.ForKeyed<Option>("opt"));

    var container = builder.Build();

    // Act
    Job job1;
    var option1 = new Option {ConnectinString = "DE Connection"};
    using (var scope = container.BeginLifetimeScope(c => c.RegisterInstance(option1).Keyed<Option>("opt")))
    {
        job1 = scope.Resolve<Job>();
    }

    Job job2;
    var option2 = new Option { ConnectinString = "AT Connection" };
    using (var scope = container.BeginLifetimeScope(c => c.RegisterInstance(option2).Keyed<Option>("opt")))
    {
        job2 = scope.Resolve<Job>();
    }

    //Assert
    Assert.AreEqual(job1.SomeService.Repository.PriceListConfig, option1);
    Assert.AreEqual(job1.AnotherService.Repository.PriceListConfig, option1);

    Assert.AreEqual(job2.SomeService.Repository.PriceListConfig, option2);
    Assert.AreEqual(job2.AnotherService.Repository.PriceListConfig, option2);
}

public class Repository
{
    public Option PriceListConfig { get; set; }
}

public class Option
{
    public string ConnectinString { get; set; }
    public string Country { get; set; }
    public string ExecutionTime { get; set; }
}

public class Job
{
    public SomeService SomeService { get; }
    public AnotherService AnotherService { get; }

    public Job(SomeService someService, AnotherService anotherService)
    {
        SomeService = someService;
        AnotherService = anotherService;
    }
}

public class AnotherService
{
    public Repository Repository { get; }

    public AnotherService(Repository repository)
    {
        Repository = repository;
    }
}

public class SomeService
{
    public Repository Repository { get; }

    public SomeService(Repository repository)
    {
        Repository = repository;
    }
}

Основная идея состоит в том, чтобы зарегистрировать Repository для каждой области действия с помощью внедрения свойств, которые будут настроены позже при создании области действия. Я сделал внедренные экземпляры доступными через общедоступные свойства. Просто для простого тестирования примера. Надеюсь, поможет.

person KozhevnikovDmitry    schedule 12.09.2018