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

ASP.NET 自訂資料庫驅動本地化資源提供者(DbResourceProvider)完整實作

     編輯:ASP.NET 基礎

動機

預設 .resx 檔案式資源管理在多伺服器、多語系、頻繁更新文字時維護困難。將資源集中存放於 資料庫,並實作 ResourceProviderFactory、IResourceProvider、IResourceReader 三大介面,即可實現:

  • 後台管理介面即時編輯多語系文字
  • 無需重新部署即可生效
  • 支援回退機制(zh-TW → zh → invariant)
  • 支援設計階段(Visual Studio 編輯器智慧感知)

一、Web.config 註冊

<configuration>
  <system.web>
    <globalization 
      resourceProviderFactoryType="Westwind.Globalization.DbSimpleResourceProviderFactory, Westwind.Globalization" 
    />
  </system.web>
</configuration>

二、三大核心類別實作

1. 工廠類別:DbSimpleResourceProviderFactory

[DesignTimeResourceProviderFactoryAttribute(typeof(DbDesignTimeResourceProviderFactory))]
public class DbSimpleResourceProviderFactory : ResourceProviderFactory
{
    public override IResourceProvider CreateGlobalResourceProvider(string className)
    {
        return new DbSimpleResourceProvider(null, className);
    }

    public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
    {
        // 將虛擬路徑轉為資源集合名稱,如 "~/Pages/About.aspx" → "pages.about"
        var resourceSet = DbResourceConfiguration.Current.StripVirtualPath(virtualPath).ToLower();
        return new DbSimpleResourceProvider(null, resourceSet);
    }
}

2. 提供者類別:DbSimpleResourceProvider

public class DbSimpleResourceProvider : IResourceProvider, IImplicitResourceProvider
{
    private readonly string _resourceSetName;
    private readonly ListDictionary _resourceCache = new ListDictionary(); // culture → IDictionary

    public DbSimpleResourceProvider(string virtualPath, string className)
    {
        _resourceSetName = className;
    }

    // 取得指定文化的資源字典(含快取)
    private IDictionary GetResourceCache(string cultureName)
    {
        cultureName ??= "";
        if (_resourceCache[cultureName] is IDictionary dict) return dict;

        // 從資料庫載入
        var mgr = new DbResourceDataManager();
        dict = mgr.GetResourceSet(cultureName, _resourceSetName);
        _resourceCache[cultureName] = dict;
        return dict;
    }

    public void ClearResourceCache() => _resourceCache.Clear();

    // 核心:取得單一資源值(含回退邏輯)
    object IResourceProvider.GetObject(string resourceKey, CultureInfo culture)
    {
        var cultureName = culture?.Name ?? CultureInfo.CurrentUICulture.Name;
        return GetObjectInternal(resourceKey, cultureName);
    }

    private object GetObjectInternal(string key, string cultureName)
    {
        var dict = GetResourceCache(cultureName);
        object val = dict?[key];

        // 1. 具體文化無值 → 回退到中性文化
        if (val == null && cultureName.Length > 3)
            return GetObjectInternal(key, cultureName.Substring(0, 2));

        // 2. 仍無值 → 回退到不變文化
        if (val == null)
        {
            dict = GetResourceCache("");
            val = dict?[key];
        }

        // 3. 仍無值 → 可選:自動在資料庫建立空白鍵值,避免下次再查
        if (val == null && string.IsNullOrEmpty(cultureName) && DbResourceConfiguration.Current.AddMissingResources)
        {
            new DbResourceDataManager().AddResource(key, "", "", _resourceSetName);
            val = "";
        }
        return val ?? "";
    }

    // 隱性資源(如 meta:resourcekey)讀取
    public IDictionary GetImplicitResourceDictionary(CultureInfo culture)
    {
        return GetResourceCache(culture?.Name ?? "");
    }

    // 提供設計階段列舉用
    public IResourceReader ResourceReader => new DbSimpleResourceReader(GetResourceCache(""));
}

3. 讀取器類別:DbSimpleResourceReader

public class DbSimpleResourceReader : IResourceReader
{
    private readonly IDictionary _resources;
    public DbSimpleResourceReader(IDictionary resources) => _resources = resources;

    public IDictionaryEnumerator GetEnumerator() => _resources.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => _resources.GetEnumerator();
    public void Close() { }
    public void Dispose() { }
}

三、資料庫結構建議

CREATE TABLE LocalizationResources (
    ResourceSet   NVARCHAR(256) NOT NULL,  -- 對應 className / virtualPath
    CultureCode   NVARCHAR(10)  NOT NULL,  -- 'zh-TW', 'zh', 'en-US', ''(invariant)
    ResourceKey   NVARCHAR(256) NOT NULL,
    ResourceValue NVARCHAR(MAX) NULL,
    CONSTRAINT PK_LocalizationResources PRIMARY KEY (ResourceSet, CultureCode, ResourceKey)
);

四、DbResourceDataManager 關鍵方法簽名

public class DbResourceDataManager
{
    // 取得某資源集合在指定文化下的所有鍵值
    public IDictionary GetResourceSet(string culture, string resourceSet) { /* ... */ }

    // 新增/更新單一資源
    public void AddResource(string key, string value, string culture, string resourceSet) { /* ... */ }
}

五、設計階段支援(可選)

實作 DbDesignTimeResourceProviderFactory 繼承 DesignTimeResourceProviderFactoryAttribute,即可在 Visual Studio 編輯 .aspx/.ascx 時自動從資料庫讀取資源鍵值,提供 IntelliSense。


六、部署與效能提醒

  • 快取失效:後台編輯資源後,呼叫 provider.ClearResourceCache() 或重啟應用程式集區。
  • 分散式部署:多台 Web 伺服器時,建議改用 SqlCacheDependency 或外部訊息佇列(Redis Pub/Sub)通知各節點清除快取。
  • 回退順序:zh-TW → zh → invariant 符合 .NET 標準文化回退規則。
將本地化資源搬進資料庫,讓「改字」不再需要開發人員介入,是國際化專案邁向 DevOps 的關鍵一步。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved