Представления в отдельных сборках в ASP.NET MVC

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

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


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

21.08.2008 12:37:23
Работает ли поставщик виртуального пути с маршрутизацией в mvc.net?
jmcd 24.10.2008 11:35:48
@jmcd: похоже, что так и есть.
Jakub Šturc 25.05.2009 13:33:49
Есть ли пример кода, который вы могли бы опубликовать, который позволил бы вам это сделать?
steve_c 11.01.2010 20:52:22
Проект, который я попробовал, был заброшен, поэтому у меня нет готового примера кода. Однако механизм просмотра искры также позволяет это ( sparkviewengine.com ). Я думаю, что там есть образец, называемый модулями, который разделяет области в разных сборках.
Erik van Brakel 11.01.2010 22:56:09
4 ОТВЕТА
РЕШЕНИЕ

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

16
29.08.2008 20:34:48
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);
}

Установите свойство «Копировать в вывод» вашего представления в «Копировать всегда»

13
3.10.2009 20:29:31
Вместо создания WebFormviewEngine вы можете сделать то же самое, получив RazorViewEngine следующим образом: RazorViewEngine engine = ViewEngines.Engines.OfType <RazorViewEngine> (). First (); а затем просто конкататируйте дополнительные пути к указанным свойствам.
CSharpie 30.11.2014 15:52:39

Дополнение для всех вас, кто все еще ищет святой Грааль: я подошел немного ближе, чтобы найти его, если вы не слишком привязаны к представлению webforms.

Я недавно опробовал двигатель Spark. Помимо того, что я совершенно потрясающий, и я бы не стал возвращаться к веб-формам, даже если бы мне угрожали, он также предоставляет несколько очень хороших хуков для модульности приложения. Пример в их документации использует Windsor в качестве контейнера IoC, но я не могу представить, что будет намного сложнее, если вы захотите использовать другой подход.

2
5.10.2015 18:33:19

Мне потребовалось слишком много времени, чтобы заставить это работать должным образом из различных частичных примеров, так что вот полный код, необходимый для получения представлений из папки 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, поскольку тот в папке представлений не будет использоваться:

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

Пара дополнительных шагов требуется, чтобы заставить это работать с Моно. Во-первых, вам нужно реализовать 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>;  %>
30
2.01.2011 10:19:18
Также важно помнить, что для представления необходимо установить действие сборки «Встроенный ресурс». Папка для просмотра также должна существовать в веб-проекте. В моем случае я помещаю представление в папку проекта сборки Views / Shared / Dynamic.cshtml и должен создать Views / Shared в веб-проекте, который ссылается на сборку.
Pavel Korsukov 4.09.2013 06:38:30