首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

VirtualPathProvider 註冊限制與外部 DLL 自動載入解決方案

     編輯:關於ASP.NET

VirtualPathProvider 允許從非檔案系統來源 (資料庫、嵌入資源、壓縮檔) 提供虛擬路徑檔案。但 MSDN 限定只能在兩處註冊:App_Code 類別的 AppInitialize 靜態方法、Global.asax Application_Start。本文探討如何從外部 DLL 自動註冊。

官方註冊位置限制

// 位置 1: App_Code 任意類別
public class VppRegistrar {
    public static void AppInitialize() {
        HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
    }
}

// 位置 2: Global.asax
protected void Application_Start() {
    HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
}

為何外部 DLL 無法直接使用 AppInitialize

  • AppInitialize 僅掃描 App_Code 目錄下原始碼檔 (.cs/.vb) 編譯產生的類別
  • 外部 DLL 已編譯完成,不在 App_Code 動態編譯範圍內
  • App_Code 有特殊編譯行為:單獨組件、支援多語言、自動觸發 AppInitialize

方案一:Web.config 設定 PreApplicationStartMethod (ASP.NET 4.0+)

// 外部 DLL 組件層級屬性 (AssemblyInfo.cs)
[assembly: PreApplicationStartMethod(typeof(MyVppModule), "Initialize")]

namespace MyExternalLib {
    public static class MyVppModule {
        public static void Initialize() {
            HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
        }
    }
}

web.config 無需額外設定,ASP.NET 4.0+ 啟動時自動掃描所有參考組件的 PreApplicationStartMethod。

方案二:WebActivatorEx 套件 (相容 ASP.NET 3.5+)

// 安裝 NuGet: Install-Package WebActivatorEx

// 外部 DLL
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(MyVppModule), "Start")]

namespace MyExternalLib {
    public static class MyVppModule {
        public static void Start() {
            HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
        }
    }
}

WebActivatorEx 透過 HttpModule 在應用程式啟動早期執行註冊方法,廣泛用於套件自動初始化。

方案三:自訂 HttpModule 註冊 (通用)

public class VppRegistrationModule : IHttpModule {
    public void Init(HttpApplication context) {
        // 在首次請求前註冊 (需確保早於任何虛擬路徑存取)
        HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
    }
    public void Dispose() { }
}

web.config 註冊:

<system.webServer>
  <modules>
    <add name="VppRegistration" type="MyExternalLib.VppRegistrationModule, MyExternalLib" />
  </modules>
</system.webServer>

方案比較

方案支援版本優點缺點
PreApplicationStartMethod4.0+官方標準、無額外依賴、最早執行不支援 3.5
WebActivatorEx3.5+相容性好、廣泛採用、支援 PostStart/Shutdown需額外 NuGet 套件
HttpModule2.0+完全控制、無框架限制執行時機較晚、需小心競態

完整 VirtualPathProvider 實作範例

public class EmbeddedResourceVpp : VirtualPathProvider {
    private readonly Assembly _resourceAssembly;
    private readonly string _resourceNamespace;
    
    public EmbeddedResourceVpp(Assembly assembly, string resourceNamespace) {
        _resourceAssembly = assembly;
        _resourceNamespace = resourceNamespace;
    }
    
    public override bool FileExists(string virtualPath) {
        string resourceName = GetResourceName(virtualPath);
        return _resourceAssembly.GetManifestResourceStream(resourceName) != null 
            || base.FileExists(virtualPath);
    }
    
    public override VirtualFile GetFile(string virtualPath) {
        string resourceName = GetResourceName(virtualPath);
        var stream = _resourceAssembly.GetManifestResourceStream(resourceName);
        return stream != null ? new EmbeddedVirtualFile(virtualPath, stream) 
            : base.GetFile(virtualPath);
    }
    
    public override CacheDependency GetCacheDependency(string virtualPath, 
        IEnumerable virtualPathDependencies, DateTime utcStart) {
        // 嵌入資源不變動,回傳 null 表示永不過期
        return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
    }
    
    private string GetResourceName(string virtualPath) {
        // ~/Views/Shared/_Layout.cshtml → MyLib.Views.Shared._Layout.cshtml
        return _resourceNamespace + virtualPath.Replace("~/", "").Replace("/", ".");
    }
}

public class EmbeddedVirtualFile : VirtualFile {
    private readonly Stream _stream;
    public EmbeddedVirtualFile(string virtualPath, Stream stream) : base(virtualPath) {
        _stream = stream;
    }
    public override Stream Open() => _stream;
}

應用場景:跨專案共用 MasterPage/View

參考:透過 DLL 共用 Master Pages。將 .master/.aspx 設為嵌入資源,透過 VPP 提供,多個 MVC 專案引用同一 DLL 即可共用版面。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved