asp.net-mvc - MVC - 使用数据库中的错误消息验证属性

标签 asp.net-mvc localization custom-attributes

我需要将我的翻译保存在数据库中,以便用户可以添加、删除和更改它们。我将所有翻译保存在一个具有复合主键(variableName,culture)的表中,其中variableName只是一些可以有多个翻译的文本的名称,它们对应于作为字符串的文化,例如“en-US” 。例如,我有一个变量“submitLogin”,我将其显示在登录按钮上,并且我的数据库中有三种语言:英语、德语和波兰语。这意味着我在表格中为该特定文本保留三行:(“submitLogin”、“en-US”、“英语翻译”)、(“submitLogin”、“de-DE”、“德语翻译”)和(“submitLogin” ”、“pl-PL”、“波兰语翻译”)。

到目前为止,我的应用程序基于类 Resources.cs,其中包含数据库中的所有翻译变量,例如:

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

在 View 中,我使用这些静态属性来获取如下翻译:

@Resources.buttonContinueShopping

我可以创建一个动态类型,它在 View 中的行为方式完全相同(除了没有静态属性,但我可以在每个 View 上创建一个对象,这不是问题 - 尽管它看起来不太好):

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

    }

全局.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)?

    最佳答案

    我认为答案是在您的 Resources.cs 文件 中提供默认值。虽然用户可以动态提供翻译,但您的应用程序无法使用它们,除非他们拥有您在模型中使用的 key 。

    如果您修改现有方法以接受默认值,那么如果不存在数据库值,则可以返回此值:

    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 中手动修改 key 来控制 key ,IMO 是它们的最佳位置,因为它们在使用时维护在同一项目中。然后,您可以(我已经使用了这种技术)编写一个配套的控制台应用程序,该应用程序可以使用反射来生成更新数据库所需的 sql。

    这个例子直接取 self 的项目,但你可以明白这个想法

        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("'", "''"));
        }
    

    关于asp.net-mvc - MVC - 使用数据库中的错误消息验证属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40151458/

    相关文章:

    javascript - C# 中的 HttpPostedFileBase docx 到字符串 - MVC

    asp.net-mvc - 如何设置 MVC 可移植区域以使用复杂目录结构中的资源?

    swift - NSLocalizedString 在 Swift 中不起作用

    jQuery - 我如何选择相等的 html5 自定义属性

    c# - 更新实体时序列不包含元素

    c# - IIS 抛出 ThreadAbortException 并回收 worker,IIS 日志记录为 "IIS configuration change"的可能原因是什么?

    Django 1.8 set_language View 不重定向到指定语言

    java - 比较看起来相同但不相同的本地化字符串的更好方法是什么

    asp.net-mvc - 如何测试方法参数是否用属性修饰?

    C# MVC 在 OR 条件下使用多个属性