Просмотры в отдельных assemblyх в ASP.NET MVC

Я пытаюсь создать веб-приложение, где я хочу иметь возможность подключать отдельные сборки. Я использую предварительный просмотр MVC 4 в сочетании с Unity для инъекции зависимостей, который я использую для создания controllerов из своих модhive плагинов. Я использую WebForms (по умолчанию aspx) в качестве моего механизма просмотра.

Если я хочу использовать представление, я застрял на тех, которые определены в основном проекте, из-за динамической компиляции части ASPX. Я ищу подходящий способ для добавления файлов ASPX в другую сборку без необходимости проходить весь шаг развертывания. Мне что-то не хватает? Или я должен прибегать к программному программированию?


Обновление: я изменил принятый ответ. Несмотря на то, что ответ Дейла очень тщательный, я пошел на решение с другим поставщиком виртуальных путей. Это работает как шарм, и я считаю, что всего около 20 строк кода.

По сути, это та же проблема, что и люди с WebForms, и пытается скомпилировать их файлы UserControl ASCX в DLL. Я нашел этот http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx, который может сработать и для вас.

Мне потребовалось слишком много времени, чтобы заставить это работать должным образом из различных частичных образцов, так что вот полный код, необходимый для получения представлений из папки «Представления» в общей библиотеке, структурированной так же, как и в обычной папке «Представления», но со всем, что нужно построить как встроенное Ресурсы. Он будет использовать только встроенный файл, если обычный файл не существует.

Первая строка 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, не будет использоваться:

      

Для его работы с Mono требуется несколько дополнительных шагов. Во-первых, вам нужно реализовать 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(); 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; %> 
 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 viewengine. Помимо того, что я был абсолютно потрясающим, и я бы не стал возвращаться к веб-формам, даже если бы меня угрожали, это также дает некоторые очень приятные крючки для модульности приложения. Пример в их документах – использование Windsor в качестве контейнера IoC, но я не могу представить, чтобы это было намного сложнее, если вы хотите использовать другой подход.

Interesting Posts
Давайте будем гением компьютера.