2011-01-10 86 views
5

我正在研究一個我需要本地化和國際化的Web應用程序。我想到我可以使用依賴注入框架來實現這一點。比方說,我宣佈一個接口ILocalResources(使用C#在這個例子中,但是這並不重要):使用DI框架進行本地化 - 好主意?

interface ILocalResources { 
    public string OkString { get; } 
    public string CancelString { get; } 
    public string WrongPasswordString { get; } 
    ... 
} 

創建此接口,一個爲我需要支持的每種語言的實現。然後,我會設置我的DI框架來實例化正確的實現,無論是靜態還是動態(例如基於請求的瀏覽器首選語言)。

是有一些原因,我不應該使用這樣的事情一個DI框架?我發現自己唯一的反對意見是它可能有點矯枉過正,但是如果我在我的web應用程序中使用DI框架,我還可以將其用於國際化?

回答

2

如果您不能使用現有的資源框架(像內置ASP.Net),並就必須建立自己的,我會認爲你在某些時候會需要公開提供本地化的資源服務。

DI框架用於處理服務實例化。您的本地化框架將公開提供本地化服務。爲什麼不應該由框架提供該服務?

不使用DI在這裏它的目的好像是說,「我建立一個CRM應用程序,但不能使用DI DI因爲沒有內置的客戶關係管理」。

所以,是的,如果你已經在你的應用程序的其他部分使用DI,IMO那就錯了不使用它來處理本地化的服務。

2

唯一的缺點我能看到的是,對於任何更新,「資源」,你就必須重新編譯包含資源的程序集。根據你的項目,這個缺點可能是一個很好的建議,只使用DI框架來解析某種ResourceService,而不是值本身。

6

一個DI框架是爲依賴注入而構建的,本地化可能只是您的服務之一,所以在這種情況下,沒有理由使用DI框架IMO來執行而不是。也許我們應該開始討論提供的ILocalResources接口。儘管我支持編譯時支持,但我不確定提供的接口是否會對您有所幫助,因爲該接口可能是您的系統中將改變最多的類型。並用該接口實現它的類型。也許你應該採用不同的設計。

當我們看看大多數本地化框架/供應商/工廠(或其他),他們是基於所有的字符串。因此,請考慮以下設計:

public interface ILocalResources 
{ 
    string GetStringResource(string key); 
    string GetStringResource(string key, CultureInfo culture); 
} 

這將允許您將密鑰和文化添加到基礎消息數據存儲,而不更改接口。當然,下滑當然不應該改變一個關鍵,因爲那可能是一個地獄。

另一種方法可能是一個抽象基類:

public abstract class LocalResources 
{ 
    public string OkMessage { get { return this.GetString("OK"); } } 
    public string CancelMessage { get { return this.GetString("Cancel"); } } 
    ... 

    protected abstract string GetStringResource(string key, 
     CultureInfo culture); 

    private string GetString(string key) 
    { 
     Culture culture = CultureInfo.CurrentCulture; 

     string resource = GetStringResource(key, culture); 

     // When the resource is not found, fall back to the neutral culture. 
     while (resource == null && culture != CultureInfo.InvariantCulture) 
     { 
      culture = culture.Parent; 
      resource = this.GetStringResource(key, culture); 
     } 

     if (resource == null) throw new KeyNotFoundException(key); 

     return resource; 
    } 
} 

而實現這種類型的看起來是這樣的:

public sealed class SqlLocalResources : LocalResources 
{ 
    protected override string GetStringResource(string key, 
     CultureInfo culture) 
    { 
     using (var db = new LocalResourcesContext()) 
     { 
      return (
       from resource in db.StringResources 
       where resource.Culture == culture.Name 
       where resource.Key == key 
       select resource.Value).FirstOrDefault(); 
     } 
    } 
} 

此方法採用兩全其美的,因爲鑰匙贏得」通過應用程序分散,並添加新的屬性只需要在一個地方完成。使用您favorite DI庫,你可以註冊一個這樣的實現:

container.RegisterSingleton<LocalResources>(new SqlLocalResources()); 

而且由於LocalResources型有且只有一個,做所有的工作抽象方法,很容易創建一個裝飾,增加了緩存,從而避免請求

public sealed class CachedLocalResources : LocalResources 
{ 
    private readonly Dictionary<CultureInfo, Dictionary<string, string>> cache = 
     new Dictionary<CultureInfo, Dictionary<string, string>>(); 
    private readonly LocalResources decoratee; 

    public CachedLocalResources(LocalResources decoratee) { this.decoratee = decoratee; } 

    protected override string GetStringResource(string key, CultureInfo culture) { 
     lock (this.cache) { 
      string res; 
      var cultureCache = this.GetCultureCache(culture); 
      if (!cultureCache.TryGetValue(key, out res)) { 
       cultureCache[key] = res= this.decoratee.GetStringResource(key, culture); 
      }     
      return res; 
     } 
    } 

    private Dictionary<string, string> GetCultureCache(CultureInfo culture) { 
     Dictionary<string, string> cultureCache; 
     if (!this.cache.TryGetValue(culture, out cultureCache)) { 
      this.cache[culture] = cultureCache = new Dictionary<string, string>(); 
     } 
     return cultureCache; 
    } 
} 

您可以將裝飾如下:從數據庫中相同的數據

container.RegisterSingleton<LocalResources>(
    new CachedLocalResources(new SqlLocalResources())); 

不要e這個裝飾器會無限期地緩存字符串資源,這可能會導致內存泄漏,所以你希望包裝WeakReference實例中的字符串,或者在它上面有某種過期超時。但想法是,您可以應用緩存而無需更改任何現有的實現。

我希望這會有所幫助。

+0

另外,你也可以實現`ILocalResources`接口的擴展方法,但缺點是你將有方法而不是屬性。但那不會那麼糟糕。 – Steven 2011-01-10 15:48:07