【问题标题】:Handling Layout properties with custom Razor view engine使用自定义 Razor 视图引擎处理布局属性
【发布时间】:2013-05-05 16:58:48
【问题描述】:

我已经实现了一个类似于此处描述的多租户视图引擎:

这让我可以像这样覆盖视图的搜索位置:

    MasterLocationFormats = new[]
    {
        "~/Views/%1/{1}/{0}.cshtml",
        "~/Views/%1/Shared/{0}.cshtml",
        "~/Views/Default/{1}/{0}.cshtml",
        "~/Views/Default/Shared/{0}.cshtml",
    };

其中%1 被替换为活动租户的正确文件夹。这工作得很好,例外一个问题。当我像这样在视图上定义布局路径时:

Layout = "~/Views/Default/Shared/_MyLyout.cshtml";

这有点违背了多租户的目的,因为我必须硬编码布局页面的确切位置。我希望能够做这样的事情:

Layout = "~/Views/%1/Shared/_MyLyout.cshtml";

如果我想让租户拥有他们的一个布局页面,我将如何支持这一点?

我已经尝试摆弄我覆盖的视图引擎方法:

  • 创建部分视图
  • 创建视图
  • 文件存在

但似乎没有任何迹象表明能够动态指定布局页面。

更新:

这是我到目前为止所做的工作。我使用这个问题的答案https://stackoverflow.com/a/9288455/292578 稍作修改创建了一个 HTML 助手:

public static string GetLayoutPageForTenant( this HtmlHelper html, string LayoutPageName )
{
    var layoutLocationFormats = new[]
    {
        "~/Views/{2}/{1}/{0}.cshtml",
        "~/Views/{2}/Shared/{0}.cshtml",
        "~/Views/Default/{1}/{0}.cshtml",
        "~/Views/Default/Shared/{0}.cshtml",
    };

    var controller = html.ViewContext.Controller as MultiTenantController;
    if( controller != null )
    {
        var tenantName = controller.GetTenantSchema();
        var controllerName = html.ViewContext.RouteData.Values["Controller"].ToString();

        foreach( var item in layoutLocationFormats )
        {
            var resolveLayoutUrl = string.Format( item, LayoutPageName, controllerName, tenantName );
            var fullLayoutPath = HostingEnvironment.IsHosted ? HostingEnvironment.MapPath( resolveLayoutUrl ) : System.IO.Path.GetFullPath( resolveLayoutUrl );
            if( File.Exists( fullLayoutPath ) ) return resolveLayoutUrl;
        }
    }

    throw new Exception( "Page not found." );
}

这类似于 saravanan 的建议。然后我可以使用以下代码在我的视图中设置布局:

Layout = Html.GetLayoutPageForTenant( "_Home" );

不幸的是,这重复了自定义视图引擎正在执行的工作,这似乎是错误的方法。

【问题讨论】:

    标签: c# asp.net-mvc razor multi-tenant viewengine


    【解决方案1】:

    我想提出以下想法,

    在我们设置布局页面的_ViewStart.cshtml 文件中,您可以使用类似这样的内容,即通过从数据库中获取基于租户的布局 url 或文件夹名称填充到控制器中。

    @{
        Layout = ViewBag.TenantLayoutPageUrl;
     }
    

     @{
        Layout = string.Format("~/Views/{0}/Shared/_MyLyout.cshtml",ViewBag.TenantId);
     }
    

    如果您有一些静态租户数据表示,例如将跟踪租户自定义的静态 Identity 类,我们可以使用它并最大限度地减少到数据库的往返行程。

    请分享您对此实施的想法,以便对社区有用

    【讨论】:

    • 这种方法的唯一问题是不是每个租户都有自己的自定义布局页面。如果未找到自定义布局页面,我仍然希望搜索默认为 Default 文件夹。
    • @Sparafusile:在这种情况下,我们可以在 ViewBag 中有一个备用 id。所以在Contoller中,我们会检查租户是否有自定义布局页面,如果有,我们将在视图包中设置该文件夹,否则我们将设置租户的文件夹名称。 ViewBag.TenantFolderName="defaultPath"; if(tenant Has customFolder){ ViewBag.TenantFolderName=tenantFolderName; }恕我直言,这将是故障安全的。
    • 虽然我同意这可行,但它有点违背了自定义视图引擎在控制器中完成所有工作的目的。我会继续寻找更优雅的解决方案。
    • 当然,我们将等待更好的解决方案。我也会从中吸取教训。
    • @Sparafusile:也感谢您分享您的想法。如果您愿意分享您实现的逻辑,请与我们分享。
    【解决方案2】:

    试试,

    public class CustomWebViewPage : WebViewPage
    {
        public override void ExecutePageHierarchy()
        {
            if (Context.Items["__MainView"] == null)
            {
                this.Layout = String.Format("~/Views/Shared/{0}/_Layout.cshtml", ViewContext.Controller.GetType().Namespace);
                Context.Items["__MainView"] = "Not Null";
            }
            base.ExecutePageHierarchy();
        }
    
        public override void Execute()
        {
        }
    }
    
    public class CustomWebViewPage<T> : WebViewPage<T>
    {
        public override void ExecutePageHierarchy()
        {
            if (Context.Items["__MainView"] == null)
            {
                this.Layout = String.Format("~/Views/Shared/{0}/_Layout.cshtml", ViewContext.Controller.GetType().Namespace);
                Context.Items["__MainView"] = "Not Null";
            }
            base.ExecutePageHierarchy();
        }
    
        public override void Execute()
        {
        }
    }
    
    <system.web.webPages.razor>
      <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <pages pageBaseType="Mv4App.CustomWebViewPage">
    

    【讨论】:

      【解决方案3】:

      您可以在租户视图文件夹 (~/Views/%1/_ViewStart.cshtml) 中添加以下 _ViewStart.cshtml。每个租户都可以管理自己的布局文件。

      @{
          Layout =  VirtualPathUtility.GetDirectory(PageContext.Page.VirtualPath) + "Shared/_Layout.cshtml";
      }
      

      【讨论】:

      • 这是一个有趣的想法,但我希望能够使用带有默认视图的自定义布局和/或使用带有自定义视图的默认布局。除非我误解了你的回答,否则我不能用你的代码做到这一点。
      • @Sparafusile 你可以这样做,你可以把_ViewStart.cshtml放在你需要自定义的任何文件夹中,你甚至可以把它放在控制器文件夹或默认文件夹中。默认情况下,Razor Engine 搜索起始页的控制器文件夹 (_ViewStart.cshtml),如果在应用程序根目录之前找不到尝试父文件夹等等。
      猜你喜欢
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      • 2015-02-12
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多