ASP.NET MVC是一种非常反人类的设计。(我没有接触过Java的MVC,不知道两者是否一样。如果一样,那么搞Java的同学也挺可怜。)尤其是MVC的路由机制,灰常灰常反动。路由所带来的“美观的”URL,通过合理的文件层次布局+URL重写机制同样可以解决。但显然文件目录结构的方式,更直观明了,贴近人们的自然思路。可惜不管我们如何吐槽,萨蒂亚?纳德拉估计是不会听的。
MVC的默认组织机构是扁平的。所有的Controller都是平级的。在大型项目中,这完全是一个灾难。当需要上百个甚至数百个Controller,或是为了让代码“自说明”时(指合理的给方法、文件命名,使阅读者在没有注释的时候也能直接读懂开发者的意图),很多Controller需要同名时,尤其让人崩溃。路是死的,人是活的。为了解决这一问题,聪明的程序猿想出了很多办法。比如利用Area机制、重写视图匹配机制、重写MVC框架等等。我们的OA系统中也有近百个Controller,不提前规划好路由,后续的工作我们就无法展开。所以,在这一节,我们就来聊聊其中两种不对MVC做大手术的方式。

(仅仅一个小功能模块就需要10多个Controller,在大型项目中,甚至需要数百个Controller,对项目管理而言,如不进行合理划分、管理,而按照mvc默认的平级存放,无疑会带来灾难性的后果。注:本图已对功能模块进行按目录划分)
该方式的核心要点是:根据功能划分,对Controller和View进行多级目录处理(如上图)。然后通过路由优先级和重载MVC自带的视图匹配逻辑的方式,达到精确控制URL与Controller、Views进行匹配。进行这是最接近自然思维的处理方式,合理而精细的安排路由优先级的情况下,可以做到非常深的目录层次。在我们的示例项目中,也将采用这种办法。
1 namespace EasyFast.Web
2 {
3 public class RouteConfig
4 {
5 public static void RegisterRoutes(RouteCollection routes)
6 {
7 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
8
9 //ASP.NET Web API Route Config
10 routes.MapHttpRoute(
11 name: "DefaultApi",
12 routeTemplate: "api/{controller}/{id}",
13 defaults: new { id = RouteParameter.Optional }
14 );
15
16 routes.MapRoute(
17 name: "Default",
18 url: "{controller}/{action}/{id}",
19 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
20 namespaces: new string[] { "EasyFast.Web.Controllers" }
21 );
22 }
23 }
24 }
1 namespace EasyFast.Web.Areas.Admin
2 {
3 public class AdminAreaRegistration : AreaRegistration
4 {
5 public override string AreaName
6 {
7 get
8 {
9 return "Admin";
10 }
11 }
12
13 public override void RegisterArea(AreaRegistrationContext context)
14 {
15 context.MapRoute(
16 "Admin_Config",
17 "Admin/Config/{controller}/{action}/{id}",
18 new { action = "Index", id = UrlParameter.Optional }
19 );
20 context.MapRoute(
21 "Admin_default",
22 "Admin/{controller}/{action}/{id}",
23 new { action = "Index", id = UrlParameter.Optional },
24 new string[] { "EasyFast.Web.Areas.Admin.Controllers" }
25 );
26 }
27 }
28 }
如上代码所示。我们给 App_Start->RouteConfig.cs 文件增加了namespace参数,并指定了完整的命名空间 “EasyFast.Web.Controllers” 。在一个MVC应用中,指定了该命名空间后,就可以解决Areas和站点根目录中有重名的Controller问题。如站点首页是HomeController,后台首页也是HomeController此时MVC会检测到两个重名的控制器,系统会要求给其中一个控制器指定完整命名空间。我们的一般做法是给App_Start文件中的路由配置文件指定命名空间。否则我们就必须为每个Areas指定命名空间(其他Areas里也可能有叫HomeController的控制器)。当然了,您也可以给App_Start和所有的Areas中的路由都指定命名空间,但是,只指定App_Start->RouteConfig.cs无疑是一种简洁的做法。
细心的朋友可能会看到:我们给App_Start->RouteConfig.cs和Areas.Admin.AdminAreaRegistration.cs都指定了命名空间。这是因为在我们的Admin区域下。也会有多个重名的控制器(例如HomeController)。同时,要注意:有目录划分的路由要写在默认路由的前面,且不可和默认路由重名。

(同一个Areas里出现两个同名的HomeController控制器。系统设置这个模块的首页和后台首页控制器重名。只要涉及到控制器重名,就必须给其中一个指定完整命名空间)
1 namespace EasyFast.Web.Extend
2 {
3 public class EasyFastViewEngine : RazorViewEngine
4 {
5 public EasyFastViewEngine()
6 {
7 ViewLocationFormats = new[]
8 {
9 "~/Views/{1}/{0}.cshtml",
10 "~/Views/Shared/{0}.cshtml",
11 "~/Views/Areas/{1}/{0}.cshtml"//我们的规则
12 };
13 AreaViewLocationFormats = new[]
14 {
15 "~/Areas/{2}/Views/{1}/{0}.cshtml",
16 "~/Areas/{2}/Views/Shared/{0}.cshtml",
17
18 "~/Areas/{2}/Views/Config/{1}/{0}.cshtml",
19 "~/Areas/{2}/Views/User/{1}/{0}.cshtml",
20 "~/Areas/{2}/Views/Case/{1}/{0}.cshtml",
21
22 "~/Areas/{2}/Views/Config/Shared/{0}.cshtml",
23 "~/Areas/{2}/Views/User/Shared/{0}.cshtml",
24 "~/Areas/{2}/Views/Case/Shared/{0}.cshtml"
25 };
26 }
27
28 public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
29 {
30 return base.FindView(controllerContext, viewName, masterName, useCache);
31 }
32
33 public static void Config()
34 {
35 ViewEngines.Engines.Clear();
36 ViewEngines.Engines.Add(new EasyFastViewEngine());
37 }
38 }
39 }
1 namespace EasyFast.Web
2 {
3 public class MvcApplication : AbpWebApplication<EasyFastWebModule>
4 {
5 protected override void Application_Start(object sender, EventArgs e)
6 {
7 AbpBootstrapper.IocManager.IocContainer.AddFacility<LoggingFacility>(
8 f => f.UseAbpLog4Net().WithConfig("log4net.config")
9 );
10 EasyFastViewEngine.Config();//注册多级目录扩展
11 base.Application_Start(sender, e);
12 }
13 }
14 }
如上代码。我们在web根目录下建立了Extend文件夹,并在其中新建了EasyFastViewEngine.cs文件。然后再文件内部,重载了MVC自带的视图查找逻辑。代码中的{2}、{1}、{0}是占位符,分别代表了Area、Controller、Action。这样一来,程序在实行时就会按照我们制定的查找顺序去指定的目录匹配View文件。通过合理的路由安排,这样做可以做到很深的目录层次结构。相信聪明的读者一定想到了如何扩展出三级、四级甚至更多级的目录。
如此做法,本身也存在一定的不便。在具体Action上右键选择转到视图,或是在具体视图右键选择控制器时,VS会提示找不到对应的控制器或是视图。但这不是MVC的问题,而是VS自身的问题,VS在设计时应该没考虑过多级目录问题。同时,在Ctrl+F5运行程序时,浏览器会直接按照文件路径加载对应的cshtml文件,并报404错误。此时,需要我们手动修改URL路径,程序就能正常显示了。


(在具体View页面直接编译运行程序时,会显示404错误,此时手动修改URL地址即可解决)
该方式不用对MVC做任何定制修改。只需要在项目架构里将前台、后台、用户中心等划分成多个web项目即可。然后在每个web项目里,根据功能群的划分,再独立建立Areas。如此做法,有三个直观的好处同样也有三个直观的缺陷。因为好处和缺陷都是同样显而易见,我们就不一一详细介绍了,只列出总结的提醒,供大家选择判断。
1 public long Add(UserTypeInput model)
2 {
3 long _result = 0;
4 var data = Mapper.Map<UserTypeInput, Core.Entities.UserType>(model);
5 lock (lockHelper)
6 {
7 if (!CheckName(model))
8 {
9 _result = _userTypeRepository.InsertAndGetIdAsync(data).Id;
10
11 }
12 }
13 return _result;
14 }
考虑如上代码。假设前后台都调用Add方法时,lock控制段显然会失效。这样一来,新增数据时,重名检测方法会失效,有可能会造成前后台分别录入了一条重名的人员类别。


(前后台分属不同的项目,利用Area实现系统设置、案件管理、用户管理等功能群,此做法只能实现三级目录,功能群模块(具体Area)/功能组(Controller)/具体功能(Action))
原文:http://www.cnblogs.com/Leo_wl/p/6024678.html