ASP.NET MVC의 별도 어셈블리에 있는 뷰
-
09-06-2019 - |
문제
별도의 어셈블리를 플러그인할 수 있는 웹 애플리케이션을 만들려고 합니다.저는 종속성 주입을 위해 Unity와 결합된 MVC Preview 4를 사용하고 있는데, 이를 사용하여 플러그인 어셈블리에서 컨트롤러를 생성합니다.저는 뷰 엔진으로 WebForms(기본 aspx)를 사용하고 있습니다.
뷰를 사용하려는 경우 ASPX 부분의 동적 컴파일로 인해 핵심 프로젝트에 정의된 뷰에 갇히게 됩니다.전체 배포 단계를 거치지 않고도 ASPX 파일을 다른 어셈블리에 포함할 수 있는 적절한 방법을 찾고 있습니다.나는 분명한 것을 놓치고 있습니까?아니면 프로그래밍 방식으로 뷰를 생성해야 합니까?
업데이트:허용되는 답변을 변경했습니다.Dale의 답변이 매우 철저함에도 불구하고 저는 다른 가상 경로 제공자를 통해 솔루션을 찾았습니다.그것은 마치 매력처럼 작동하며 코드 전체에서 약 20줄만 사용하면 됩니다.
해결책
본질적으로 이것은 사람들이 WebForms를 사용하고 UserControl ASCX 파일을 DLL로 컴파일하려고 시도하는 것과 동일한 문제입니다.나는 이것을 찾았다 http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx 그것은 당신에게도 효과가 있을 것입니다.
다른 팁
다양한 부분 샘플에서 이 작업이 제대로 작동하도록 하는 데 너무 오랜 시간이 걸렸습니다. 따라서 일반 Views 폴더와 동일하게 구조화되었지만 모든 항목이 포함된 것으로 빌드되도록 설정된 공유 라이브러리의 Views 폴더에서 보기를 가져오는 데 필요한 전체 코드는 다음과 같습니다. 자원.일반적인 파일이 존재하지 않는 경우에만 포함된 파일을 사용합니다.
Application_Start의 첫 번째 줄:
HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider());
VirtualPathProvider
public class EmbeddedVirtualFile : VirtualFile
{
public EmbeddedVirtualFile(string virtualPath)
: base(virtualPath)
{
}
internal static string GetResourceName(string virtualPath)
{
if (!virtualPath.Contains("/Views/"))
{
return null;
}
var resourcename = virtualPath
.Substring(virtualPath.IndexOf("Views/"))
.Replace("Views/", "OrangeGuava.Common.Views.")
.Replace("/", ".");
return resourcename;
}
public override Stream Open()
{
Assembly assembly = Assembly.GetExecutingAssembly();
var resourcename = GetResourceName(this.VirtualPath);
return assembly.GetManifestResourceStream(resourcename);
}
}
public class EmbeddedViewPathProvider : VirtualPathProvider
{
private bool ResourceFileExists(string virtualPath)
{
Assembly assembly = Assembly.GetExecutingAssembly();
var resourcename = EmbeddedVirtualFile.GetResourceName(virtualPath);
var result = resourcename != null && assembly.GetManifestResourceNames().Contains(resourcename);
return result;
}
public override bool FileExists(string virtualPath)
{
return base.FileExists(virtualPath) || ResourceFileExists(virtualPath);
}
public override VirtualFile GetFile(string virtualPath)
{
if (!base.FileExists(virtualPath))
{
return new EmbeddedVirtualFile(virtualPath);
}
else
{
return base.GetFile(virtualPath);
}
}
}
이를 작동시키기 위한 마지막 단계는 루트 Web.Config에 강력한 형식의 MVC 보기를 구문 분석하기 위한 올바른 설정이 포함되어야 한다는 것입니다. views 폴더에 있는 보기는 사용되지 않기 때문입니다.
<pages
validateRequest="false"
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<controls>
<add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
</controls>
</pages>
Mono와 함께 작동하려면 몇 가지 추가 단계가 필요합니다.먼저, 필요에 따라가 아니라 앱이 시작될 때 views 폴더의 모든 파일이 로드되므로 GetDirectory를 구현해야 합니다.
public override VirtualDirectory GetDirectory(string virtualDir)
{
Log.LogInfo("GetDirectory - " + virtualDir);
var b = base.GetDirectory(virtualDir);
return new EmbeddedVirtualDirectory(virtualDir, b);
}
public class EmbeddedVirtualDirectory : VirtualDirectory
{
private VirtualDirectory FileDir { get; set; }
public EmbeddedVirtualDirectory(string virtualPath, VirtualDirectory filedir)
: base(virtualPath)
{
FileDir = filedir;
}
public override System.Collections.IEnumerable Children
{
get { return FileDir.Children; }
}
public override System.Collections.IEnumerable Directories
{
get { return FileDir.Directories; }
}
public override System.Collections.IEnumerable Files
{
get {
if (!VirtualPath.Contains("/Views/") || VirtualPath.EndsWith("/Views/"))
{
return FileDir.Files;
}
var fl = new List<VirtualFile>();
foreach (VirtualFile f in FileDir.Files)
{
fl.Add(f);
}
var resourcename = VirtualPath.Substring(VirtualPath.IndexOf("Views/"))
.Replace("Views/", "OrangeGuava.Common.Views.")
.Replace("/", ".");
Assembly assembly = Assembly.GetExecutingAssembly();
var rfl = assembly.GetManifestResourceNames()
.Where(s => s.StartsWith(resourcename))
.Select(s => VirtualPath + s.Replace(resourcename, ""))
.Select(s => new EmbeddedVirtualFile(s));
fl.AddRange(rfl);
return fl;
}
}
}
마지막으로 강력한 형식의 뷰는 거의 완벽하게 작동하지는 않습니다.모델은 유형이 지정되지 않은 객체로 처리되므로 강력한 유형을 다시 얻으려면 다음과 같이 공유 보기를 시작해야 합니다.
<% var Model2 = Model as IEnumerable<AppModel>; %>
protected void Application_Start()
{
WebFormViewEngine engine = new WebFormViewEngine();
engine.ViewLocationFormats = new[] { "~/bin/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" };
engine.PartialViewLocationFormats = engine.ViewLocationFormats;
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(engine);
RegisterRoutes(RouteTable.Routes);
}
뷰의 '출력에 복사' 속성을 '항상 복사'로 설정하세요.
아직도 성배를 찾고 있는 여러분에게 추가 사항:웹폼 뷰엔진에 너무 집착하지 않는다면 나는 그것을 찾는 데 좀 더 가까워졌습니다.
나는 최근에 Spark 뷰엔진을 시험해 보았습니다.완전히 훌륭하고 위협을 받더라도 웹 양식으로 돌아가지 않을 것이라는 점 외에도 응용 프로그램의 모듈성을 위한 몇 가지 아주 좋은 후크를 제공합니다.문서의 예는 Windsor를 IoC 컨테이너로 사용하는 것이지만 다른 접근 방식을 취하려는 경우 훨씬 더 어려울 것이라고는 상상할 수 없습니다.