【问题标题】:Unity can't resolve interface in Session_Start and resolves wrong DbContextUnity 无法解析 Session_Start 中的接口并解析错误的 DbContext
【发布时间】:2013-02-13 19:01:58
【问题描述】:

我正在使用 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 实例。

解决方案:

好的,这就是交易:

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

如 EDIT 2 中所述,使用本地容器变量可以解决该问题,并通过 DependencyResolver 访问容器。

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

事实证明,注册实例不起作用。 不过这样做:

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

但我并不真正感到满意,因为我仍然不明白为什么会这样。看来我很长一段时间都需要看一本书什么的。

非常感谢。

【问题讨论】:

  • 你从哪里得到 _container?
  • 我在MvcApplication类中创建了一个实例,private IUnityContainer _container = new UnityContainer();

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


【解决方案1】:

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

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

你有两个选择:

  1. 使用 InjectionConstructor 指定注入值:RegisterType&lt;DbContext, CustomContext&gt;(new HierarchecalLifetimeManager(), new InjectionConstructor(connectionString))

  2. 使用工厂:Container.RegisterType&lt;DbContext&gt;(new InjectionFactory(c =&gt; new CustomContext(connectionString)), new HierarcicalLifetimeManager()) (inspired by this)

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

如果您想为整个应用程序提供一个真正的单例实例,请使用ContainerControlledLifetimeManager()(这实际上是 RegisterInstance 的默认值,因此您甚至不需要指定它)。但是随着站点的使用,您的 DbContext 将变得相当庞大。

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

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

【讨论】:

  • 其实1)下的注册是我后来尝试的,成功了。我只是在我的问题编辑中输入了错误的代码 sn-p (那时真的很晚了)。但是感谢您对发生的事情的解释。
【解决方案2】:

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

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

DependencyResolver.SetResolver(new UnityDependencyResolver(container));

解析你的接口(Session_Start()):

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

【讨论】:

  • 我确实使用了 UnityDependencyResolver。我会看看在 Session_Start 中使用它是否有效。但这会很奇怪,因为它只是代表我在问题中所做的事情。
  • 它有点像+1,但有一些其他的修改......我还没有到那里......
  • 我接受这一点,因为它确实引导我找到解决方案,但对于任何有类似问题的人,请先阅读整个内容......
  • 您的 CustomContext 类是否继承自 DbContext?
  • 是的。当我做registerType 时它会起作用,这完全是奇怪的。我不知道为什么我不能只注册一个实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-21
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 2021-07-04
相关资源
最近更新 更多