Использование пользовательского VirtualPathProvider для загрузки встроенных ресурсов Частичные виды
Я написал собственные реализации 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");
Что заставляет думать, что это не частичное представление?
EDIT: Я опубликовал свой код для виртуальных файлов и реализаций виртуальных файлов для всех, кто наткнулся на это, ища решение о том, как работать с этим компонентом. Этот вопрос также будет полезен для тех, кто основан на названии вопроса.
Это реализация 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());
Ответы
Ответ 1
Поскольку теперь вы обслуживаете свои представления из неизвестного местоположения, теперь не используется файл ~/Views/web.config
и указывает базовый класс для просмотров бритвы (<pages pageBaseType="System.Web.Mvc.WebViewPage">
). Таким образом, вы можете добавить директиву @inherits в верхней части каждого встроенного представления, чтобы указать базовый класс.
@inherits System.Web.Mvc.WebViewPage
@model ...
Ответ 2
Я использовал ответ OPs в качестве базы, но немного расширил его и включил ответ на вопрос в своем решении.
Это похоже на несколько часто задаваемый вопрос здесь на 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);
}
}
Надеюсь, это поможет!
Ответ 3
Я тяжело опираясь на информацию в OP, а также Дарин Димитров ответил, чтобы создать простой прототип для совместного использования MVC компонентов по проектам. Хотя это было очень полезно, я все же столкнулся с несколькими дополнительными барьерами, которые рассматриваются в прототипе, например, с использованием общих представлений с помощью модели @.