36

Tôi đã viết các triển khai VirtualFile và VirtualPathProvider tùy chỉnh đang thu thập thành công tài nguyên được nhúng là Chế độ xem một phần.Sử dụng VirtualPathProvider tùy chỉnh để tải tài nguyên nhúng Một phần Chế độ xem

Tuy nhiên, khi tôi cố gắng để làm cho chúng nó tạo ra lỗi này:

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

Khi render quan điểm một phần, bên trong một Xem thông thường, nó trông giống như sau:

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

Điều gì khiến nó tin rằng đây không phải là một phần xem?

CHỈNH SỬA: Tôi đã đăng mã cho cả tệp ảo & triển khai nhà cung cấp tệp ảo cho bất kỳ ai gặp phải vấn đề này khi tìm giải pháp đó. Câu hỏi này cũng sẽ phục vụ tốt cho những người dựa trên tiêu đề câu hỏi.

ere là việc thực hiện VirtualFile để tham khảo:

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

Và tất nhiên, đừng quên đăng ký cung cấp dịch vụ mới này trong file Global.asax của dự án của bạn trong sự kiện Application_Start()

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

Trả lời

44

Vì bây giờ bạn là servi ng quan điểm của bạn từ một số vị trí không xác định không còn là tệp ~/Views/web.config áp dụng và cho biết lớp cơ sở cho chế độ xem dao cạo của bạn (<pages pageBaseType="System.Web.Mvc.WebViewPage">). Vì vậy, bạn có thể thêm chỉ thị @inherits ở đầu mỗi chế độ xem được nhúng để biểu thị lớp cơ sở.

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

Cảm ơn bạn rất nhiều. Niềm tiếc duy nhất của tôi là tôi có nhưng một người bỏ phiếu để đưa ra. = P Up Bình chọn câu trả lời này –

+3

@ darin Dimitrov Tôi không cho rằng bạn biết một mẹo mà tôi có thể sử dụng để khuyến khích intellisense coi đây là một Xem khi bên trong dll của tôi? Ngay bây giờ, tôi có rất ít hỗ trợ intellisense với quan điểm mà tôi đặt trong dll của tôi –

+0

@MatthewCox, tôi không biết nhiều về Intellisense trong quan điểm. Đã lâu rồi tôi không còn dựa vào nó nữa. Bạn không nhận được Intellisense nếu tập tin có phần mở rộng '.cshtml'? –

6

Tôi đã sử dụng câu trả lời OP làm cơ sở nhưng mở rộng một chút và kết hợp câu trả lời cho câu hỏi trong giải pháp của tôi.

Điều này có vẻ giống như một câu hỏi thường gặp ở đây trên SO và tôi chưa thấy câu trả lời hoàn chỉnh vì vậy tôi nghĩ có thể hữu ích khi chia sẻ giải pháp làm việc của tôi.

Tôi tải tài nguyên của mình từ cơ sở dữ liệu và tôi đã lưu chúng trong bộ đệm ẩn mặc định (System.Web.Caching.Cache).

Điều tôi đã làm cuối cùng là tạo CacheDependency tùy chỉnh trên KEY mà tôi đang sử dụng để tra cứu tài nguyên. Bằng cách đó, bất cứ khi nào mã khác của tôi vô hiệu hóa bộ nhớ cache đó (trên chỉnh sửa, v.v.), phụ thuộc vào bộ nhớ đệm trên khóa đó sẽ bị loại bỏ và VirtualPathProvider sẽ vô hiệu hóa bộ nhớ cache của nó và VirtualFile sẽ được tải lại.

Tôi cũng đã thay đổi mã để nó tự động thêm trước câu lệnh thừa kế để nó không cần lưu trữ trong tài nguyên cơ sở dữ liệu của tôi và tôi cũng tự động thêm một vài câu lệnh mặc định bằng cách sử dụng "view" này các kênh thông thường, vì vậy mọi thứ mặc định bao gồm trong web.config hoặc khung nhìn của bạn không thể sử dụng được.

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

Hope this helps!

0

Tôi dựa nhiều vào thông tin trong OP cũng như câu trả lời của Darin Dimitrov để tạo một simple prototype để chia sẻ các thành phần MVC trong các dự án. Mặc dù chúng rất hữu ích nhưng tôi vẫn gặp phải một vài rào cản bổ sung được giải quyết trong nguyên mẫu như sử dụng lượt xem được chia sẻ với mô hình của @.

Các vấn đề liên quan