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

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

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 мне не приходилось «обновлять» мою службу для каждой итерации.

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

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

Эта проблема

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

Мой друг предложил использовать Factory для моего 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.

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

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


person Luke    schedule 02.06.2015    source источник
comment
что вы подразумеваете под «совместным использованием услуг между двумя проектами»? Вы используете один и тот же код Ninject для настройки своих зависимостей как для asp.net mvc, так и для проекта службы Windows. обычно каждый хост определяет срок службы/внедрение компонентов... и похоже, что ваш хост mvc находится на другой длине волны, чем служба Windos. следовательно, они должны иметь свои собственные регистрации нинжектов.   -  person Raja Nadar    schedule 02.06.2015
comment
У каждого проекта есть свой Корень Композиции. Поскольку это два разных проекта, каждый проект должен иметь собственное ядро.   -  person Win    schedule 02.06.2015
comment
Вин прав; более подробная информация здесь: blog.ploeh.dk/2015/01/06/ состав-корневое-повторное использование   -  person Mark Seemann    schedule 02.06.2015
comment
Спасибо за ответы. Да, у меня есть отдельный загрузчик для веб-сайта и для службы Windows. Проблема в том, что я хочу настроить DbContext как PerRequest (один и тот же экземпляр для всех запросов) для веб-сайта и как несколько экземпляров (новый для каждого вызова) для службы Windows. Однако они используют одну и ту же кодовую базу (сервисы — это бизнес-уровень).   -  person Luke    schedule 02.06.2015
comment
Итак, как я могу настроить их по-разному, но иметь один и тот же код на бизнес-уровне, когда мне нужно другое поведение. Например, если я решил использовать фабрику и получил свой контекст от фабрики. Например, var dbContext = DbContextFactory.GetInstance() теперь у него есть код, который ссылается на фабрику, поэтому я больше не использую свой синглтон для своего веб-приложения.   -  person Luke    schedule 02.06.2015
comment
@Coutlon, вам не нужно ничего настраивать на своем бизнес-уровне. Я никогда не использовал ninject, но мой любимый контейнер позволил бы мне регистрировать зависимости с индивидуальным временем жизни. В любом случае, если у вас 2 корня и, следовательно, 2 ядра, у вас также должно быть две отдельных регистрации. В вашем приложении MVC вы должны зарегистрировать зависимости своей службы BLL в соответствии с запросом, а в своей службе Windows зарегистрировать свои зависимости BLL как временные или любые другие. Кажется довольно прямолинейным, если я не понимаю.   -  person Prime03    schedule 02.06.2015


Ответы (2)


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

  2. если это невозможно и вы должны использовать один и тот же регистрационный код ninject для обоих ваших хостов, то типом приложения может быть именованный дискриминатор для создания экземпляра 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», вы можете настроить его одним способом, а если он получает «WindowsService», вы можете настроить его по-другому.

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

person Raja Nadar    schedule 02.06.2015

Мне было указано, что расширение factory использует 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();

Принимая во внимание, что для моей оконной службы я хотел, чтобы каждый раз это был новый экземпляр, я установил 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();
}
person Luke    schedule 03.06.2015