2

好的,我有这个有趣的 ASP.NET MVC 4 解决方案/项目结构,它创建可插入的应用程序模块。我按照这种技术创建了它:

http://geekswithblogs.net/cokobware/archive/2013/01/15/asp.net-mvc-4-pluggable-application-modules.aspx

结果,我在项目中有一个带有空区域文件夹的主应用程序。我还有一个插件项目,它位于磁盘上主应用程序的区域文件夹中,它还将其构建输出文件夹设置为主应用程序\bin文件夹。

在我的可插拔模块应用程序中,我决定在其中创建一个区域部分,并创建了一个名为 Test 的区域。默认情况下,ASP.NET MVC 4 视图引擎不支持将其作为可插入模块,因为它会尝试在不正确的位置查找视图。

所以从概念上讲,我们有:

Main             <- Main application folder
   Areas         <- Main application folder
      Plugin     <- Plugin module application folder
         Areas   <- Plugin module application folder
            Test <- Plugin module application folder

为了解决这个问题,我创建了一种方法来解释AreaName自定义RazorViewEngine类中的属性,以重写视图引擎正在寻找的 URL,以在这些可插入模块区域中查找视图文件。

首先,我使用以下约定为属于我的可插拔模块的测试区域定义我的区域注册类:

Namespace Areas.Plugin
    Public Class PluginAreaRegistration
        Inherits AreaRegistration

        Public Overrides ReadOnly Property AreaName() As String
            Get
                Return "Plugin.Test"
            End Get
        End Property

        Public Overrides Sub RegisterArea(ByVal context As System.Web.Mvc.AreaRegistrationContext)
            context.MapRoute( _
                "Plugin_default", _
               "Plugin/Test/{controller}/{action}/{id}", _
                New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional},
                {"Plugin.Test.Controllers"}
            )
        End Sub
    End Class
End Namespace

然后我继承RazorViewEngine并覆盖了一些方法来解析和生成可插入模块的区域文件夹中的视图路径:

Public Class MyExtendedRazorViewEngine
Inherits RazorViewEngine

' set the location format strings
Public Sub New()
    MyBase.PartialViewLocationFormats = _
        {
        "~/Views/{1}/{0}.cshtml",
        "~/Views/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/{1}/{0}.vbhtml",
        "~/Views/Shared/{0}.cshtml",
        "~/Views/Shared/{0}.vbhtml",
        "~/Areas/{3}/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{3}/Areas/{2}/Views/{1}/{0}.vbhtml",
        "~/Areas/{1}/Views/Shared/{0}.cshtml",
        "~/Areas/{1}/Views/Shared/{0}.vbhtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.cshtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.vbhtml"
        }

    MyBase.AreaViewLocationFormats = {
        "~/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/Shared/{0}.vbhtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.cshtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.vbhtml"
    }

    MyBase.AreaMasterLocationFormats = {
        "~/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/Shared/{0}.vbhtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.cshtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.vbhtml"
    }

    MyBase.AreaPartialViewLocationFormats = {
        "~/Areas/{2}/Views/{1}/{0}.cshtml",
        "~/Areas/{2}/Views/{1}/{0}.vbhtml",
        "~/Areas/{2}/Views/Shared/{0}.cshtml",
        "~/Areas/{2}/Views/Shared/{0}.vbhtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.cshtml",
        "~/Areas/{2}/Areas/{1}/Views/{0}.vbhtml"
    }

    MyBase.ViewLocationFormats = {
        "~/Views/{1}/{0}.cshtml",
        "~/Views/{1}/{0}.vbhtml",
        "~/Views/Shared/{0}.cshtml",
        "~/Views/Shared/{0}.vbhtml"
    }

    MyBase.MasterLocationFormats = {
        "~/Views/{1}/{0}.cshtml",
        "~/Views/{1}/{0}.vbhtml",
        "~/Views/Shared/{0}.cshtml",
        "~/Views/Shared/{0}.vbhtml"
    }

    MyBase.PartialViewLocationFormats = {
        "~/Views/{1}/{0}.cshtml",
        "~/Views/{1}/{0}.vbhtml",
        "~/Views/Shared/{0}.cshtml",
        "~/Views/Shared/{0}.vbhtml"
    }

End Sub

Protected Overrides Function CreatePartialView(controllerContext As ControllerContext, partialPath As String) As IView

    Dim area As String = controllerContext.RouteData.DataTokens.Item("Area")
    Dim areaname As String()
    Dim pp As String = partialPath

    If Not area Is Nothing Then
        areaname = area.Split(".")
        If areaname.Length > 1 Then
            pp = pp.Replace(area, areaname(0) & "/Areas/" & areaname(1))
        End If
    End If

    Return MyBase.CreatePartialView(controllerContext, pp)
End Function

Protected Overrides Function CreateView(controllerContext As ControllerContext, viewPath As String, masterPath As String) As IView

    Dim area As String = controllerContext.RouteData.DataTokens.Item("Area")
    Dim areaname As String()
    Dim vp As String = viewPath
    Dim mp As String = masterPath
    If Not area Is Nothing Then
        areaname = area.Split(".")
        If areaname.Length > 1 Then
            vp = vp.Replace(area, areaname(0) & "/Areas/" & areaname(1))
            mp = mp.Replace(area, areaname(0) & "/Areas/" & areaname(1))
        End If
    End If

    Return MyBase.CreateView(controllerContext, vp, mp)
End Function

Protected Overrides Function FileExists(controllerContext As ControllerContext, virtualPath As String) As Boolean

    Dim area As String = controllerContext.RouteData.DataTokens.Item("Area")
    Dim areaname As String()
    Dim vp As String = virtualPath
    If Not area Is Nothing Then
        areaname = area.Split(".")
        If areaname.Length > 1 Then
            vp = vp.Replace(area, areaname(0) & "/Areas/" & areaname(1))
        End If
    End If
    Return MyBase.FileExists(controllerContext, vp)
End Function
End Class

我已经修改了主应用程序Global.asax文件以获取新的视图引擎:

Imports System.Web.Http
Imports System.Web.Optimization

Public Class MvcApplication
    Inherits System.Web.HttpApplication

    Sub Application_Start()
        AreaRegistration.RegisterAllAreas()

        WebApiConfig.Register(GlobalConfiguration.Configuration)
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters)
        RouteConfig.RegisterRoutes(RouteTable.Routes)
        BundleConfig.RegisterBundles(BundleTable.Bundles)

        ViewEngines.Engines.Clear()
        ViewEngines.Engines.Add(New MyExtendedRazorViewEngine())
    End Sub
End Class

启动浏览器并为我的主应用程序调用 Home 控制器后,我看到了正确的页面和布局渲染。当我转到我的插件模块的索引的 Home 控制器操作时,索引视图再次正确呈现,_Layout.vbhtml并从主应用程序中获取。

但是,当我为插件测试区的索引视图调用 Home 控制器操作时,我只能看到索引页面视图呈现,但_Layout.vbhtml主应用程序中没有包含主控。

为了让插件可插入模块下方的区域视图呈现主应用程序的主布局模板,我缺少什么?

4

0 回答 0