2016-06-23 13 views
0

RazorGenerator를 사용하여 사용자 정의 (파생 된) RazorViewEngine 및 프리 컴파일 된 뷰를 사용하려고합니다.사용자 정의 RazorViewEngine 및 RazorGenerator 프리 컴파일 된 뷰 사용

일부 컨텍스트 :

우리는 우리가 여러 클라이언트 구현에 사용하는 기본 제품이있다. 이를 통해 핵심 뷰 집합이 생깁니다. 대부분의 견해가 대부분의 경우에 효과적입니다. 지금 당장 우리는 각각의 새로운 솔루션에 대한 기존 뷰를 복사하고 필요에 따라 수정합니다. 이는 95 %의 조회수가 클라이언트간에 동일하고 5 %가 변경되는 결과를 가져옵니다.

기본 뷰 집합을 DLL로 컴파일하고 클라이언트 전체에서 다시 사용하고 싶습니다. 지금까지 나는 RazorGenerator를 사용하여 잘 작동하고 있습니다.

이제 다음 단계는보기의 사용자 지정 (재정의)을 허용하는 것입니다. 그래도주의 사항이 있습니다. 우리의 응용 프로그램에는 사용자가 속한 두 가지 "모드"가 있습니다.이 모드에는 다른보기가 필요할 수 있습니다.

RazorGeneratorView에서 파생 클래스를 만들었습니다. 이 뷰는 기본적으로 Autofac이 해결하는 UserProfile 객체에서 "OrderingMode"를 검사합니다. 모드에 따라 뷰 해결을 위해 경로 로케이터가 대체됩니다.

개별 클라이언트 응용 프로그램이라는 생각은 기존보기 폴더에서 먼저보기를 확인하려고 시도합니다. 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; 
    } 



} 

여기 RazorGenerator가 ViewEngine을 등록하는 데 사용하는 StartUp 클래스가 있습니다.

  1. 이 코드는 (필자는 PosViewEngine 등록 후) 마지막으로 실행되고, 이것이 제공되면 첫번째 해결됩니다 엔진 인 의미 (첫 번째 위치에서 엔진을 삽입합니다

    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); 
        } 
    } 
    

    문제는 응답). 이것은보기를 찾는 것을 끝내 - 그것은 핵심보기입니다.

  2. 내 사용자 지정보기 엔진 첫째 첫째 후 RazorGenerator 엔진

    나는 FileExists에 예외로 끝날
    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); 
    } 
    

(ControllerContext controllerContext, 문자열 virtualPath를 등록하려면 시작 프로그램의 코드를 변경하는 경우) 방법 - "상대 가상 경로 'Views/Account/LogOn.cshtml'은 허용되지 않습니다."

분명히 물리적 경로와 가상 경로가 함께 혼합되는 것과 관련이 있습니다.

누군가 다른 사람이 똑같은 짓을하려 한 것처럼 보입니다. here하지만 이것에 대한 대답은 없었습니다.

답변

0

이 접근법을 시도하려는 다른 사람들에게 답을 게시 할 것입니다. 기본적으로 RazorGenerator 어셈블리에있는 PrecompiledMvcEngine에서 파생 된 사용자 정의 뷰 엔진을 구현해야합니다.

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/{순서 지정 모드}/{컨트롤러}/{뷰} 경로를 볼 규칙을 구현했습니다. 보기가 발견되지 않으면 전통적인 /보기/제어기 /보기를 볼 것입니다. 이 트릭은 나중에 다른 클래스 라이브러리에있는 가상 경로입니다.

이렇게하면 응용 프로그램의 기존보기를 "대체"할 수있었습니다.

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); 

당신은 필요 사용자 정의 ViewEngine이 뷰의 위치에 FIRST 사용되는이 방법 -

ViewEngines.Engines.Add(engine); 

는 그래서 마지막으로 엔진에 추가됩니다 추가로 그 변경합니다.

이 방법을 사용하면 여러보기에서보기를 다시 정의하고 해당보기를 재정의 할 수 있습니다.

이것은 대부분의 응용 프로그램에 과도 할 수 있습니다. - 질문에서 언급했듯이 - 이것은 우리가 여러 클라이언트 응용 프로그램을 개발할 때 사용하는 기본 제품입니다. 클라이언트 단위로 유연성 수준을 유지하면서 재사용을 시도하는 것은 우리가 달성하려고 시도했던 것입니다.

+0

exiting 뷰에 맞춤 콘텐츠를 추가 할 수 있습니까? 나는 이것을 게시했다. http://stackoverflow.com/questions/38303160/how-to-use-razor-to-process-dynamic-templates-included-in-web-page – Andrus

+0

이 접근법이 아니다. 이는 컨트롤러 액션에 기본 뷰 집합을 제공하고 필요에 따라 오버라이드하는 오버 법칙입니다. 귀하의 질문에 대한 귀하의 자신의 속성과 RazorEngine 같은 속성을 통해 내용을 주입 뭔가를 노출하는 사용자 정의 기본보기를 사용하고 있습니다. – JDBennett