Использование пользовательского VirtualPathProvider для загрузки встроенного ресурса Partial Views - PullRequest
36 голосов
/ 13 октября 2011

Я написал пользовательские реализации VirtualFile и VirtualPathProvider, которые успешно получают встроенные ресурсы, являющиеся частичными представлениями.

Однако, когда я пытаюсь их отобразить, выдается эта ошибка:

The view at '~/Succeed.Web/Succeed.Web.Controls.SImporter._SImporter.cshtml' must derive from WebViewPage, or WebViewPage<TModel>.

При отображении частичного представления внутри обычного представления это выглядит следующим образом:

Html.RenderPartial("~/Succeed.Web/Succeed.Web.Controls.SImporter._SImporter.cshtml");

Что заставляет его верить, что это не частичное представление?

РЕДАКТИРОВАТЬ: Я опубликовал свой код для реализации как виртуального файла, так и виртуального провайдера файлов для всех, кто сталкивается с этим поиском решения для обеспечения работы этого компонента.Этот вопрос также будет полезен для тех, кто основан на заголовке вопроса.

Вот реализация VirtualFile для справки:

public class SVirtualFile : VirtualFile
{
    private string m_path;

    public SVirtualFile(string virtualPath)
        : base(virtualPath)
    {
        m_path = VirtualPathUtility.ToAppRelative(virtualPath);
    }

    public override System.IO.Stream Open()
    {
        var parts = m_path.Split('/');
        var assemblyName = parts[1];
        var resourceName = parts[2];

        assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
        var assembly = System.Reflection.Assembly.LoadFile(assemblyName + ".dll");

        if (assembly != null)
        {
            return assembly.GetManifestResourceStream(resourceName);
        }
        return null;
    }
}

VirtualPathProvider:

public class SVirtualPathProvider : VirtualPathProvider
{
    public SVirtualPathProvider() 
    { 

    }

    private bool IsEmbeddedResourcePath(string virtualPath)
    {
        var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return checkPath.StartsWith("~/Succeed.Web/", StringComparison.InvariantCultureIgnoreCase);
    }

    public override bool FileExists(string virtualPath)
    {
        return IsEmbeddedResourcePath(virtualPath) || base.FileExists(virtualPath);
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        if (IsEmbeddedResourcePath(virtualPath))
        {
            return new SVirtualFile(virtualPath);
        }
        else
        {
            return base.GetFile(virtualPath);
        }
    }

    public override CacheDependency GetCacheDependency( string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        if (IsEmbeddedResourcePath(virtualPath))
        {
            return null;
        }
        else
        {
            return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        }
    }
}

И изКонечно, не забудьте зарегистрировать этого нового провайдера в файле Global.asax вашего проекта в событии Application_Start ()

System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new SVirtualPathProvider());

Ответы [ 3 ]

44 голосов
/ 13 октября 2011

Поскольку теперь вы обслуживаете свои виды из какого-то неизвестного места, больше нет файла ~/Views/web.config, который применяется и указывает базовый класс для ваших видов бритвы (<pages pageBaseType="System.Web.Mvc.WebViewPage">).Таким образом, вы можете добавить директиву @inherits вверху каждого встроенного представления, чтобы указать базовый класс.

@inherits System.Web.Mvc.WebViewPage
@model ...
6 голосов
/ 05 августа 2013

Я использовал ответ OP в качестве основы, но немного расширил его и включил ответ на вопрос в своем решении.

Это кажется довольно часто задаваемым вопросом здесь, на SO, и я невидел полный ответ, поэтому я подумал, что было бы полезно поделиться своим рабочим решением.

Я загружаю свои ресурсы из базы данных и кэширую их в кэше по умолчанию (System.Web.Caching.Cache).

В итоге я создал пользовательскую CacheDependency для KEY, которую я использую для поиска ресурса.Таким образом, всякий раз, когда мой другой код аннулирует этот кеш (при редактировании и т. Д.), Зависимость кеша от этого ключа удаляется, а VirtualPathProvider, в свою очередь, делает недействительным свой кеш, а VirtualFile перезагружается.

Я также изменялкод, чтобы он автоматически дописывал оператор наследует, чтобы его не нужно было хранить в моем ресурсе базы данных, и я также автоматически добавляю несколько операторов по умолчанию, так как это «представление» не загружается по обычным каналам, поэтому все, что по умолчанию включает в себяВаш web.config или viewstart не могут быть использованы.

CustomVirtualFile:

public class CustomVirtualFile : VirtualFile
{
    private readonly string virtualPath;

    public CustomVirtualFile(string virtualPath)
        : base(virtualPath)
    {
        this.virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
    }

    private static string LoadResource(string resourceKey)
    {
        // Load from your database respository or whatever here...
        // Note that the caching is disabled for this content in the virtual path
        // provider, so you must cache this yourself in your repository.

        // My implementation using my custom service locator that sits on top of
        // Ninject
        var contentRepository = FrameworkHelper.Resolve<IContentRepository>();

        var resource = contentRepository.GetContent(resourceKey);

        if (String.IsNullOrWhiteSpace(resource))
        {
            resource = String.Empty;
        }

        return resource;
    }

    public override Stream Open()
    {
        // Always in format: "~/CMS/{0}.cshtml"
        var key = virtualPath.Replace("~/CMS/", "").Replace(".cshtml", "");

        var resource = LoadResource(key);

        // this automatically appends the inherit and default using statements 
        // ... add any others here you like or append them to your resource.
        resource = String.Format("{0}{1}", "@inherits System.Web.Mvc.WebViewPage<dynamic>\r\n" +
                                           "@using System.Web.Mvc\r\n" +
                                           "@using System.Web.Mvc.Html\r\n", resource);

        return resource.ToStream();
    }
}

CustomVirtualPathProvider:

public class CustomVirtualPathProvider : VirtualPathProvider
{
    private static bool IsCustomContentPath(string virtualPath)
    {
        var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return checkPath.StartsWith("~/CMS/", StringComparison.InvariantCultureIgnoreCase);
    }

    public override bool FileExists(string virtualPath)
    {
        return IsCustomContentPath(virtualPath) || base.FileExists(virtualPath);
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        return IsCustomContentPath(virtualPath) ? new CustomVirtualFile(virtualPath) : base.GetFile(virtualPath);
    }

    public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        if (IsCustomContentPath(virtualPath))
        {
            var key = VirtualPathUtility.ToAppRelative(virtualPath);

            key = key.Replace("~/CMS/", "").Replace(".cshtml", "");

            var cacheKey = String.Format(ContentRepository.ContentCacheKeyFormat, key);

            var dependencyKey = new String[1];
            dependencyKey[0] = string.Format(cacheKey);

            return new CacheDependency(null, dependencyKey);
        }

        return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
    }

    public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
    {
        if (IsCustomContentPath(virtualPath))
        {
            return virtualPath;
        }

        return base.GetFileHash(virtualPath, virtualPathDependencies);
    }
}

Надеюсь, это поможет!

0 голосов
/ 18 марта 2015

Я опирался на информацию в ОП, а также на ответ Дарина Димитрова, чтобы создать простой прототип для совместного использования компонентов MVC между проектами.Хотя они были очень полезны, я все же столкнулся с несколькими дополнительными барьерами, которые устранены в прототипе, такими как использование общих представлений с @ model.

...