36

Napisałem niestandardowe implementacje VirtualFile i VirtualPathProvider, które pomyślnie uzyskują osadzone zasoby, które są częściowymi widokami.Używanie niestandardowej usługi VirtualPathProvider do ładowania zasobu osadzonego Częściowe widoki

Jednak, gdy próbuję je uczynić go produkuje ten błąd:

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

Podczas renderowania częściowego widoku wewnątrz regularnego widzenia, wygląda następująco:

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

Co powoduje, że nie jest to częściowy widok?

EDYCJA: Wysłałem mój kod do wirtualnego pliku & implementacji wirtualnych dostawców plików dla każdego, kto natknął się na to, szukając rozwiązania na temat działania tego składnika. To pytanie będzie również dobrze służyć osobom bazującym na tytule pytania.

ere jest realizacja VirtualFile dla odniesienia:

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

I oczywiście, nie zapomnij, aby zarejestrować nowego dostawcę w pliku Global.asax swojego projektu w Application_Start() zdarzenie

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

Odpowiedz

44

Bo teraz jesteś servi W przypadku twoich widoków z nieznanej lokalizacji nie ma już pliku ~/Views/web.config, który ma zastosowanie i wskazuje klasę podstawową dla widoków maszynki do golenia (<pages pageBaseType="System.Web.Mvc.WebViewPage">). Aby dodać klasę podstawową, możesz dodać dyrektywę @inherits u góry każdego osadzonego widoku.

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

Dziękuję bardzo. Żałuję tylko, że mam tylko jeden głos do oddania. = P Up Głosuj na tę odpowiedź –

+3

@ darin Dimitrov Nie sądzę, że znasz sztuczkę, którą mógłbym użyć, aby zachęcić intellisense do potraktowania tego jako widoku w mojej bibliotece dll? W tej chwili mam bardzo mało wsparcia intellisense z widokami, które umieściłem w moim dll –

+0

@MatthewCox, nie wiem zbyt wiele o Intellisense w poglądach. Dawno temu przestałem polegać na tym. Czy nie otrzymujesz Intellisense, jeśli plik ma rozszerzenie '.cshtml'? –

6

Użyłem odpowiedzi OP jako podstawy, ale trochę ją rozszerzyłem i włączyłem odpowiedź na pytanie w moim rozwiązaniu.

Wydaje się, że jest to dość powszechnie zadawane pytanie na stronie SO i nie widziałem pełnej odpowiedzi, więc pomyślałem, że może mi się przydać udostępnienie mojego działającego rozwiązania.

Załaduję moje zasoby z bazy danych i mam je buforowane w domyślnej pamięci podręcznej (System.Web.Caching.Cache).

To, co udało mi się zrobić, to utworzenie niestandardowej pamięci podręcznej na klucz, którego używam do wyszukiwania zasobu. W ten sposób, gdy mój drugi kod unieważni tę pamięć podręczną (w edycji itp.), Zależność od pamięci podręcznej tego klucza zostanie usunięta, a VirtualPathProvider z kolei unieważni jego pamięć podręczną, a plik VirtualFile zostanie przeładowany.

Zmieniłem także kod tak, aby automatycznie dziedziczył instrukcję tak, że nie musi być przechowywany w moim zasobie bazy danych, a także automatycznie dodaje kilka domyślnych instrukcji użycia, ponieważ ten "widok" nie jest ładowany przez normalne kanały, więc wszystko, co domyślnie znajduje się w twoim pliku web.config lub viewstart, nie nadaje się do użytku.

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

Nadzieja to pomaga!

0

Opierałem się mocno na informacjach z PO, a także na odpowiedzi Darina Dymitrowa, aby utworzyć simple prototype do udostępniania komponentów MVC w różnych projektach. Chociaż były one bardzo pomocne, nadal natrafiałem na kilka dodatkowych barier, które są poruszane w prototypie, jak używanie wspólnych widoków z modelami @.

Powiązane problemy