wake-up-neo.com

Verwenden des benutzerdefinierten VirtualPathProvider zum Laden eingebetteter Ressourcen-Teilansichten

Ich habe benutzerdefinierte VirtualFile- und VirtualPathProvider-Implementierungen geschrieben, die erfolgreich eingebettete Ressourcen abrufen, die Teilansichten sind.

Wenn ich versuche, sie zu rendern, wird dieser Fehler ausgegeben:

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

Beim Rendern der Teilansicht in einer regulären Ansicht sieht es folgendermaßen aus:

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

Was lässt es glauben, dass dies keine Teilansicht ist?

BEARBEITEN: Ich habe meinen Code für die Implementierungen der Anbieter virtueller Dateien und virtueller Dateien für alle veröffentlicht, die über diese Lösung stolpern und nach einer Lösung suchen, damit diese Komponente funktioniert. Diese Frage eignet sich auch für diejenigen, die auf dem Fragetitel basieren.

die VirtualFile-Implementierung dient als Referenz:

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);
        }
    }
}

Vergessen Sie natürlich nicht, diesen neuen Provider in der Datei Global.asax Ihres Projekts im Event Application_Start () zu registrieren

System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new SVirtualPathProvider());
36
Feisty Mango

Da Sie nun Ihre Ansichten von einem unbekannten Ort aus bereitstellen, gibt es nicht mehr die ~/Views/web.config-Datei, die die Basisklasse für Ihre Rasiereransichten (<pages pageBaseType="System.Web.Mvc.WebViewPage">) angibt und angibt. Sie können also in jeder eingebetteten Ansicht eine @ inherits-Direktive hinzufügen, um die Basisklasse anzugeben.

@inherits System.Web.Mvc.WebViewPage
@model ...
44
Darin Dimitrov

Ich habe die Antwort des OP als Basis verwendet, aber etwas erweitert und die Antwort auf die Frage in meine Lösung aufgenommen.

Dies scheint eine eher häufig gestellte Frage zu SO zu sein, und ich habe noch keine vollständige Antwort gesehen. Ich dachte, es wäre hilfreich, meine Arbeitslösung mitzuteilen.

Ich lade meine Ressourcen aus einer Datenbank und habe sie im Standard-Cache (System.Web.Caching.Cache) zwischengespeichert.

Am Ende habe ich eine benutzerdefinierte Cache-Abhängigkeit für den Schlüssel erstellt, den ich zum Nachschlagen der Ressource verwende. Immer dann, wenn mein anderer Code diesen Cache ungültig macht (bei einer Bearbeitung usw.), wird die Cache-Abhängigkeit von diesem Schlüssel entfernt und der VirtualPathProvider wiederum macht seinen Cache ungültig und die VirtualFile wird neu geladen.

Ich habe auch den Code so geändert, dass er automatisch die erbliche Anweisung voranstellt, sodass es nicht in meiner Datenbankressource gespeichert werden muss. Außerdem führe ich automatisch einige Standardanweisungen mit Anweisungen aus, da diese "Ansicht" nicht über die normalen Kanäle geladen wird Alle in Ihrer web.config oder viewstart enthaltenen Standardeinstellungen sind nicht verwendbar.

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);
    }
}

Hoffe das hilft!

6
Max Schilling

Ich stützte mich stark auf die Informationen im OP sowie auf die Antwort von Darin Dimitrov, um einen einfachen Prototyp für die gemeinsame Nutzung von MVC-Komponenten in Projekten zu erstellen. Während diese sehr hilfreich waren, stieß ich immer noch auf einige zusätzliche Barrieren, die im Prototyp angesprochen werden, z. B. gemeinsame Ansichten mit @ -Models.

0
kmkemp