为ASP.NET MVC创建一个基于Unity的ControllerFactory

谈到IoC和ASP.NET的集成,很多人会先后想到Ninject,不过我们个人还是倾向于Unity。这篇文章简单地介绍如果创建基于Unity的ControllerFactory。如下面的代码所示,我们通过直接继承DefaultControllerFactory创建一个自定的UnityControllerFactory。构造函数指定的是配置的UnityContainer的名称,如果没有显式指定则采用默认的UnityContainer。在重写的GetControllerInstance方法中,直接调用IUnityContainer的Resolve方法根据Controller类型创建相应的对象。[源代码从这里下载]

   1: public class UnityControllerFactory: DefaultControllerFactory
   2: {
   3:     public IUnityContainer Container { get; private set; }
   4:     public UnityControllerFactory(string containerName = "")
   5:     {
   6:         IUnityContainer container = new UnityContainer();
   7:         UnityConfigurationSection configSection = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
   8:         if (null == configSection && !string.IsNullOrEmpty(containerName))
   9:         {
  10:             throw new ConfigurationErrorsException("Cannot find <unity> configuration section");
  11:         }
  12:  
  13:         if (string.IsNullOrEmpty(containerName))
  14:         {
  15:             configSection.Configure(container);
  16:         }
  17:         else
  18:         {
  19:             configSection.Configure(container, containerName);
  20:         }
  21:         this.Container = container;
  22:     }
  23:     protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
  24:     {
  25:         Guard.ArgumentNotNull(controllerType, "controllerType");
  26:         return (IController)this.Container.Resolve(controllerType);
  27:     }
  28: }

为了演示DefaultControllerFactory的作用,我们来创建一个简单的例子。假设我们要创建一个维护联系人的应用,我们通过具有如下定义的Contact类型表示联系人,而IContactRepository接口定义了一个从存储中获取所有联系人的GetAllContacts方法,DefaultContactRepository是对IContactRepository接口的实现。

   1: public class Contact
   2: {
   3:     public string Name { get; set; }
   4:     public string Gender { get; set; }
   5:     public string Address { get; set; }
   6: }
   7:  
   8: public interface IContactRepository
   9: {
  10:     IEnumerable<Contact> GetAllContacts();
  11: }
  12:  
  13: public class DefaultContactRepository : IContactRepository
  14: {
  15:     public IEnumerable<Contact> GetAllContacts()
  16:     {
  17:         yield return new Contact
  18:         {
  19:             Name = "Zhang San",
  20:             Gender = "Male",
  21:             Address = "#328, XingHu Street, Su Zhou, Jiang Su Province, PRC."
  22:         };
  23:  
  24:         yield return new Contact
  25:         {
  26:             Name = "Li Si",
  27:             Gender = "Female",
  28:             Address = "#328, Jin Ji Hu Road, Su Zhou, Jiang Su Province, PRC."
  29:         };
  30:     }
  31: }

我们在Web应用的主页显示联系人列表,为此我创建了如下一个HomeController。在这里我们演示的是构造器注入,所以我们通过构造函数指定的IContactRepository对象来初始化Repository属性。在Action方法Index中调用IContactRepository的GetAllContacts方法为对应的View指定Model。

   1: public class HomeController : Controller
   2: {
   3:     public IContactRepository Repository { get; private set; }
   4:     public HomeController(IContactRepository repository)
   5:     {
   6:         this.Repository = repository;
   7:     }
   8:     public ActionResult Index()
   9:     {
  10:         return View(this.Repository.GetAllContacts());
  11:     }
  12: }

Index.cshtml代码如下所示,这是一个Model类型为IEnumerable<Contact>的View,它将所有的联系人信息列出来。

   1: @model IEnumerable<Artech.Web.Mvc.Extensions.Contact>
   2: @{
   3:     ViewBag.Title = "Index";
   4: }
   5:  
   6: <h2>Contact List</h2>
   7:  
   8: <div>
   9: <ul>
  10: @foreach (var contact in this.Model)
  11: { 
  12:     <li>
  13:         <h3>@contact.Name</h3>
  14:         <p>Gender: @contact.Gender</p>
  15:         <p>Address: @contact.Address</p>
  16:         <hr />
  17:     </li>
  18: }
  19: </ul>
  20: </div>

自定义的UnityContainerFactory的注册定义在Gloable.asax中。初次之外,额外需要做的是忽略掉针对favicon.ico的路由,否则程序运行将会失败。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     public static void RegisterGlobalFilters(GlobalFilterCollection filters)
   4:     {
   5:         filters.Add(new HandleErrorAttribute());
   6:     }
   7:  
   8:     public static void RegisterRoutes(RouteCollection routes)
   9:     {
  10:         routes.IgnoreRoute("favicon.ico");
  11:         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  12:         routes.MapRoute("Default", "{controller}/{action}/{id}",
  13:             new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
  14:         );
  15:     }
  16:  
  17:     protected void Application_Start()
  18:     {
  19:         AreaRegistration.RegisterAllAreas();
  20:         RegisterGlobalFilters(GlobalFilters.Filters);
  21:         RegisterRoutes(RouteTable.Routes);
  22:  
  23:         ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());
  24:     }
  25: }

接口IContactRepository和DefualtContactRepository之间的映射关系定义在如下所示的<unity>配置中。

   1: <unity>
   2:   <alias alias="IContactRepository" type="Artech.Web.Mvc.Extensions.IContactRepository, UnityIntegration" />
   3:   <alias alias="DefaultContactRepository" type="Artech.Web.Mvc.Extensions.DefaultContactRepository, UnityIntegration" />
   4:   <container>
   5:     <register type="IContactRepository" mapTo="DefaultContactRepository"/>
   6:   </container>
   7: </unity>

通过浏览器访问Web应用的主页,将会得到如下所示的联系人列表。

image

标签: Unity, ASP.NET, MVC
posted @ 2011-12-13 16:35 Artech 阅读(2350) 评论(13) 编辑 收藏

 回复 引用 查看   
#1楼[楼主] 2011-12-13 16:43 Artech      
 回复 引用 查看   
#2楼 2011-12-13 16:51 Mainz      
MEF可以吗?都是内置的。
 回复 引用 查看   
#3楼[楼主] 2011-12-13 17:04 Artech      
引用Mainz:MEF可以吗?都是内置的。

还真没有认真想过这个问题,这个问题值得考虑一下。

 回复 引用 查看   
#4楼 2011-12-13 17:10 mickeysuda      
@Mainz
常见的依赖注入,MEF可以支持,但是MEF设计的本意不是DI Container。

 回复 引用 查看   
#5楼 2011-12-13 17:11 幸运草      
老A 的作品就是实用。
 回复 引用 查看   
#6楼 2011-12-13 17:12 mickeysuda      
哎,有时间要系统学学MVC了。跟不上啊。
 回复 引用 查看   
#7楼[楼主] 2011-12-13 17:24 Artech      
引用幸运草:老A 的作品就是实用。

呵呵,我的很多文章都来源于项目对框架的需求。

 回复 引用 查看   
#8楼[楼主] 2011-12-13 17:25 Artech      
引用mickeysuda:哎,有时间要系统学学MVC了。跟不上啊。

在.NET平台下开发Web应用,个人觉得MVC是首选。

 回复 引用 查看   
#9楼 2011-12-14 11:00 Kain      
引用Artech:
引用mickeysuda:哎,有时间要系统学学MVC了。跟不上啊。

在.NET平台下开发Web应用,个人觉得MVC是首选。

还有其他选择么,ORZ

 回复 引用 查看   
#10楼 2012-01-01 22:56 我是你的猪      
在用Area的时候似乎会报错
Guard.ArgumentNotNull(controllerType, "controllerType");

controllerType为null

 回复 引用 查看   
#11楼 2012-01-11 11:02 Leo.Z      
引用我是你的猪:
在用Area的时候似乎会报错
Guard.ArgumentNotNull(controllerType, "controllerType");

controllerType为null


同问

 回复 引用 查看   
#12楼[楼主] 2012-01-11 12:46 Artech      
引用Leo.Z:
引用我是你的猪:
在用Area的时候似乎会报错
Guard.ArgumentNotNull(controllerType, "controllerType");

controllerType为null


同问

我也运到过这种情况,正确的写法应该是
if(controllerType == null)
{
return null;
}

 回复 引用 查看   
#13楼 2012-02-22 13:15 遗忘海岸      
打不开啊,MVC3?
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 2286300 WGENY/4JnLU=