• 在asp.net mvc2中添加Area及相关的路由定义


           在一个较复杂的Mvc项目中,我们可以利用Area对模块进行划分。在MVC2中,添加Area后,经常出现找不到视图的情况,此时,我们必须自行定义一个视图引擎,以告知相关的View的路径,形成正确的ViewEngineResult返回。具体步骤如下:

    一、定义一个视图引擎,继承自WebFormViewEngine

        1.1 在其构造函数中指定ViewLocationFormats与MasterLocationFormats,将Area中对应的area参数加到寻找View的路径中

        1.2 override基类型WebFormViewEngine中的两个方法:FindView与FindPartialView,对出现area的路由,指明正确寻找View的方法,以便返回正确的ViewEngineResult结果。

        1.3 示例代码如下:

    public class MyViewEngine:WebFormViewEngine

        {

            public MyViewEngine()

                : base()

            {

                // 视图位置匹配规则设置  

                ViewLocationFormats = new string[]  

               {  

                   "~/{0}.aspx"

                   "~/{0}.ascx"

                   "~/Views/{1}/{0}.aspx"

                   "~/Views/{1}/{0}.ascx"

                   "~/Views/Shared/{0}.aspx"

                   "~/Views/Shared/{0}.ascx",   

               };

                // 母版页匹配规则设置  

                MasterLocationFormats = new string[]  

               {  

                   "~/{0}.Master"

                   "~/Shared/{0}.Master"

                   "~/Views/{1}/{0}.Master"

                   "~/Views/Shared/{0}.Master"

               };  

            }

            ///<summary>

            ///重写视图推送的方法

            ///</summary>

            ///<param name="controllerContext"></param>

            ///<param name="viewName"></param>

            ///<param name="masterName"></param>

            ///<param name="useCache"></param>

            ///<returns></returns>

            public overrideViewEngineResult FindView(ControllerContext controllerContext,string viewName, string masterName,bool useCache)

            {

                ViewEngineResult areaResult = null;

                if (controllerContext.RouteData.Values.ContainsKey("area")) {

                    //如果存在area

                    //将原来寻找view的路径“~/Views/controller/viewName”改为"Areas/area/Views/controller/viewName"

                    string areaViewName = FormatViewName(controllerContext, viewName);

                    //根据view生成一个ViewEngineResult对象

                    areaResult = base.FindView(controllerContext, areaViewName, masterName, useCache);

                    if (areaResult != null && areaResult.View != null) {

                        return areaResult;

                    }

                    //没有找到对应的view,则到share下去找是否有共用的view

                    //将原来的从shared下找view的路径"~/Views/Shared/viewName"改为"Areas/area/Views/Shared/viewName"

                    string sharedAreaViewName = FormatSharedViewName(controllerContext, viewName);

                    //根据shared下的view重新生成ViewEngineResult,返回

                    areaResult = base.FindView(controllerContext, sharedAreaViewName, masterName, useCache);

                    if (areaResult != null && areaResult.View != null) {

                        return areaResult;

                    }

                }

                //没有找到相关结果,返回默认的

                return base.FindView(controllerContext, viewName, masterName, useCache);

            }

            ///<summary>

            ///重写PartialView推送的方法

            ///</summary>

            ///<param name="controllerContext"></param>

            ///<param name="partialViewName"></param>

            ///<param name="useCache"></param>

            ///<returns></returns>

            public overrideViewEngineResult FindPartialView(ControllerContext controllerContext,string partialViewName, bool useCache)

            {

                ViewEngineResult areaResult = null;

                if (controllerContext.RouteData.Values.ContainsKey("area")) {

                    string areaPartialName = FormatViewName(controllerContext, partialViewName);

                    areaResult = base.FindPartialView(controllerContext, areaPartialName, useCache);

                    if (areaResult != null && areaResult.View != null) {

                        return areaResult;

                    }

                    string sharedAreaPartialName = FormatSharedViewName(controllerContext, partialViewName);

                    areaResult = base.FindPartialView(controllerContext, sharedAreaPartialName, useCache);

                    if (areaResult != null && areaResult.View != null) {

                        return areaResult;

                    }

                }

                return base.FindPartialView(controllerContext, partialViewName, useCache);

            }

            private staticstring FormatViewName(ControllerContext controllerContext,string viewName)

            {

                string controllerName = controllerContext.RouteData.GetRequiredString("controller");

                string area = controllerContext.RouteData.Values["area"].ToString();

                return string.Format("Areas/{0}/Views/{1}/{2}", area, controllerName, viewName);

            }

            private staticstring FormatSharedViewName(ControllerContext controllerContext,string viewName)

            {

                string area = controllerContext.RouteData.Values["area"].ToString();

                return string.Format("Areas/{0}/Views/Shared/{1}", area, viewName);

            }

     }

    二、在Global.asax中指定自定义视图引擎,并注册Area路由

        2.1 清空默认的视图引擎,并指定新的视图引擎为自定义的视图引擎

        2.2 注册相关的Area路由

        2.3 示例代码:   

    public class MvcApplication : System.Web.HttpApplication

        {

            public staticvoid RegisterRoutes(RouteCollection routes)

            {

                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

                routes.MapRoute(

                    "Default", //默认的路由

                    "Yang/{controller}/{action}/{id}",

                    new { area="Yang",controller ="Root", action = "Index", id =UrlParameter.Optional },

                    new string[] {"MvcWebForTest2.Areas.Yang.Controllers" }

                );

      

                routes.MapRoute(

                    "Root", // 默认首页指定到一个Area下的HomeControllerIndex

                     "",      // Url,即网站名的url

                     new { area="Xiao",controller ="Home", action = "Index", id =UrlParameter.Optional },

                     new string[] { "MvcWebForTest2.Areas.Xiao.Controllers" }

                );

            }

            protected void Application_Start()

            {

                //清除原来所有的视图引擎

                ViewEngines.Engines.Clear();

                //加上自定义的视图引擎

                ViewEngines.Engines.Add(newMyViewEngine());

                //注册所有的Area

                AreaRegistration.RegisterAllAreas();

                //注册路由表

                RegisterRoutes(RouteTable.Routes);

            }

        }

     2.4 补充说明

            在MVC2中,当在一个项目中添加一个Area后,VS会自动为这个Area添加一个继承自AreaRegistration的类型,这个类型的作用是,当2.3中的AreaRegistration.RegisterAllAreas()方法执行时,项目下所有继承自AreaRegistration的子类的对应的命名空间都会注册到路由表中去。AreaRegistration子类型的示例如下(VS自动生成):

    using System.Web.Mvc;

    namespace MvcWebForTest2.Areas.Yang

    {

        public classYangAreaRegistration:AreaRegistration

        {

            public overridestring AreaName

            {

                get

                {

                    return "Yang";

                }

            }

            public overridevoid RegisterArea(AreaRegistrationContext context)

            {

                context.MapRoute(

                    "Yang_default",

                    "Yang/{controller}/{action}/{id}",

                    new { action = "Index", id = "" }

                );

            }

        }

    }

    通过对System.Web.Mvc的源码进行分析可以看出RegisterAllAreas()方法的执行是非常有趣的。

    a、首先读取缓存中是否保存了相关的AreaRegistration子类型对应的Area的命名空间记录(注:缓存只在.net FrameWork4.0中才会启用)

    b、如果缓存中没有找到,它通过反射,获取所有的Type相符(属于AreaRegistration子类)的子类型的命名空间(在.net FrameWork4.0中它会把找到的类型的命名空间序列化后写入缓存的"MVC-AreaRegistrationTypeCache.xml"的中),以下节选自System.Web.Mvc项目源码中的表述

    // Go through all assemblies referenced by the application and search for types matching a predicate

    上文中是否相符的type的判断条件

    type != null && type.IsPublic && type.IsClass && !type.IsAbstract

    且typeof(AreaRegistration).IsAssignableFrom(type) &&type.GetConstructor(Type.EmptyTypes) != null;

    c、利用AreaRegistrationContext对象,对所有符合条件的命名空间进行注册

            internal void CreateContextAndRegister(RouteCollection routes,object state) {

                AreaRegistrationContext context =new AreaRegistrationContext(AreaName, routes, state);

                string thisNamespace = GetType().Namespace;

                if (thisNamespace != null) {

                    context.Namespaces.Add(thisNamespace + ".*");

                }

                RegisterArea(context);

            }

    (注:如果在global.ascx中没有执行RegisterAllAreas()或者在对应的area下没有添加继承自AreaRegistration的子类,在对应的Html方法输出Html.ActionLink时,即使指定了routeDictionary中的area参数,也会出现链接出错的情况,此时就需要自行在application_start中对这个area进行注册了,此部分详见第四大点)

    三、在View中使用Html.ActionLink在不同Area之间进行跳转     

    <%=Html.ActionLink("链接到xiao","Me""Home",new { area="Xiao" },null)%>

    四、在global.ascx的Application_Start方法中自行注册Area的方式:

             在项目中,若不喜欢在每个area的根文件夹下都生成一个继续自AreaRegistration子类的cs文件,或者在Application_Start()方法中没有调用AreaRegistration.RegisterAllAreas()方法,或者希望整个项目下的路由路径(URL)可以统一集中到Global中管理,我们也可以自行利用MapRoute或自定义扩展方法来注册area的命名空间。但不推荐此种做法。下面给出一个RouteCollection类型扩展CreateArea的方法的示例,以便在RegisterRoutes中一次性注册某个Area下的所有Controller,扩展方法CreateArea的示例代码如下:           

    namespace System.Web.Routing

    {

        //请注册扩展方法的命名空间

        public staticclass RoutingExtension

        {

            public staticvoid CreateArea(thisRouteCollection routeCollection, string area, string controllerNameSpace, params Route[] routeEntities)

            {

                if (routeEntities == null || routeEntities.Length <= 0) {

                    return;

                }

                foreach (Route routein routeEntities) {

                    if (route.Constraints == null) {

                        route.Constraints = new RouteValueDictionary();

                    }

                    if (route.Defaults == null) {

                        route.Defaults = new RouteValueDictionary();

                    }

                    if (route.DataTokens == null) {

                        route.DataTokens = new RouteValueDictionary();

                    }

                    route.Constraints.Add("area", area);

                    //将一个area下的controllers命名空间加入

                    route.DataTokens.Add("namespaces",new[] { controllerNameSpace });

                    route.Defaults.Add("area", area);

                    if (!routeCollection.Contains(route)) {

                        routeCollection.Add(route);

                    }

                }

            }

        }

    }

    然后在Global的RegisterRoutes方法中如下图所示调用CreateArea扩展方法注册某个area下的所有路由

    public static void RegisterRoutes(RouteCollection routes)

            {

                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

                // Area Yang下的控制器组注册

                routes.CreateArea("Yang",

                                          "MvcWebForTest2.Areas.Yang.Controllers",

                                          new Route[]{

                                            routes.MapRoute("yangRt1","Yang/{controller}/{action}", new { controller = "Home", action = "Index" }),

                                            routes.MapRoute("yangRt2","Yang/myurl/{action}",new {controller="Home",action="Index"})

                                          });

                //默认路由及首页设置,定位到指定Area

                routes.CreateArea("Xiao",

                                           "MvcWebForTest2.Areas.Xiao.Controllers",

                                            new Route[]{

                                              routes.MapRoute("Default","Xiao/{controller}/{action}", new { controller = "Home", action = "Index" }),

                                              routes.MapRoute("Root","", new { controller ="Home", action = "Index" })

                                         }); 

                }

        这样的话,即使在每个area下没有继承自AreaRegistration的子类,或者没有调用RegisterAllAreas()方法,都可以正常地路由所有area下的页面。

    示例下载

  • 相关阅读:
    python中创建实例属性
    Python中if __name__ == "__main__": 的理解
    模块
    python函数式编程
    python-复杂生成式
    生成器的测试
    mysql乱码配置
    javascript
    Sql Server 2008 R2 下载地址
    Microsoft Data Access Components 2.8
  • 原文地址:https://www.cnblogs.com/rosiu/p/3077918.html
Copyright © 2020-2023  润新知