自定义的ControllerFactory:接口实现,支持Area

几个星期之前,有个朋友对我说,他的项目中需要将前后台区分开来,也就是类似分Area的功能。不过Area只在MVC 2中出现,因此现在想在1.0版本中先实现类似的功能了。他打算,根据Route中捕获的内容(如“area”),然后去找对应命名空间下的Controller。这样看来不难,似乎只要在Route上做点配置,而默认的DefaultControllerFactory已经对命名空间的查询提供支持了(可惜有线程安全的问题)。

成都创新互联网络公司拥有10余年的成都网站开发建设经验,上1000家客户的共同信赖。提供网站建设、成都网站建设、网站开发、网站定制、外链、建网站、网站搭建、自适应网站建设、网页设计师打造企业风格,提供周到的售前咨询和贴心的售后服务

不过他说,***发现似乎这块功能不是他想象的那样,因此希望我可以看看到底是什么问题。由于当时没有扩展ASP.NET MVC的需求,后来我事情一多就忘了,现在先说声抱歉。最近开始对ASP.NET MVC动手动脚了,发现这样一个Area的功能非常有用,而且巧合的是,我也打算把Area和命名空间对应起来。

只是我选择的路和那位兄弟不一样,我打算自己写一个简单的ControllerFactory来替换掉默认的DefaultControllerFactory。这么做的主要原因是:我不知道DefaultControllerFactory已经提供对命名空间的支持了,微软默默地实现了却没有对外公开过,我也是后来阅读代码时才发现的。同时又意识到线程安全的问题,于是就还是打算自己写了。

好在ASP.NET MVC从设计之初就提供了扩展的能力,每个组件粒度都很小,大部分组件都是可以独立拔插的(Controller类除外,如果你使用自己的IController实现,就会发现大部分功能,如各Filter都失效了)。而要实现一个Controller Factory,只要实现一个简单的IControllerFactory就可以了(我喜欢接口):

 
 
 
  1. public interface IControllerFactory
  2. {
  3.     IController CreateController(RequestContext requestContext, string controllerName);
  4.     void ReleaseController(IController controller);
  5. }

于是构建一个AreaControllerFactory也大致只需要以下一些代码:

 
 
 
  1. public class AreaControllerFactory : IControllerFactory
  2. {
  3.     public IController CreateController(RequestContext requestContext, string controllerName)
  4.     {
  5.         ...
  6.     }
  7.     public void ReleaseController(IController controller)
  8.     {
  9.         IDisposable disposable = controller as IDisposable;
  10.         if (disposable != null)
  11.         {
  12.             disposable.Dispose();
  13.         }
  14.     }
  15. }

然后按照惯例,还是一步步谈起。首先是构造函数,我们的策略是根据不同的Area加载不同命名空间下的Controller类型。方便起见,我选择“基础命名空间”和“扩展部分”两块,它们从构造函数中传入:

 
 
 
  1. private Dictionary<stringstring> m_areaPartMapping = new Dictionary<stringstring>();
  2. public string NamespaceBase { getprivate set; }
  3. public AreaControllerFactory(string namespaceBase)
  4.     : this(namespaceBase, null)
  5. { }
  6. public AreaControllerFactory(string namespaceBase, IDictionary<stringstring> areaPartMapping)
  7. {
  8.     this.NamespaceBase = namespaceBase.EndsWith(".") ? namespaceBase : namespaceBase + ".";
  9.     if (areaPartMapping != null)
  10.     {
  11.         foreach (var pair in areaPartMapping)
  12.         {
  13.             this.m_areaPartMapping.Add(pair.Key.ToLowerInvariant(), pair.Value);
  14.         }
  15.     }
  16. }

于是我们就可以这样使用:

 
 
 
  1. var controllerFactory = new AreaControllerFactory("MyApp.Controllers");
  2. ControllerBuilder.Current.SetControllerFactory(controllerFactory);

如果在需要的时候,还可以指定Area与特定命名空间“部分”的映射关系。因此,我们需要从Area来获取这个“Part”:

 
 
 
  1. private string GetNamespacePart(string area)
  2. {
  3.     if (String.IsNullOrEmpty(area)) return "";
  4.     string part;
  5.     if (this.m_areaPartMapping.TryGetValue(area.ToLowerInvariant(), out part))
  6.     {
  7.         return part;
  8.     }
  9.     return area;
  10. }

这里我选择“配置”和“约定”相结合的方式。得到一个Area之后,我们会在映射表里进行查找Part,如果没有,则Area本身便是Part。根据Part和Controller名称,我们便可以获得Controller的类型:

 
 
 
  1. private ReaderWriterLockSlim m_rwLock = new ReaderWriterLockSlim();
  2. private Dictionary<string, Type> m_controllerTypes = new Dictionary<string, Type>();
  3. private Type GetControllerType(string area, string controllerName)
  4. {
  5.     string part = this.GetNamespacePart(area);
  6.     string typeName = String.IsNullOrEmpty(part) ?
  7.         this.NamespaceBase + controllerName.ToLowerInvariant() + "Controller" :
  8.         this.NamespaceBase + part + "." + controllerName.ToLowerInvariant() + "Controller";
  9.     Type type;
  10.     this.m_rwLock.EnterReadLock();
  11.     try
  12.     {
  13.         if (this.m_controllerTypes.TryGetValue(typeName, out type))
  14.         {
  15.             return type;
  16.         }
  17.     }
  18.     finally
  19.     {
  20.         this.m_rwLock.ExitReadLock();
  21.     }
  22.     type = Type.GetType(typeName, falsetrue);
  23.     if (type != null)
  24.     {
  25.         this.m_rwLock.EnterWriteLock();
  26.         try
  27.         {
  28.             this.m_controllerTypes[typeName] = type;
  29.         }
  30.         finally
  31.         {
  32.             this.m_rwLock.ExitWriteLock();
  33.         }
  34.     }
  35.     return type;
  36. }

由于我选择在应用程序中使用同一个AreaControllerFactory对象,因此线程安全是一定要有保证的。这里我们用到了读写锁,不过请注意,红色那句话并不保证对于每个相同的typeName只执行一次,也不保证相同的typeName对于m_controllerTypes字典只会进行一次写操作(所以我没有Add,而是使用了下标操作)。不过,由于这些“重复”不会造成问题,因此就没有去涉及太多这方面的考虑。

***,便是那CreateControlle方法:

 
 
 
  1. public IController CreateController(RequestContext requestContext, string controllerName)
  2. {
  3.     Type controllerType;
  4.     object area;
  5.     if (requestContext.RouteData.Values.TryGetValue("area", out area))
  6.     {
  7.         controllerType = this.GetControllerType(area.ToString(), controllerName);
  8.     }
  9.     else
  10.     {
  11.         controllerType = this.GetControllerType(null, controllerName);
  12.     }
  13.     if (controllerType == null)
  14.     {
  15.         throw new HttpException(404,
  16.             String.Format(
  17.                 "Controller of path {0} not found.",
  18.                 requestContext.HttpContext.Request.Path));
  19.     }
  20.     try
  21.     {
  22.         return (IController)Activator.CreateInstance(controllerType);
  23.     }
  24.     catch (Exception ex)
  25.     {
  26.         string message = String.Format("Error creating controller {0}" + controllerType);
  27.         throw new InvalidOperationException(message, ex);
  28.     }
  29. }

似乎没有什么可谈的:我们从RouteData中获取出area对应的值,并且调用GetControllerType方法获得Controller的类型,并使用Activator.CreateInstance创建对象。在不合法的情况下,抛出合适的异常即可。

至此,AreaControllerFactory就完成了,很容易,不是吗?很显然,这个组件的功能非常有限,例如为什么所有的Controller一定要在同一个命名空间下?没错,它其实只是符合“我要求”的一个东西。但是,在项目中很多东西都是如此,我只实现我够用的功能。例如,我可能不会向对外公开的API那样,严格检查每个问题,抛出严谨的异常。我可能倾向于在项目中使用接口,而不是使用抽象类。因为是我的项目,我可以快速反馈,需要修改的时候就修改吧。

同样的,如果DefaultControllerFactory真在某些特别情况下有问题,或者支持的有些复杂。那么不如我们就自己动手吧。一次性投入,而且这样的小组件也花不了多少时间。

本文来自老赵点滴:《支持Area的ControllerFactory》

本文名称:自定义的ControllerFactory:接口实现,支持Area
当前路径:http://www.hantingmc.com/qtweb/news20/525720.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联