我正在使用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))
但我并不真的感到满意,因为我仍然没有弄清楚为什么会这样。看起来我需要长时间阅读一本书或其他东西。
非常感谢提前。
答案 0 :(得分:1)
而不是直接访问容器(从你的Q中不清楚你在哪里获得对容器的引用?),为什么不让MVC依赖解析器解决呢?
设置依赖关系解析器(Application_Start()):
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
解析你的界面(Session_Start()):
var obj = DependencyResolver.Current.GetService<IMyInterface>();
答案 1 :(得分:1)
问题是您将RegisterInstance
与HierarchecalLifetimeManager
一起使用。我猜你正在尝试为每个请求获取一个新实例,因为Unity.Mvc3项目使用LifetimeManager来处理这种魔法(以及HttpModules来管理子容器的创建和销毁)。
问题是,当一个新请求进来时,它会想要构造一个新对象,但不知道如何;你只是在应用程序启动时注册了一次实例,而不是制作对象的方法。所以你需要使用RegisterType()来实现它。
您有两种选择:
使用InjectionConstructor指定注入值:RegisterType<DbContext, CustomContext>(new HierarchecalLifetimeManager(), new InjectionConstructor(connectionString))
使用工厂:Container.RegisterType<DbContext>(new InjectionFactory(c => new CustomContext(connectionString)), new HierarcicalLifetimeManager())
(inspired by this)
*注意:参数顺序可能有误。
如果您想为整个应用程序使用真正的单例实例,请使用ContainerControlledLifetimeManager()
(这实际上是RegisterInstance的默认设置,因此您甚至不需要指定它)。但是随着网站的使用,你的DbContext会变得非常庞大。
此外,关于未在Session_Start()中注册的项目的初始问题:
ASP.NET维护一个HttpApplication类池。这意味着如果您将Unity容器作为成员变量,您将拥有几个都有自己注册的实例。 Application_Start()只被调用一次,而Session_Start()可以使用不带注册的不同实例来调用。您需要使用静态变量来解决这个问题(这是您最终使用DependencyResolver进行的操作)。