Внедрение зависимостей в сервисе и для веб-приложения

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

public class MyService : IMyService 
{
    private IMyDbContext DbContext;

    public MyService(IMyDbContext dbContext)
    {
        this.DbContext = dbContext;
    }

    public DoSomething(int id)
    {
        // Use the business layer for something
        var user = this.DbContext.Set<User>().Find(id);
    }
}

Как видите, мой сервис использует мою Entity Framework DbContext в качестве зависимости. Я использую Ninject в качестве своего контейнера IoC, но я думаю, что это может относиться к любому.

Конфигурация веб-сайта ASP.NET MVC

Для моего веб-сайта ASP.NET это настроено с помощью Ninject для создания единого IMyDbContext за запрос и только один экземпляр IMyService требуется для запроса - так что это приятно и просто.

  • IMyService - Переходный процесс
  • IMyDbContext - По запросу

Конфигурация службы Windows

Я хочу настроить его так, чтобы на каждой итерации моего Window Service мне не приходилось "обновлять" мой сервис для каждой итерации.

Я хочу иметь следующую конфигурацию:

  • IMyService - один экземпляр или переходный процесс
  • IMyDbContext - новый экземпляр каждый раз, когда вызывается метод

Эта проблема

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

Мой друг предложил использовать Фабрику для моего IDbContextчто было бы хорошо, если бы я не хотел продолжать использовать Per Request Scope в моем приложении ASP.NET - но я делаю. Я не уверен, что могу настроить фабрику так, чтобы она возвращала один и тот же экземпляр каждый раз только для этой цели и новый экземпляр для каждого запроса в моей службе Windows.

Проблема с этим, например, если я использую фабрику сейчас (для службы Windows, которая требует новый экземпляр для каждого вызова DoSomething()):

public class MyService : IMyService 
{
    private IMyContextFactory DbContextFactory;

    public MyService(IMyContextFactory dbContextFactory)
    {
        this.DbContextFactory = dbContextFactory;
    }

    public DoSomething(int id)
    {
        // Get instance of the db for use
        var dbContext = this.DbContextFactory.GetInstance();

        // Use the business layer for something
        var user = dbContext.Set<User>().Find(id);
    }
}

Как вы можете видеть, мне пришлось добавить строку кода, чтобы использовать фабрику.

Это больше не работает для моего веб-сайта, который хочет использовать один (на запрос) экземпляр DbContext.

Мне нужна конфигурация, которая будет работать для обоих сценариев, так как я делюсь базой кода.

У меня уже есть Boostrapper для обоих проектов, который настраивает kernel Привязки, это просто, как я настраиваю свои привязки для различных сценариев, разделяя базу кода.

2 ответа

  1. поскольку MVC и служба Windows - это два разных хоста с разными требованиями к времени жизни внедренных зависимостей, они должны иметь свои собственные регистрации Ninject. MVC будет определять IMyService иначе, чем Windows Service Host.

  2. если это невозможно и вам нужно использовать один и тот же регистрационный код для обоих хостов, тогда тип приложения может быть именованным распознавателем для создания экземпляра IMyService.

    kernel.Bind<IMyService>().To<MyService>().Named("MVC"); // add lifetime thingy
    kernel.Bind<IMyService>().To<MyService>().Named("WindowsService"); // add lifetime
    
    
    // usage
    var iammvc= kernel.Get<IMyService>("MVC");
    

если Ninject получает "MVC", вы можете настроить его 1 способом, а если он получает "WindowsService", вы можете настроить его по-другому.

Вариант 1 обычно предпочтительнее, поскольку MVC и Windows Service имеют разные контексты выполнения (Http Request n all), и легче контролировать их зависимости независимо.

Мне сказали, что расширение фабрики использует IResolutionRoot разрешить зависимости.

Это означает, что когда фабрика создает экземпляр, он создает его в соответствии с существующей конфигурацией типа, как в привязках.

Если я установлю следующую привязку:

Kernel.Bind<IMyDbContext>().To<MyDbContext>().InRequestScope();

Когда я вызываю фабрику, она возвращает один и тот же экземпляр для каждого звонка фабрике:

// All of these will return the same DbContext
var dbContext1 =  this.DbContextFactory.Create();
var dbContext2 =  this.DbContextFactory.Create();
var dbContext3 =  this.DbContextFactory.Create();

Принимая во внимание, что для моего Window Service я хотел, чтобы это был новый экземпляр каждый раз, я установил DbContext на переходный процесс:

Kernel.Bind<IMyDbContext>().To<MyDbContext>().InTransientScope();

Тогда я могу позвонить на завод в рамках моего сервиса:

// This will return a new instance of my DbContext every time
var dbContext1 =  this.DbContextFactory.Create();
var dbContext2 =  this.DbContextFactory.Create();
var dbContext3 =  this.DbContextFactory.Create();

Вот привязка для фабрики:

Kernel.Bind<IMyDbContextFactory>().ToFactory();

Вот интерфейс для Фабрики:

public interface IMyDbContextFactory
{
    IMyDbContext Create();
}
Другие вопросы по тегам