【问题标题】:DataAnnotations CustomValidator with Generic Type具有通用类型的 DataAnnotations CustomValidator
【发布时间】:2011-07-11 20:29:27
【问题描述】:

这是一个使用 S#arp 架构的 MVC 3 项目。

我的域有几个继承NamedEntity 的对象,这是一个扩展Entity 并定义string Name 属性的抽象类。

所有这些对象都有一个共同点,即它们在其对象类型中必须是唯一的名称。

鉴于这些对象的集合相对较小(极端情况下为 200 个),我不介意往返数据库以检查是否存在重复。

我的问题是,在保持 DRY 的同时轻松验证这一点的最佳方法是什么?

我已经经历了六次迭代,其中任何一次都不是特别喜欢。我已经尝试在 ViewModel 上创建 [ValidUniqueName] 属性,并且 ViewModel 可以键入 EditViewModel<T> where T: NamedEntity。问题在于属性不知道要与之交谈的正确存储库/任务对象,因为 Generic。

为了解决这个问题,我尝试在 ViewModel 中创建一个 INamedEntityTasks<T> 引用,并从 ValidationContext 访问它以获取 ValidUniqueName 属性。这不能按预期工作,因为 Attribute 不是通用的(它不可能),因此它不知道如何正确地将 ValidationContext.ObjectInstance 转换回可用的东西。

此时,我唯一能想到的就是创建单独的[ValidUniqueFoo][ValidUniqueBar] 等属性,这似乎很糟糕。或者,我可以将验证放在每个控制器中,但这似乎同样糟糕。

【问题讨论】:

    标签: .net asp.net asp.net-mvc-3 data-annotations s#arp-architecture


    【解决方案1】:

    目前,它似乎将涉及肮脏的黑客或一些非常复杂的代码(请参阅其他答案)。不得不写并不可怕

    public class ValidUniqueCityAttribute : ValidationAttribute {
    
    public override bool IsValid(object value)
        {
            if (value == null || !(value is CityCreateOrEditViewModel)) return base.IsValid(value);
    
            INamedEntityTasks<City> tasks = new NamedEntityTasks<City>();
    
            CityCreateOrEditViewModel vm = value as CityCreateOrEditViewModel;
            return !tasks.CheckForDuplicateName(vm.Entity.Id, vm.Entity.Name);
        }
    }
    

    然后,对于每个 NamedEntity,我们复制和粘贴、搜索和替换...是的,这是多余的,但它确实具有允许更大灵活性的优势,例如,如果我们想以其他方式验证城市名称而不是类别(例如,查找给定州所有城市的主列表,尽管这完全是人为的和可怕的做法:))。

    【讨论】:

    • 这看起来不错,但我相信你可以让它更通用。我需要考虑一下。
    • 让我知道你想出了什么!我绝对有兴趣找到更通用的解决方案。如果tasks 没有正确输入(例如,如果它作为基础NamedEntity 输入,您可能会得到误报(您有一个名为 Foo 的类别,但它不会让您创建一个名为 Foo 的城市)。我尝试修改 CheckForDuplicateName 以获取 NamedEntity 对象并比较类型,但 typeof(City)typeof(NamedEntity) 不匹配,因此也不起作用。
    • 我快到了!使用反射,但我得到了误报。一旦我有更多,我会发布更多。
    • 所以经过大量的反复试验,我得出的结论是,实际上不可能使它更通用。属性似乎不喜欢成为不是编译时常量的过去参数,这意味着没有委托。我认为您上面的解决方案可能会得到最好的解决方案,这很遗憾,因为我希望看到一个属性适用于多种情况。
    • 感谢您的帮助!现在我们只需要向 C# 团队施压以实现泛型属性,整个问题就迎刃而解了!
    【解决方案2】:

    您可以简单地向[ValidUniqueName] 添加一个重载的构造函数吗?还是我完全错过了重点?

    编辑

    您能否将两个类型参数传递给您的 ViewModel 类似的东西

    ViewModel<T, TRepository>
    

    【讨论】:

    • 构造函数的参数是什么?所以我有[ValidUniqueName(...)] public class EditViewModel&lt;Foo&gt; ...不太确定我可以通过什么来提供帮助。我想我可以用ValidUniqueName(typeof(T)) 破解它,但这肯定会是一个破解。
    • 是的,我明白你的意思。我认为传递 typeof(T) 参数将是最干净的解决方案,当使用重载构造函数时,但正如你所说,这将是一个肮脏的黑客。
    • 我仍然不知道这是否可行。为了调用像CheckForDuplicateName 这样的函数,我需要一个INamedEntityTasks&lt;&gt; 的类型化实例。即使使用反射来创建某些东西(例如在omegacoder.com/?p=38 中),我仍然需要知道该类型将是什么才能使用它(链接示例中的第 7 行)。
    • 即使尝试做类似ValidFooAttributeValidBarAttribute (类爆炸)都继承自ValidUniqueNameAttribute 的事情似乎也行不通。我可以在彼此没有任何关系的情况下实现两个类,每个类都创建自己的Tasks 对象并调用 CheckForDuplicateName ...这只是几行重复的代码,但它是重复的。这可能是最不难看的解决方案。
    • 我和你一样热衷于保持干燥,但我担心在不知道你的代码的情况下我帮不上什么忙,对不起。我已经用一个可能的建议编辑了我的答案,但我不确定你将如何实现它。
    猜你喜欢
    • 1970-01-01
    • 2023-03-07
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    相关资源
    最近更新 更多