2016-06-23 13 views
0

Я пытаюсь использовать пользовательские (производные) RazorViewEngine AND предварительно скомпилированные представления с использованием RazorGenerator.Использование настраиваемых представлений RazorViewEngine AND RazorGenerator

Некоторый контекст:

У нас есть базовый продукт, который мы используем для различных реализаций клиента. При этом у нас есть основной набор базовых представлений. Большинство просмотров работает большую часть времени. Сейчас мы в конечном итоге копируем существующие представления для каждого нового решения и при необходимости модифицируем. Это заканчивается тем, что 95% просмотров совпадают между клиентами и 5% изменены.

Я хочу сделать базовый набор представлений, скомпилировать их в DLL и повторно использовать их через клиентов. Пока что я хорошо работаю, используя RazorGenerator.

Теперь следующим шагом является возможность настройки (переопределения) видов. Однако есть предостережение. Наше приложение имеет два «режима», в которых находится пользователь. В режиме, в котором они находятся, может потребоваться другое представление.

Я создал производный класс из RazorGeneratorView. Это представление в основном проверяет «OrderingMode» из объекта UserProfile, который разрешает Autofac. На основе режима - локатор пути заменяется на разрешение представления.

Идея создания отдельных клиентских приложений будет пытаться разрешить представление сначала в традиционной папке Views. Только я добавляю в подкаталог Views/{OrderingMode}/{Controller}/{View} .cshtml.

Если вид не найден - тогда он будет выглядеть в скомпилированной библиотеке (основные виды).

Это позволяет мне переопределять отдельные виды/частичные части, как это необходимо для клиентов.

public PosViewEngine() : base() 
    { 
     //{0} = View Name 
     //{1} = ControllerName 
     //{2} = Area Name 
     AreaViewLocationFormats = new[] 
     { 
      //First look in the hosting application area folder/Views/ordering type 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml 
      "Areas/{2}/Views/%1/{1}/{0}.cshtml", 

      //Next look in the hosting application area folder/Views/ordering type/Shared 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml 
      "Areas/{2}/Views/%1/Shared/(0}.cshtml", 

      //Finally look in the IMS.POS.Web.Views.Core assembly 
      "Areas/{2}/Views/{1}/{0}.cshtml" 
     }; 

     //Same format logic 
     AreaMasterLocationFormats = AreaViewLocationFormats; 

     AreaPartialViewLocationFormats = new[] 
     { 
      //First look in the hosting application area folder/Views/ordering type 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/Partials/{PartialViewName}.cshtml 
      "Areas/{2}/Views/%1/{1}/Paritals/{0}.cshtml", 

      //Next look in the hosting application area folder/Views/ordering type/Shared 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml 
      "Areas/{2}/Views/%1/Shared/(0}.cshtml", 

      //Finally look in the IMS.POS.Web.Views.Core 
      "Areas/{2}/Views/{1}/{0}.cshtml" 
     }; 

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

     MasterLocationFormats = ViewLocationFormats; 

     PartialViewLocationFormats = new[] 
     { 
      "Views/%1/{1}/Partials/{0}.cshtml", 
      "Views/%1/Shared/{0}.cshtml", 
      "Views/{1}/Partials/{0}.cshtml", 
      "Views/Shared/{0}.cshtml" 
     }; 




    } 

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) 
    { 
     return base.CreatePartialView(controllerContext, partialPath.ReplaceOrderType(CurrentOrderingMode())); 
    } 

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) 
    { 
     OrderType orderType = CurrentOrderingMode(); 
     return base.CreateView(controllerContext, viewPath.ReplaceOrderType(orderType), masterPath.ReplaceOrderType(orderType)); 
    } 

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath) 
    { 
     return base.FileExists(controllerContext, virtualPath.Replace("%1/",string.Empty)); 
    } 


    private OrderType CurrentOrderingMode() 
    { 
     OrderType result; 
     _profileService = DependencyResolver.Current.GetService<IUserProfileService>(); 

     if (_profileService == null || _profileService.OrderingType == 0) 
     { 
      IApplicationSettingService settingService = 
       DependencyResolver.Current.GetService<IApplicationSettingService>(); 

      result = 
       settingService.GetApplicationSetting(ApplicationSettings.DefaultOrderingMode) 
        .ToEnumTypeOf<OrderType>(); 
     } 
     else 
     { 
      result = _profileService.OrderingType; 
     } 

     return result; 
    } 



} 

Здесь используется класс StartUp, используемый RazorGenerator для регистрации ViewEngine.

public static class RazorGeneratorMvcStart 
{ 
    public static void Start() 
    { 
     var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly) 
     { 
      UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal 
     }; 

     ViewEngines.Engines.Insert(0, engine); 

     // StartPage lookups are done by WebPages. 
     VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
    } 
} 

Проблема заключается в том:

  1. Этот код выполняется в последний раз (после того, как я зарегистрировать PosViewEngine) и вставляет двигатель на первой позиции (это означает, что это двигатель, который получает разрешен 1-го при обслуживании ответы). Это заканчивает поиск взгляда - это основной вид.
  2. Если я изменить код в StartUp зарегистрировать мой пользовательский вид двигателя первый первый, а затем RazorGenerator двигатель

    public static void Start() 
    { 
        var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly) 
        { 
         UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal 
        }; 
    
        ViewEngines.Engines.Clear(); 
        ViewEngines.Engines.Insert(0, new PosViewEngine()); 
        ViewEngines.Engines.Insert(1, engine); 
    
        // StartPage lookups are done by WebPages. 
        VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
    } 
    

Я в конечном итоге с исключением на FileExists (ControllerContext controllerContext, строка виртуальный_путь) method - «Относительный виртуальный путь« Views/Account/LogOn.cshtml »здесь не разрешен».

Очевидно, что это связано с тем, что физические и виртуальные пути смешиваются.

Похоже, что кто-то пытался сделать то же самое here, но я не видел ответа на это.

ответ

0

Для тех, кто хочет попробовать этот подход, я отправлю ответ. В основном вам нужно реализовать собственный механизм просмотра, который выводится из PrecompiledMvcEngine, найденного в сборке RazorGenerator.

public class PosPrecompileEngine : PrecompiledMvcEngine 
{ 
    private IUserProfileService _profileService; 



    public PosPrecompileEngine(Assembly assembly) : base(assembly) 
    { 
     LocatorConfig(); 
    } 

    public PosPrecompileEngine(Assembly assembly, string baseVirtualPath) : base(assembly, baseVirtualPath) 
    { 
     LocatorConfig(); 
    } 

    public PosPrecompileEngine(Assembly assembly, string baseVirtualPath, IViewPageActivator viewPageActivator) : base(assembly, baseVirtualPath, viewPageActivator) 
    { 
     LocatorConfig(); 
    } 

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) 
    { 
     return base.CreatePartialView(controllerContext, partialPath.ReplaceOrderType(CurrentOrderingMode())); 
    } 

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) 
    { 
     OrderType orderType = CurrentOrderingMode(); 
     return base.CreateView(controllerContext, viewPath.ReplaceOrderType(orderType), masterPath.ReplaceOrderType(orderType)); 
    } 

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath) 
    { 
     return base.FileExists(controllerContext, virtualPath.ReplaceOrderType(CurrentOrderingMode())); 
    } 
} 

В этом классе - я переопределяю пути локатора. Поскольку у меня есть «базовые» скомпилированные представления в другой сборке из веб-приложения, мы внедрили соглашение, в котором механизм просмотра сначала будет искать в пути PosViews/{порядок заказа}/{controller}/{view} в веб-приложении. Если представление не расположено, то оно будет выглядеть в традиционном/Views/controller/view. Трюк здесь - это более поздний виртуальный путь, расположенный в другой библиотеке классов.

Это позволило нам «переопределить» существующее представление для приложения.

private void LocatorConfig() 
    { 
     //{0} = View Name 
     //{1} = ControllerName 
     //{2} = Area Name 
     AreaViewLocationFormats = new[] 
     { 
      //First look in the hosting application area folder/Views/ordering type 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml 
      "PosAreas/{2}/Views/%1/{1}/{0}.cshtml", 

      //Next look in the hosting application area folder/Views/ordering type/Shared 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml 
      "PosAreas/{2}/Views/%1/Shared/(0}.cshtml", 

      //Next look in the POS Areas Shared 
      "PosAreas/{2}/Views/Shared/(0}.cshtml", 

      //Finally look in the IMS.POS.Web.Views.Core assembly 
      "Areas/{2}/Views/{1}/{0}.cshtml" 
     }; 

     //Same format logic 
     AreaMasterLocationFormats = AreaViewLocationFormats; 

     AreaPartialViewLocationFormats = new[] 
     { 
      //First look in the hosting application area folder/Views/ordering type 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/Partials/{PartialViewName}.cshtml 
      "PosAreas/{2}/Views/%1/{1}/Partials/{0}.cshtml", 

      //Next look in the hosting application area folder/Views/ordering type/Shared 
      //Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml 
      "PosAreas/{2}/Views/%1/Shared/(0}.cshtml", 

      //Next look in the hosting application shared folder 
      "PosAreas/{2}/Views/Shared/(0}.cshtml", 

      //Finally look in the IMS.POS.Web.Views.Core 
      "Areas/{2}/Views/{1}/{0}.cshtml" 
     }; 

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

     MasterLocationFormats = ViewLocationFormats; 

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

Зарегистрируйте этот двигатель в своем запуске приложений.

public static void Configure() 
    { 
     var engine = new PosPrecompileEngine(typeof(ViewEngineConfig).Assembly) 
     { 
      UsePhysicalViewsIfNewer = true, 
      PreemptPhysicalFiles = true 
     }; 
     ViewEngines.Engines.Add(engine); 

     // StartPage lookups are done by WebPages. 
     VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
    } 

Вот окончательный ключ. Когда RazorGenerator получает установлен вид NuGet - вы в конечном итоге с этим классом запуска, который будет выполняться при запуске

[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(Views.Core.RazorGeneratorMvcStart), "Start")] 


public static class RazorGeneratorMvcStart 
{ 
    public static void Start() 
    { 
     var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly) 
     { 
      UsePhysicalViewsIfNewer = true, 
      PreemptPhysicalFiles = true 
     }; 
     ViewEngines.Engines.Add(engine); 

     // StartPage lookups are done by WebPages. 
     VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
    } 
} 

По умолчанию - RazorGenerator добавляет ViewEngine к первой в коллекции

ViewEngines.Engines.Insert(0,engine); 

Вам нужно изменить что надстройке

ViewEngines.Engines.Add(engine); 

Так что добавляют к двигателям последних - это путь ваш заказ ViewEngine используется FIRST в поиске точки зрения.

Этот подход позволяет повторно использовать представления в нескольких приложениях, позволяя использовать это средство для переопределения.

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

+0

Может ли это использоваться для добавления пользовательского контента в выходящие виды? Я опубликовал это http://stackoverflow.com/questions/38303160/how-to-use-razor-to-process-dynamic-templates-included-in-web-page – Andrus

+0

Не с этим подходом. Это скорее соглашение, в котором я предоставил базовый набор представлений действиям контроллера и при необходимости заменил их. Что бы вы могли для своего вопроса использовать пользовательский базовый вид, чтобы выставлять свои собственные свойства и что-то вроде RazorEngine, чтобы вводить контент через свойство. – JDBennett

 Смежные вопросы

  • Нет связанных вопросов^_^