2016-10-20 58 views
1

我需要在數據庫中保留我的翻譯,以便用戶可以添加,刪除和更改它們。我將所有翻譯都放在一個帶有複合主鍵(variableName,culture)的表格中,其中variableName只是一些文本的名稱,可以有多個翻譯,並且它們對應於字符串的文化,如「en-US」 。因此,例如,我有一個變量「submitLogin」,它在登錄按鈕上顯示,我的數據庫中有三種語言:英語,德語和波蘭語。 (「submitLogin」,「en-US」,「英文翻譯),(」submitLogin「,」de-DE「,」德文翻譯「)和(」submitLogin「 」,‘PL-PL’,‘波蘭文翻譯’)MVC - 使用數據庫中的錯誤消息驗證屬性

到目前爲止,我的應用程序已經基於一類Resources.cs其中包含從數據庫中的所有翻譯變量,如:

public static string buttonContinueShopping { 
    get { 
     return (string) resourceProvider.GetResource("buttonContinueShopping", CultureInfo.CurrentUICulture.Name); 
    } 
} 

在意見我使用這些靜態屬性來獲得我的翻譯是這樣的:

@Resources.buttonContinueShopping 

我可以創建一個動態類型,它會在視圖中的行爲方式完全相同(除了沒有靜態屬性,但我可以在每個視圖中創建一個對象,這不是問題 - 儘管看起來不錯不):

public class Resource : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     ResourceManager rsManager = new ResourceManager(); 
     result = rsManager.GetString(binder.Name); 
     return true; 
    } 
} 

,但我有一個問題我的模特的屬性。到目前爲止,我已經使用他們這樣的:

[Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "errorRequired")] 
[DataType(DataType.EmailAddress, ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "errorWrongDataType")] 
[EmailAddress] 
[Display(Name = "nameEmail", ResourceType = typeof(Resources.Resources))] 
public string Email { get; set; } 

現在我必須擺脫我Resources.cs的運行控制檯程序從數據庫中讀取轉換變量的所有唯一值,並創建屬性之後,因爲它們產生(就像我上面展示的那個)。我不能再有這個文件了,因爲用戶可以在運行時添加新的翻譯變量。

如何儘可能少地改變並使這些屬性讀取數據庫中的錯誤消息,顯示名稱等?

我有三個想法,但我不知道如何讓他們做:

  1. 使用自定義屬性 - 我試了必需的特性,但它只是不添加任何客戶端驗證,也不HTML中的任何錯誤消息。

  2. 使用自定義DataAnnotationsModelMetadataProvider - 我試過,但它不會重新加載頁面後工作 - 在第一頁加載存在的所有錯誤(尤其是這樣的:「字段的電子郵件是必需的。」),但重新加載頁面後,需要錯誤消息更改爲'字段是必需的'。這是我做:

    public class CustomDataAnnotationsProvider: DataAnnotationsModelMetadataProvider 
    { 
    private ResourceManager resourceManager = new ResourceManager(); 
    
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
    { 
        string key = string.Empty; 
        string localizedValue = string.Empty; 
    
    foreach (var attr in attributes) 
    { 
        if (attr != null) 
        { 
          if (attr is DisplayAttribute) 
          { 
           key = ((DisplayAttribute)attr).Name; 
           if (!string.IsNullOrEmpty(key) && !key.Contains(" ")) 
           { 
            localizedValue = resourceManager.GetString(key); 
            ((DisplayAttribute)attr).Name = localizedValue; 
           } 
          } 
          else if (attr is ValidationAttribute || attr is RequiredAttribute) 
          { 
           key = ((ValidationAttribute)attr).ErrorMessage; 
           if (!string.IsNullOrEmpty(key) && !key.Contains(" ")) 
           { 
            localizedValue = resourceManager.GetString(key); 
            ((ValidationAttribute)attr).ErrorMessage = localizedValue; 
           } 
          } 
        } 
    } 
    return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 
    

    }

Global.asax中:

ModelMetadataProviders.Current = new CustomDataAnnotationsProvider(); 

型號:

[Required(ErrorMessage = "errorRequired")] 
[EmailAddress(ErrorMessage = "errorWrongDataType")] 
[Display(Name = "nameEmail")] 
public string Email { get; set; } 
  • 使用反射(將是最好的但我不知道該怎麼做)。比方說,我離開我的屬性,並從我的Resources.cs中刪除所有屬性。現在RequiredAttribute是做什麼的?它需要給定的類型並獲得給定的屬性,例如它試圖做到這一點:

    Resources.Resources.nameEmail.get 
    
  • 的問題是:是否有可能寫一些反射代碼,將採取非現有屬性(如nameEmail)「請求」照顧?

    回答

    2

    我認爲答案是在Resources.cs file中提供默認值。雖然用戶可以動態地提供翻譯,但您的應用程序無法使用它們,除非他們擁有您在模型中使用的密鑰。

    如果您修改現有的方法來接受默認值,那麼你可以返回這一點,如果沒有一個數據庫值存在:

    public static string buttonContinueShopping { 
        get { 
         return GetResource("buttonContinueShopping", 
          CultureInfo.CurrentUICulture.Name, "Continue Shopping"); 
        } 
    } 
    
    
    public string GetResource(string key, string cultureName, string defaultText) 
    { 
        // Get db value 
        if (dbValue != null) 
         return dbValue; 
    
        return defaultText; 
    } 
    

    您可以通過手動修改它們Resources.cs其中IMO是控制鍵這是他們最好的地方,因爲他們在同一個項目中維護,因爲他們正在使用。你可以(並且我已經使用了這種技術)然後編寫一個伴隨控制檯應用程序,它可以使用反射來生成更新數據庫所需的sql。

    這個例子是從我的項目採取直接,但你可以得到的想法

    static void Main(string[] args) 
        { 
         var resources = typeof(Resources).GetProperties(); 
    
         StreamWriter streamWriter = new StreamWriter(new FileStream(@"..\..\Resources.sql", FileMode.Create)); 
    
         streamWriter.WriteLine(createTable); 
    
    
         for (int i = 0; i < resources.Count(); i++) 
         { 
          var line = GetValues(resources[i].Name, resources[i].GetValue(null, null) as string); 
    
          if (i == 0) 
          { 
           streamWriter.Write(insertValues + line); 
          } 
          else if (i == resources.Count() - 1) 
          { 
           streamWriter.Write(",\r\n" + line + "\r\nGO"); 
          } 
          else if (i % 4 == 0) 
          { 
           streamWriter.Write("\r\nGO\r\n\r\n" + insertValues + line); 
          }     
          else 
          { 
           streamWriter.Write(",\r\n" + line); 
          }     
         } 
    
         streamWriter.Flush(); 
         streamWriter.Close(); 
    
    
        } 
    
        private static string createTable = "IF NOT EXISTS (SELECT * FROM sys.objects where Object_Id = OBJECT_ID(tempdb..#Resources))" 
         + "\r\n\tCREATE TABLE Resources (StaticTextKey VARCHAR(100), DefaultText VARCHAR(MAX))\r\nGO\r\n"; 
    
        private static string insertValues = "INSERT INTO #Resources (StaticTextKey, DefaultText) VALUES\r\n"; 
    
        private static string GetValues(string staticTextKey, string defaultText) 
        { 
         return string.Format("('{0}', '{1}')", staticTextKey, defaultText.Replace("'", "''")); 
        }