MVC 2.0+Entity Framework在使用依赖注入时遇到的问题

前言:

最近在看MVC 3.0 Beta Release Note的时候,发现了IDependencyResolver接口,又看到了两篇关于MVC下使用依赖注入的文章(传送门1传送门2)。

于是乎,对依赖注入进行了学习,并对以前做的一个网站进行了重构(使用Unity)。

其实完全没有必要,因为无法充分体现依赖注入的优势,反而在性能上有点损失,就当练手一下吧~

 

在这个过程中(网站是MVC2.0做的),发现MVC2.0和Entity Framework在依赖注入过程中出现了一些问题,所以在此,详解一下。

 

MVC 2.0下实现依赖注入:

MVC2.0对依赖注入的支持度很小,理解依赖注入的原理后大家都知道,这里的关键就是需要找到一个切入点,分析对象之间的关系和生命周期后发现,从Controller切入是一个明智的选择。

那怎么拦截原本的Controller创建过程,替代成我们自己的创建过程呢?

 

看了RoRoWoBlog的源码和传送门1这篇文章后,发现MVC中的Controller是由 DefaultControllerFactory 这个类创建的,很明显,这是一个Controller工厂类,职责就是用来创建Controller对象的。

反编译之:

image

 

可以发现内部有大量的虚方法,而且从名字中可以看出,这是这个工厂的关键,继承并重写之即可。

public class MyConrtollerFctory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (requestContext == null || controllerType == null)
        {
            throw new ArgumentNullException("controller");
        }
        return MvcApplication.Container.Resolve(controllerType) as IController;
    }
}

 

接下来利用一个静态方法在Global.asax中替换默认的Controller工厂

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    //替换默认Controller工厂
    ControllerBuilder.Current.SetControllerFactory(new MyConrtollerFctory());
}

 

如何正常地抛出异常:

一切看似是那么地顺利,运行一下,成功了!

但是,忽然发现问题了!

image

 

当初输入一个不存在的Controller的时候,竟然报错了!而不是返回404错误!

高亮的那段话,显示问题出在这,看上去是在替代这个方法的时候,没有正确地抛出异常

 

继续反编译,查看这个方法:

protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    if (controllerType == null)
    {
        throw new HttpException(0x194, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[] { requestContext.HttpContext.Request.Path }));
    }
    if (!typeof(IController).IsAssignableFrom(controllerType))
    {
        throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[] { controllerType }), "controllerType");
    }
    return this._activatorResolver.Current.Create(requestContext, controllerType);
}

 

看上去问题就出在这了,由于我们抛出了自定的异常,导致外层没有Catch到,所以它认为这不是404错误

 

既然知道了原因,那解决起来岂不是很简单?直接把这段代码拷贝过来就行了~

但是问题又来了,这段代码中有几个对象是 internal(程序集可见)

我们的代码和它不在同一个程序集,那当然也不能访问了… 怎么办?继续反编译?把代码复制过来?

 

这个方案恐怕很不友好,其实,有一个小技巧,虽然看起来不怎么样,但是它的确有效~

public class MyConrtollerFctory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (requestContext == null || controllerType == null)
        {
            //关键
            base.GetControllerInstance(requestContext, controllerType);
        }
        return MvcApplication.Container.Resolve(controllerType) as IController;
    }
}

 

将重写Controller工厂类的时候,如果需要抛出异常,就直接访问基类的方法,让基类去抛吧~ 这样就不用担心什么了~

image

 

Entity Framework中遇到的问题:

不仅是MVC2.0,EF对依赖注入的支持度也不高。

主要体现在,ObjectContext 是由工具自动生成的,可以修改,但不建议修改它。

ObjectContext 又是有多个构造函数的,要么写配置文件,要么给它的构造函数上加上 InjectionConstructor 这个Attribute。

前者太麻烦,我只是一个小项目,没必要写配置文件;后者要修改核心代码,不符合上面的原则。

 

其实还有一种很方便的办法可以解决这个问题,只要在 UnityContainer  中注册一下 ObjectContext 的构造函数即可。

/// <summary>
/// Unity容器
/// </summary>
static private UnityContainer container;
static public UnityContainer Container
{
    get
    {
        if (container == null)
        {
            container = new UnityContainer();
            //注册构造函数
            container.RegisterInstance<ModelContainer>(new ModelContainer());
        }
        return container;
    }
}

 

后记:

据VC 3.0 Beta对依赖注入有了很好的支持(传送门2),期待~ 希望在正式版中能确定一下用法,不要一直变了~

posted @ 2010-10-16 14:18  Dozer  阅读(2772)  评论(0编辑  收藏  举报