36

Ho scritto implementazioni VirtualFile e VirtualPathProvider personalizzate che stanno ottenendo risorse incorporate che sono Viste parziali.Utilizzo di VirtualPathProvider personalizzato per caricare la risorsa incorporata Viste parziali

Tuttavia, quando tento di renderli produce questo errore:

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

Quando il rendering del vista parziale, all'interno di un normale vista, sembra che il seguente:

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

Che cosa sta inducendo a credere che questa non sia una visione parziale?

MODIFICA: Ho inserito il mio codice per entrambi i file virtuali & implementazioni di provider di file virtuali per chiunque si imbatta in questa ricerca di soluzioni per far funzionare quel componente. Questa domanda servirà anche per quelli basati sul titolo della domanda.

ere è l'implementazione VirtualFile di riferimento:

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

E, naturalmente, non dimenticate di registrare questo nuovo provider nel file Global.asax del progetto nell'evento Application_Start()

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

risposta

44

Perché ora siete servi Le tue visualizzazioni da qualche luogo sconosciuto non sono più il file ~/Views/web.config che si applica e indica la classe base per le tue visualizzazioni del rasoio (<pages pageBaseType="System.Web.Mvc.WebViewPage">). Quindi potresti aggiungere una direttiva @inherits nella parte superiore di ogni vista incorporata per indicare la classe base.

@inherits System.Web.Mvc.WebViewPage 
@model ... 
+0

Grazie mille. Il mio unico rimpianto è che ho solo un voto da dare. = P Up Vota questa risposta –

+3

@ darin Dimitrov Non credo che tu sappia di un trucco che potrei impiegare per incoraggiare l'intellisense a trattare questo come una vista quando dentro la mia DLL? In questo momento, ho molto poco supporto intellisense con le visualizzazioni che ho inserito nella mia DLL –

+0

@ MatthewCox, non ne so molto di Intellisense nelle visualizzazioni. È passato molto tempo da quando ho smesso di fare affidamento su di esso. Non ottieni Intellisense se il file ha l'estensione '.cshtml'? –

6

Ho usato la risposta OP come base, ma ho espanso un po 'e incorporato la risposta alla domanda nella mia soluzione.

Questa sembra una domanda un po 'più frequente qui su SO e non ho visto una risposta completa quindi ho pensato che potrebbe essere utile condividere la mia soluzione di lavoro.

Carico le mie risorse da un database e li ho memorizzati nella cache predefinita (System.Web.Caching.Cache).

Quello che ho finito è stato creare una CacheDependency personalizzata sulla KEY che sto usando per cercare la risorsa. In questo modo, ogni volta che il mio altro codice invalida quella cache (su una modifica, ecc.) La dipendenza della cache su quella chiave viene rimossa e il VirtualPathProvider a sua volta invalida la sua cache e il VirtualFile viene ricaricato.

Ho anche modificato il codice in modo che prepari automaticamente l'istruzione inherits in modo che non debba essere archiviato nella risorsa del mio database e aggiungo automaticamente alcune istruzioni di default utilizzando questa "vista" non caricata tramite canali normali, quindi qualsiasi cosa di default include nel tuo web.config o viewstart non sono utilizzabili.

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

Spero che questo aiuti!

0

Mi sono appoggiata pesantemente alle informazioni nell'OP e alla risposta di Darin Dimitrov per creare un simple prototype per condividere i componenti MVC tra i progetti. Mentre questi sono stati di grande aiuto, mi sono imbattuto in alcune barriere aggiuntive che sono state affrontate nel prototipo come l'utilizzo di viste condivise con i modelli @.