web-dev-qa-db-fra.com

Utilisation de VirtualPathProvider personnalisé pour charger des vues partielles de ressources incorporées

J'ai écrit les implémentations personnalisées de VirtualFile et VirtualPathProvider qui permettent d'obtenir des ressources incorporées qui sont des vues partielles.

Cependant, lorsque j'essaye de les rendre, cela produit cette erreur:

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

Lors du rendu de la vue partielle, à l'intérieur d'une vue normale, il se présente comme suit:

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

Qu'est-ce qui lui fait croire que ce n'est pas une vue partielle?

EDIT: J'ai publié mon code pour les implémentations de fournisseur de fichier virtuel et de fichier virtuel pour toute personne qui tombe par hasard sur cette recherche d'une solution permettant de faire fonctionner ce composant. Cette question sera également utile pour ceux basés sur le titre de la question.

voici l’implémentation de VirtualFile pour référence:

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

Et bien sûr, n'oubliez pas d'enregistrer ce nouveau fournisseur dans le fichier Global.asax de votre projet dans l'événement Application_Start ().

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

Parce que maintenant vous diffusez vos vues depuis un emplacement inconnu, le fichier ~/Views/web.config ne s'applique plus et indique la classe de base de vos vues rasoir (<pages pageBaseType="System.Web.Mvc.WebViewPage">). Vous pouvez donc ajouter une directive @inherits en haut de chaque vue intégrée pour indiquer la classe de base.

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

J'ai utilisé les réponses OP comme base, mais j'ai développé un peu et incorporé la réponse à la question dans ma solution.

Cela semble être une question assez communément posée ici sur SO et comme je n’ai pas trouvé de réponse complète, j’ai donc pensé qu’il serait peut-être utile de partager ma solution de travail.

Je charge mes ressources depuis une base de données et je les ai mises en cache dans le cache par défaut (System.Web.Caching.Cache).

J'ai fini par créer une CacheDependency personnalisée sur la clé que j'utilise pour rechercher la ressource. De cette façon, chaque fois que mon autre code invalide ce cache (sur une édition, etc.), la dépendance du cache sur cette clé est supprimée et le VirtualPathProvider invalide à son tour son cache et le VirtualFile est rechargé.

J'ai également modifié le code pour qu'il prenne automatiquement l'instruction héritée, de sorte qu'il n'ait pas besoin d'être stocké dans la base de données. J'ajoute également automatiquement quelques instructions par défaut car cette "vue" n'est pas chargée via les canaux normaux. Tout élément inclus par défaut dans votre web.config ou viewstart n'est pas utilisable.

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

J'espère que cela t'aides!

6
Max Schilling

Je me suis beaucoup appuyé sur les informations du PO ainsi que sur la réponse de Darin Dimitrov pour créer un prototype simple pour le partage de composants MVC entre projets. Bien que ceux-ci aient été très utiles, j'ai néanmoins rencontré quelques obstacles supplémentaires abordés dans le prototype, tels que l'utilisation de vues partagées avec @ model.

0
kmkemp