Представления в отдельных сборках в ASP.NET MVC - PullRequest
50 голосов
/ 21 августа 2008

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

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


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

Ответы [ 4 ]

30 голосов
/ 01 января 2011

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

Для работы с 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<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>;  %>
16 голосов
/ 30 августа 2008

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

13 голосов
/ 04 октября 2009
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);
}

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

2 голосов
/ 22 марта 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...