Обработка свойств макета с помощью настраиваемого механизма просмотра Razor

Я реализовал механизм просмотра с несколькими арендаторами, подобный тому, что описано здесь:

Позвольте мне переопределить места поиска для просмотра следующим образом:

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

В котором %1 заменяется правильной папкой для активного арендатора. Это работает только прекрасное исключение одной проблемы. Когда я определяю путь Layout на моем представлении следующим образом:

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

Это как бы побеждает цель иметь многопользовательскую собственность, так как мне приходится жестко кодировать точное местоположение страницы макета. Я хочу иметь возможность сделать что-то вроде этого:

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

Если бы я хотел разрешить арендаторам иметь одну страницу макета, как бы я мог поддержать это?

Я пробовал использовать методы просмотра, которые я переделывал:

  • CreatePartialView
  • CreateView
  • FileExists

Но ничто, кажется, не указывает на возможность динамически указывать страницу макета.

Update:

Вот что я до сих пор работаю. Я использовал ответ на этот вопрос qaru.site/info/444644/..., слегка модифицированный, чтобы создать помощник 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." );
}

который похож на предложенный саравананом. Затем я могу установить макет на мой взгляд с помощью этого кода:

Layout = Html.GetLayoutPageForTenant( "_Home" );

К сожалению, это дублирует работу, которую делает пользовательский движок просмотра, который кажется неправильным способом.

Ответ 1

Я хотел бы предложить следующую идею:

В файле _ViewStart.cshtml, где мы устанавливаем страницы макета, вы можете использовать что-то вроде этого, с идеей URL-адреса макета на основе Tenant или имени папки заполняется в контроллере, извлекая из базы данных.

@{
    Layout = ViewBag.TenantLayoutPageUrl;
 }

или

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

Если у вас есть статические представления данных Tenant, например статический класс Identity, который будет отслеживать настройку вашего арендатора, мы можем использовать это и свести к минимуму округление до db.

Поделитесь своей идеей с этой реализацией, чтобы она была полезной для сообщества.

Ответ 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

Вы можете добавить следующее _ViewStart.cshtml в папку просмотра арендаторов (~/Views/%1/_ViewStart.cshtml). Каждый арендатор может управлять своими файлами макетов.

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