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

StackOverflow https://stackoverflow.com/questions/19746

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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


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

Это было полезно?

Решение

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

Другие советы

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

Первая строка Application_Start:

HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider());

ВиртуальныйПатПровидер

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top