服务和Web应用程序中的依赖注入

时间:2015-06-02 18:16:21

标签: asp.net dependency-injection windows-services ninject ninject-extensions

我对业务层使用依赖注入,其中包含服务,例如下面的示例:

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 - Transient
  • IMyDbContext - 按要求

Windows服务配置

我希望对它进行配置,以便对于我的Window Service的每次迭代,我都不必新增&#39;我为每次迭代提供的服务。

我想要进行以下配置:

  • IMyService - 单个实例或瞬态
  • IMyDbContext - 每次调用方法时的新实例

问题

当我在两个不同项目之间共享服务时,如何实现这一目标。

我的朋友建议我为我的IDbContext使用工厂,如果我不想在我的ASP.NET应用程序中继续使用Per Request Scope,那就没关系了 - 但我确实如此。我不确定我是否可以将工厂配置为每次为此目的返回相同的实例,并为我的Windows服务中的每个请求添加一个新实例。

问题是,例如,如果我现在使用工厂(对于每次调用DoSomething()都需要新实例的Windows服务):

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绑定的两个项目,我已经有了一个boostrapper,它只是如何我在共享代码库时为不同场景配置绑定。

2 个答案:

答案 0 :(得分:1)

  1. 因为MVC和Windows服务是两个不同的主机,对注入的依赖项的生命周期有不同的要求,所以它们应该有自己的Ninject注册。 MVC将以不同于Windows服务主机的方式定义IMyService。

  2. 如果不可能,并且您必须为两台主机使用相同的ninject注册码,那么应用程序的类型可以是一个命名的discrimator来创建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");
    
  3. 如果Ninject获得&#34; MVC&#34;,你可以单向设置它,如果它得到&#34; WindowsService&#34;,你可以用不同的方式设置它。

    选项1通常是首选,因为MVC和Windows服务具有不同的执行上下文(Http Request n all),并且更容易独立地控制它们的依赖项。

答案 1 :(得分:0)

我告诉我工厂扩展使用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();
}