2012-09-16 85 views
2

您好我正在使用Ninject.MVC Nuget包与我的MVC3应用程序,我有一些构造函数注入的当前绑定设置。Ninject.MVC构造函数注入其中注入对象的构造函数采用参数

private static void RegisterServices(IKernel kernel) 
    { 
     kernel.Bind<IUnitOfWork>().To<ERSUnitOfWork>(); 
     kernel.Bind<IRepository<Recipe>>().To<GenericRepository<Recipe>>(); 
    } 

我控制器例子如下:

public class RecipesController : Controller 
{ 
    private readonly IUnitOfWork unitOfWork; 
    private readonly ERSDbContext context; 
    private readonly IRepository<Recipe> recipeRepository; 

    public RecipesController(IUnitOfWork unitOfWork, IRepository<Recipe> recipeRepository) 
    { 
     this.context = new ERSDbContext(); 
     this.unitOfWork = unitOfWork; 
     this.recipeRepository = recipeRepository; 
    } 
} 

我想从控制器中删除私有的DbContext财产和通过新的ERSDbContext()来ERSUnitOfWork和GenericRepository的构造为部分Ninject正在执行的构造函数注入,但最好在控制器内部保持ERSDbContext类的初始化?

任何帮助你如何做到这一点将不胜感激。谢谢

我有点希望它不需要我的NinjectWebCommon类必须创建DbContext,我想要在控制器中初始化。

+1

DbContexts创建起来非常便宜。没有“初始化”。该对象已创建,但未打开任何连接或构建复杂结构。当第一次调用DbContext时,DbContext使用延迟初始化。 –

+0

相关:http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why。 – Steven

+0

我以前见过这个相关的帖子,我看到了像我的一个简单项目的方法注入的例子。我的问题是知道如何将其实现到我当前的示例中。 – Pricey

回答

3

这是你如何抽象你的依赖关系的问题。

因为你想拥有,当你创建你DbContext例如在控制,你应该有一个工厂,将创建特定类型的DbContext情况下,像这样:

public interface IDbContextFactory 
{ 
    T CreateDbContext<T>() where T : DbContext; 
} 

(请注意,如果您的DI框架处理类型参数的通用接口上还算不错,那么你可以用IDbContextFactory interface去,将其与实体框架5)

介绍如果你永远只需要创建一个广告类型DbContext实例EFAULT,参数构造函数,你可以定义你的界面,如下所示:

public interface IDbContextFactory 
{ 
    T CreateDbContext<T>() where T : DbContext, new(); 
} 

,然后定义像这样实现:

public class DbContextFactory : IDbContextFactory 
{ 

    #region Implementation of IDbContextFactory 

    public T CreateDbContext<T>() where T : DbContext, new() 
    { 
     // Create a new instance of T and return. 
     return new T(); 
    } 

    #endregion 
} 

如果需要调用不同的构造函数,那么你删除new() constraint并且必须使用反射调用(或者,您可以创建一个lambda表达式并根据T的类型对其进行高速缓存)以创建键入的DbContext

从那里您将IDbContextFactory合同与您的实施相关联,并将IDbContextFactory实施注入您的课程,就像您将使用其他任何接口一样。

+0

我没有看到这个好处,除非你想单元测试,在这种情况下,你可以让你的DbContext从一个通用接口派生(这更直接)。增加一个额外的工厂似乎没有意义。 –

+0

@MystereMan在问题的最后一句中明确指出,创作的控制是OP想要的东西。仅仅约束自己并不会给OP这个控制权。如果你的控制器中有更长时间运行的方法,那么'DbContext'的生命周期就会被默认扩展(除非你早先处理它)。如果方法代码的一小部分依赖于'DbContext',最好尽快创建并拆除。让这样的事情在方法/控制器的生命中保持开放通常不是好主意。 – casperOne

+0

@MystereMan另外请注意,我并不是主张类型化的'DbContext'从接口派生,关键是要有一个工厂为你生成它们。你可能不想为'DbContext'使用默认的无参数构造函数。 – casperOne

1

这是依赖注入的好处之一,它会自动解析所有构造函数参数及其相关的构造函数参数。它为你做了这一切,只需要定义对象的映射。

所以你的情况,你只是这样做:

kerel.Bind<ERSDbContext>().ToSelf(); 

然后,你可以添加ERSDbContext您UOW和回购,你是好。

如果你想单元测试的东西,那么你就需要抽象的背景下不知何故,无论是作为casperOne提到或我提到(其中你让你的DbContext从通用接口派生),然后做到这一点,而不是:

kernel.Bind<IDbContext>().To<ERSDbContext>(); 

使用DI的好处之一是它控制对象的生命周期。如果您将DbContext的构建推迟到DI容器的外部,那么您必须手动管理其生命周期,而不是允许DI容器根据生命周期策略(例如在请求结束时自动销毁它)来管理它。

我认为延迟创建上下文没有任何可衡量的好处。我认为你正在做过早的优化。

+0

我遇到的问题是,如果其中一个节点正在执行保存,那么在同一个HTTP请求的范围内不能有多个DbContext。这就是为什么我创建了UoW类。原来我的背景是在UoW和我的回购,但那是糟糕的设计。现在我已经把它们分开了,并且使用我的回购查询以及我的任何保存的UoW,就像你说的,我必须确保UoW处理上下文的处置。现在我处于需要将相同的环境传递给UoW和回购站的情况。 – Pricey

+0

与此相关的错误是“不能定义两个对象之间的关系,因为它们连接到不同的ObjectContext对象”,这是我在使用上面的示例时仍然可以获得的东西,不幸的是。感谢您解释更多关于DbContext,这有助于。 – Pricey

+0

对不起,我的例子没有显示它,但有时我在同一个控制器操作中使用多个存储库,这是我的问题。 – Pricey

0

由于我的并发问题,我暂时取消了工厂的使用,而我正在使用这种方法,因为我的时间很短。

kernel.Bind<ERSDbContext>().ToSelf().InRequestScope(); 

然后在我的UoW和GenericRepository中构造函数需要一个ERSDbContext类型参数。

我想稍后改进这种方法,但现在这工作。

+0

我真的没有看到这个问题,因为这正是它的目的。我认为你的代码味道更多的是关于你如何设计你的UoW和Repository。您的存储库应该将UoW作为构造函数参数,并且您的UoW应该将上下文作为构造函数参数。 –

+0

出于利益您为什么认为存储库应该将UoW作为构造函数参数?不应该将它们保持分开,并且存储库应该没有关于如何提交事务的概念?只做查询?我在想的是如何使DbContext成为一个我可以传递的接口..但是我现在所拥有的并不算太坏,因为Ninject正在处理依赖关系,并且我还假设将它添加到HttpContext Items集合中以使其请求作用域? – Pricey

+0

不,Ninject不会将它添加到HttpContext.Items,而是挂钩到RequestEnd事件中,然后处理所有事情。至于为什么仓库应该采用UoW取决于你的外墙的工作方式。您仍然可以将UoW传递给存储库,并且还可以依赖于您的业务对象中的UoW。这样你可以控制提交。 –