如何确定不可预测的 LINQ 查询结果的来源?

发布于 2024-08-21 18:36:04 字数 2384 浏览 9 评论 0原文

我一直在开发一个使用 xVal 的服务器端验证和数据注释的应用程序。我们最近遇到了错误,其中对于具有多个验证的字段,验证消息是不可预测的,如果该字段为空,则验证消息可能会失败(例如,需要电子邮件地址,但也无法通过有效性检查)。

假设我只需要返回第一个验证错误,我向验证运行程序添加了一个方法来实现该目标(更新:请参阅底部的编辑以了解确切的方法):

public static IEnumerable<ErrorInfo> GetFirstErrors<T>(object instance) where T : ValidationAttribute
    {
        return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<T>().Take(1)
               where !attribute.IsValid(prop.GetValue(instance))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
    }

我还设置了一个在 NUnit 中验证的简单测试方法:

private class FirstErrorValidationTest
    {
        [RequiredValueValidator(ErrorMessage = "This field is required"), StringLength(50)]
        public string FirstName { get; set; }

        [RequiredValueValidator(ErrorMessage = "This field is required"), StringLength(50)]
        public string LastName { get; set; }

        [RequiredValueValidator(ErrorMessage = "This field is required"), EmailAddressValidator, StringLength(50)]
        public string EmailAddress { get; set; }
    }

    [Test]
    public void Assert_GetFirstErrors_Gets_First_Listed_Validation_Attribute_Error_Messages()
    {
        FirstErrorValidationTest test = new FirstErrorValidationTest()
        {
            FirstName = "",
            LastName = "",
            EmailAddress = ""
        };

        var errors = DataAnnotationsValidationRunner.GetFirstErrors(test);

        Assert.AreEqual(3, errors.Count());

        foreach (var error in errors)
            Assert.IsTrue(error.ErrorMessage.Contains("required"));
    }

问题是该测试的输出高度不可预测。有时它会通过,有时只返回一两个错误,有时根本不返回。这里的问题是我的 LINQ 查询、我的测试还是两者都有?

编辑:以稍微不同的方法粘贴的要点;这是实际被击中的:

    public static IEnumerable<ErrorInfo> GetFirstErrors(object instance)
    {
        return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<ValidationAttribute>().Take(1)
               where !attribute.IsValid(prop.GetValue(instance))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
    }

I've been working on an application that uses xVal's server side validation with data annotations. We recently ran into errors where the validation messages have been unpredictable for fields that have multiple validations that could fail if the field is empty (e.g., an email address is required, but also fails a validity check).

Assuming that I needed to just return the first validation error, I added a method to our validation runner to achieve that goal (UPDATE: see edit at the bottom for the exact method):

public static IEnumerable<ErrorInfo> GetFirstErrors<T>(object instance) where T : ValidationAttribute
    {
        return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<T>().Take(1)
               where !attribute.IsValid(prop.GetValue(instance))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
    }

I also set up a simple test method to verify in NUnit:

private class FirstErrorValidationTest
    {
        [RequiredValueValidator(ErrorMessage = "This field is required"), StringLength(50)]
        public string FirstName { get; set; }

        [RequiredValueValidator(ErrorMessage = "This field is required"), StringLength(50)]
        public string LastName { get; set; }

        [RequiredValueValidator(ErrorMessage = "This field is required"), EmailAddressValidator, StringLength(50)]
        public string EmailAddress { get; set; }
    }

    [Test]
    public void Assert_GetFirstErrors_Gets_First_Listed_Validation_Attribute_Error_Messages()
    {
        FirstErrorValidationTest test = new FirstErrorValidationTest()
        {
            FirstName = "",
            LastName = "",
            EmailAddress = ""
        };

        var errors = DataAnnotationsValidationRunner.GetFirstErrors(test);

        Assert.AreEqual(3, errors.Count());

        foreach (var error in errors)
            Assert.IsTrue(error.ErrorMessage.Contains("required"));
    }

The problem is that this test's output is highly unpredictable. Sometimes it passes, sometimes it returns just one or two of the errors, and sometimes none at all. Is the issue here with my LINQ query, my test, or both?

EDIT: Great point on pasting in a slightly different method; here's the one actually being hit:

    public static IEnumerable<ErrorInfo> GetFirstErrors(object instance)
    {
        return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<ValidationAttribute>().Take(1)
               where !attribute.IsValid(prop.GetValue(instance))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

沐歌 2024-08-28 18:36:04

摆脱Take(1)。我怀疑空字符串正在通过 Required 测试。如果您碰巧得到它而不是长度验证器,则测试通过。

Get rid of the Take(1). I suspect the empty string is passing the Required test. If you happen to get that instead of the length validator, the test passes.

战皆罪 2024-08-28 18:36:04

尝试使用

var errors = DataAnnotationsValidationRunner.GetFirstErrors(test).ToArray();

Try to use

var errors = DataAnnotationsValidationRunner.GetFirstErrors(test).ToArray();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文