microsoft / dotnet

This repo is the official home of .NET on GitHub. It's a great starting point to find many .NET OSS projects from Microsoft and the community, including many that are part of the .NET Foundation.
https://devblogs.microsoft.com/dotnet/
MIT License
14.34k stars 2.21k forks source link

Data Annotations on record fails in unittest #1296

Closed MarcoHuib closed 3 years ago

MarcoHuib commented 3 years ago

Currently we are setting up a new project and like to use the new records introduced in C# 9. We encounter a problem with DataAnnotations inside the record (constructor) not being triggered during the unittest.

Now the DataAnnotation is triggered when calling the Controller, but when i try to simulate this in a unittest (see code below) it will never return any errors.

        //Unit Testing ASP.NET DataAnnotations validation
        //http://stackoverflow.com/questions/2167811/unit-testing-asp-net-dataannotations-validation
        protected static IList<ValidationResult> ValidateModel(object model)
        {
            var validationResults = new List<ValidationResult>();
            var ctx = new ValidationContext(model, null, null);
            Validator.TryValidateObject(model, ctx, validationResults, true);
            return validationResults;
        }

Currently we try different solutions but nothing works...

    public record FooRecord(string BarProperty)
    {
        [Required]
        public string BarProperty { get; init; } = BarProperty;

    }

This results in an error when i using this with mvc api.

System.InvalidOperationException: Record type '*******.FooRecord' has validation metadata defined on property 'BarProperty' that will be ignored. 'BarProperty' is a parameter in the record primary constructor and validation metadata must be associated with the constructor parameter.

As someone comment on stackoverflow to use targets in dataannotation. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/#attribute-targets

    public record FooRecord([property: Required]string BarProperty)
    {
    }

Results in a error: Record type '*******.FooRecord' has validation metadata defined on property 'BarProperty' that will be ignored. 'BarProperty' is a parameter in the record primary constructor and validation metadata must be associated with the constructor parameter.

The only workaround that is possible is write the complete record

    public record FooRecord
    {
        [Required]
        public string BarProperty { get; init; }

        public FooRecord([Required] string barProperty) => (BarProperty) = (barProperty);
        public void Deconstruct(out string barProperty)
        {
            barProperty = BarProperty;
        }
    }

I think this happens because the constructor of a record are params and properties at the same time. I'm hoping if someone knows why this happens and maybe know how to solve this using the shorthand syntax:

    public record FooRecord([Required] BarProperty){ }
MarcoHuib commented 3 years ago

Wrong location for reporting bug. create new bug at https://github.com/dotnet/core/issues/6006#issue-819972273