nopcommerce中文网

nopcommerce是国外asp.net领域一个高质量的b2c开源项目,基于EntityFramework和MVC开发,QQ群1:75272942(2000人超级群,已满) QQ群2:640322459

导航 - 搜索

转载:想爱容易,相处难:当ASP.NET MVC爱上IoC

也许你会问ASP.NET MVC为什么会爱上IoC?

  相爱的理由常常很简单,就像一首歌中所唱——“只为相遇那一个眼神”。

  而ASP.NET MVC爱上IoC只为IoC能实现MVC控制器的依赖注入。

  下面是博客园招聘频道(job.cnblogs.com)所用的一个MVC控制器:

复制代码
public class EnterpriseController
{
protected IJobService _jobService;
protected IEnterpriseService _enterpriseService;

#region Constructors
public EnterpriseController(IJobService jobService,
IEnterpriseService enterpriseService)
{
_jobService
= jobService;
_enterpriseService
= enterpriseService;
}
#endregion
}
复制代码

  如上面的代码所示,有了IoC进行依赖注入,就不需要在构造函数中专门创建对应于_jobService与_enterpriseService的实例。IoC容器会在运行时自动创建IJobService与IEnterpriseService的实例,并传递给EnterpriseController的构造函数。

  就因为这一点,MVC就爱上了IoC。爱就这么简单。

  但是相爱容易,相处难。。。相处的过程中总会遇到各种各样的问题。。。所以幸福来自于你是否能努力解决这些问题。

  代码世界也一样,当我们让MVC与IoC相处时,就遇到了问题。这里我们以IoC容器Unity为例,说明一下我们遇到的问题与解决方法。

  要想实现Controller的依赖注入,就需要让IoC容器接管Controller的创建,而ASP.NET MVC 3中提供的IDependencyResolver接口就为实现这个提供了可能。所以,我们首先创建一个实现IDependencyResolver接口的UnityDependencyResolver类,代码如下:

复制代码
public class UnityDependencyResolver : IDependencyResolver
{
IUnityContainer container;

public UnityDependencyResolver(IUnityContainer container)
{
this.container = container;
}

public object GetService(Type serviceType)
{
return container.Resolve(serviceType);
}

public IEnumerable<object> GetServices(Type serviceType)
{
return container.ResolveAll(serviceType);
}
}
复制代码

  UnityDependencyResolver的作用就是调用IoC容器(这里是Unity)解析相应类型的实例。创建了UnityDependencyResolver,我们还需要告诉MVC用它进行解析。在Global.asax的Application_Start()方法中添加如下代码:

protected void Application_Start()
{
IUnityContainer container
= new UnityContainer();
DependencyResolver.SetResolver(
new UnityDependencyResolver(container));
}

  我们运行一下程序试试,出现下面的错误提示:

The current type, System.Web.Mvc.IControllerFactory, is an interface and cannot be constructed. Are you missing a type mapping?

  从上面的错误信息可以分析出,错误是发生在调用UnityDependencyResolver.GetService方法时。ASP.NET MVC在运行的时候需要得到IControllerFactory的实现实例,然后用它去创建相应的控制器实例。如果不用IoC容器,MVC默认会创建DefaultControllerFactory的实例。现在用了IoC,MVC找不到IControllerFactory的实现实例(我们根本没有注册嘛),所以出现上面的错误。

  为了解决这个问题,我们注册一下DefaultControllerFactory:

container.RegisterType<IControllerFactory, DefaultControllerFactory>();

  继续运行程序,又出现新的错误:

The current type, System.Web.Mvc.IControllerActivator, is an interface and cannot be constructed. Are you missing a type mapping?

  找不到IControllerActivator的实现实例,看来,创建Controller还需要这个东东。查看MVC的源代码发现IControllerActivator的默认实现是DefaultControllerActivator,但郁闷的是它竟然是private class,无法注册它。别无选择,只能自己实现IControllerActivator,名叫CustomControllerActivator,代码如下:

复制代码
public class CustomControllerActivator : IControllerActivator
{
IController IControllerActivator.Create(
System.Web.Routing.RequestContext requestContext,
Type controllerType)
{
return DependencyResolver.Current
.GetService(controllerType)
as IController;
}
}
复制代码

  继续运行,又出现新的错误:

The current type, System.Web.Mvc.IViewPageActivator, is an interface and cannot be constructed. Are you missing a type mapping?

  天哪!难道MVC中的所有接口都要注册一下。。。

  这时,脑子里突然闪出一个指示牌:

  于是,脚踩刹车,打了一把方向盘,驶上了另一条道 —— 如果IoC容器中没有注册,不引发异常,而是返回null,让MVC用自己的方式去处理。

  修改UnityDependencyResolver的GetService方法:

复制代码
public object GetService(Type serviceType)
{
if (!this.container.IsRegistered(serviceType))
{
return null;
}
return container.Resolve(serviceType);
}
复制代码

  并取消之前在IoC容器中对DefaultControllerFactory与CustomControllerActivator的注册。

  继续运行,成功!虽然成功,但停车一看,原来兜了一个圈子,又回到了出发的地方。一切还是交由MVC处理,IoC容器形同虚设,Controller的依赖注入无法实现。如果这时访问想依赖注入的Controller(构造函数带有参数),会出现下面的错误提示:

No parameter