【问题标题】:Is it wrong to dynamically add "data-val" and "data-val-required" in the View?在 View 中动态添加“data-val”和“data-val-required”有错吗?
【发布时间】:2013-11-18 21:45:54
【问题描述】:

我有一个可以用[Required] 属性装饰的 ViewModel(见下文)。我已经到了需要让客户端控制哪些字段是必需的或不需要的地步。他们可以配置这个槽 XML,并且所有这些信息在第一次创建时都存储在模型中。现在我的字段没有用[Required] 装饰,但在提交之前仍需要验证(根据“用户设置”)(例如Phone 字段)。

public class MyBusinessObjectViewModel
{
    [Required]
    public string Email { get; set; } //compulsory

    public string Phone { get; set; } //not (yet) compulsory, but might become
}

如果用户不输入Phone 号码,数据仍将被发布。不想弄乱自定义验证器,我只是将“data-val”和“data-val-required”属性添加到 Html,如下所示:

Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("data-val", "true");
dict.Add("data-val-required", "This field is required.");
@Html.TextBoxFor(x => x, dict); 

这会强制客户端验证所有根据需要动态设置的属性。这是好习惯吗?我可以期待什么样的副作用?

【问题讨论】:

    标签: c# asp.net-mvc validation


    【解决方案1】:

    您应该考虑使用自己的metadata provider 扩展元模型框架,以在您的站点配置和模型元数据之间进行实际绑定。在元数据创建过程中,您实际上可以在属性模型元数据上将 required property 标志设置为 true。我无法确定这是否会导致内置编辑器模板生成属性,但我认为确实如此。在最坏的情况下,您实际上可以创建一个新的RequiredAttribute 并将其附加到该属性,这有点笨拙,但在某些情况下效果很好。

    您也可以使用IMetadataAware 属性来执行此操作,尤其是如果Required 是您的用户可以自定义的唯一元数据方面,但实现实际上取决于您要执行的操作。

    在您的特定情况下使用自定义 ModelMetadataProvider 的一个主要优点是您可以使用依赖注入(通过ModelMetadataProviders)将您的客户设置持久性机制纳入范围,而使用数据属性您只能编写一个隔离的创建元数据模型后立即运行的方法。

    这是自定义模型元数据提供程序的示例实现,您只需将客户端设置更改为您想要使用的任何设置。

    已更新但根本没有测试

    public class ClientSettingsProvider
    {
        public ClientSettingsProvider(/* db info */) { /* init */ }
    
        public bool IsPropertyRequired(string propertyIdentifier)
        {
           // check the property identifier here and return status
        }
    
    }
    
    public ClientRequiredAttribute : Attribute
    {
        string _identifier;
        public string Identifier { get { return _identifer; } }
        public ClientRequiredAttribute(string identifier)
        { _identifier = identifier; }
    }
    
    public class RequiredModelMetadataProvider : DataAnnotationsModelMetadataProvider
    {
        ClientSettings _clientSettings;
    
        public RequiredModelMetadataProvider(ClientSettings clientSettings)
        {
            _clientSettings = clientSettings;
        }
    
        protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
        {
            // alternatively here is where you could 'inject' a RequiredAttribute into the attributes list
    
            var clientRequiredAttribute = attributes.OfType<ClientRequiredAttribute>().SingleOrDefault();
            if(clientRequiredAttribute != null && _clientSettings.IsPropertyRequired(clientRequiredAttribute.Identifier))
            {
                // By injecting the Required attribute here it will seem to 
                // the base provider we are extending as if the property was
                // marked with [Required]. Your data validation attributes should
                // be added, provide you are using the default editor templates in
                // you view.
                attributes = attributes.Union(new [] { new RequiredAttribute() });
            }
    
            var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
    
            // REMOVED, this is another way but I'm not 100% sure it will add your attributes
            // Use whatever attributes you need here as parameters...
            //if (_clientSettings.IsPropertyRequired(containerType, propertyName))
            //{
            //    metadata.IsRequired = true;
            //}
    
            return metadata;
        }
    }
    

    用法

    public class MyModel
    {
         [ClientRequired("CompanyName")]
         public string Company { get; set; }
    }
    
    public class MyOtherModel
    {
         [ClientRequired("CompanyName")]
         public string Name { get; set; }
    
         public string Address { get; set; }
    }
    

    这两种模型都会根据您的客户端设置提供商验证字符串“CompanyName”。

    【讨论】:

    • 你好,保罗,谢谢你的回答。在过去的两天里,我一直在“玩”元数据并为我的 ViewModel 动态添加属性,所以这并不是我解决问题的第一选择。它似乎确实产生了可行的结果,但代价高昂。我会读进去的。
    • 查看修改,应该和直接添加参数差不多。
    • 目前正在测试。谢谢你的建议。我会在星期一回来。
    • 在上面的示例中,为了简单起见,我只是检查每个属性,但更好的方法是定义一个自定义属性,如 CustomRequiredAttribute("PropertyIdentifier") 并检查标识符是否匹配。这将使您的客户端设置逻辑更容易,更独立于实现,并确保“坏数据”无法使任意属性成为必需。您还可以重用标识符,以便在多个模型类上显示的属性可以一致地工作。
    • 代码中已经有一个自定义元数据提供程序,来自我的一位队友。也许我可以捎带。
    【解决方案2】:

    不想弄乱自定义验证器,因此您弄乱了视图,通过将验证逻辑从预期找到的位置删除来混淆验证逻辑。

    真的,不要害怕创建自定义属性验证器。你现在正在做的是获得技术债务。

    【讨论】:

    • 我确实对此表示怀疑,这就是我分享我的问题的原因。假设我会采用元数据方法,我如何确保这些值是在运行时而不是在编译期间设置的?
    猜你喜欢
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多