Unity无法解析Session_Start中的接口并解析错误的DbContext

时间:2013-02-13 19:01:59

标签: c# asp.net-mvc-3 dependency-injection unity-container

我正在使用Mvc3和Unity.Mvc3来构建一个可测试和分离的网站,但我显然做错了。

在我的Application_Start()我注册了一个依赖:

// container is a property of the MvcApplication
// and the HierarchicalLifetimeManager should make sure that the registrations
// only last for this request (or should it?)
_container.Register<Interface, Class>(new HierarchicalLifetimeManager())   

然后在Session_Start()我尝试解决我的依赖关系以将一些数据保存到会话中:

var obj = _container.Resolve<Interface>();

此时我得到一个异常,说Unity无法解析接口,但我以为我为该接口注册了一个类???

我很茫然,找到解决方案变得越来越难。

修改

这是我的整个代码,遗漏了一些不必要的部分:

public class MvcApplication : System.Web.HttpApplication  
{   
    // as EDIT 2 says, this is wrong...
    //private IUnityContainer _container = new UnityContainer();

    protected void Application_Start()
    {
        // mvc stuff, routes, areas and whatnot

        // create container here and it works, almost
        var container = new UnityContainer();

        // register dependencies            
        string connectionString = "String from config";
        container.RegisterInstance<DbContext>(new CustomContext(connectionString), new HierarchicalLifetimeManager())
                 .RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager())
                 .RegisterType(typeof(IRepository<>), typeof(Repository<>), new HierarchicalLifetimeManager());

        // register controller resolver
        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        // if i try to resolve repos here, it works and they all have the same context
        // just like the unit of work
    }

    protected void Session_Start()
    {
        // here the container complains that it can't resolve the interface

        // wrong
        //var userRepo = _container.Resolve<IRepository<User>>();

        // right, but still failes, because it is resolving DbContext
        // instead of using the CustomContext instance
        var userRepo = DependencyResolver.Current.GetService<IRepository<User>>();

        // save some user data to session           
    }
}


public class SampleController : Controller {

    // here the container tries to resolve the System.Data.Entity.DbContext
    // instead of just giving the repo that instance that I registered
    public SampleController(IRepository<Entity> repo) {
    }

}

我显然在这个工作单元,依赖注入的东西上惨遭失败,最糟糕的是我不知道为什么...... 所以请在我开始拔牙之前帮忙。

编辑2:

部分在那里。如果我如上所述创建容器,它在Session_Start()中失败。如果我在Application_Start()中创建它作为局部变量,并使用DependencyResolver,它可以工作。怎么和为什么,打败我?

但它仍在尝试解析DbContext而不是CustomContext实例。

SOLUTION:

好的,所以这是交易:

问题1)访问Session_Start()中的容器:

如编辑2中所述,使用本地容器变量解决了这个问题,并通过DependencyResolver访问容器。

问题2)解析已注册的db上下文实例:

事实证明,注册实例不起作用。 但这确实如此:

container.RegisterType<DbContext, CustomContext>(null, new HierarchicalLifetimeManager(), new InjectionConstructor(connectionString))

但我并不真的感到满意,因为我仍然没有弄清楚为什么会这样。看起来我需要长时间阅读一本书或其他东西。

非常感谢提前。

2 个答案:

答案 0 :(得分:1)

而不是直接访问容器(从你的Q中不清楚你在哪里获得对容器的引用?),为什么不让MVC依赖解析器解决呢?

设置依赖关系解析器(Application_Start()):

DependencyResolver.SetResolver(new UnityDependencyResolver(container));

解析你的界面(Session_Start()):

var obj = DependencyResolver.Current.GetService<IMyInterface>();

答案 1 :(得分:1)

问题是您将RegisterInstanceHierarchecalLifetimeManager一起使用。我猜你正在尝试为每个请求获取一个新实例,因为Unity.Mvc3项目使用LifetimeManager来处理这种魔法(以及HttpModules来管理子容器的创建和销毁)。

问题是,当一个新请求进来时,它会想要构造一个新对象,但不知道如何;你只是在应用程序启动时注册了一次实例,而不是制作对象的方法。所以你需要使用RegisterType()来实现它。

您有两种选择:

  1. 使用InjectionConstructor指定注入值:RegisterType<DbContext, CustomContext>(new HierarchecalLifetimeManager(), new InjectionConstructor(connectionString))

  2. 使用工厂:Container.RegisterType<DbContext>(new InjectionFactory(c => new CustomContext(connectionString)), new HierarcicalLifetimeManager())inspired by this

  3. *注意:参数顺序可能有误。

    如果您想为整个应用程序使用真正的单例实例,请使用ContainerControlledLifetimeManager()(这实际上是RegisterInstance的默认设置,因此您甚至不需要指定它)。但是随着网站的使用,你的DbContext会变得非常庞大。

    此外,关于未在Session_Start()中注册的项目的初始问题:

    ASP.NET维护一个HttpApplication类池。这意味着如果您将Unity容器作为成员变量,您将拥有几个都有自己注册的实例。 Application_Start()只被调用一次,而Session_Start()可以使用不带注册的不同实例来调用。您需要使用静态变量来解决这个问题(这是您最终使用DependencyResolver进行的操作)。