🔷

New Data Annotations in .NET 8

Language Fundamentals Advanced 2 min read 200 words
C#

New Data Annotations in .NET 8

.NET 8 introduces several new data annotation attributes for model validation that provide more control over property constraints.

New Attributes Overview

Length Attribute

Specifies both minimum and maximum length for strings:

public class Product
{
    [Length(2, 30)]
    public string Name { get; set; }

    [Length(2, 255)]
    public string Description { get; set; }
}

Usage: Replaces the need for both [MinLength] and [MaxLength] on the same property.

Range Attribute with Exclusive Bounds

The Range attribute now supports exclusive minimum and maximum values:

public class Product
{
    [Range(1, 1000, MinimumIsExclusive = true, MaximumIsExclusive = false)]
    public decimal Price { get; set; }
}

Parameters:

  • MinimumIsExclusive = true: Value must be > minimum (not >=)
  • MaximumIsExclusive = false: Value can be <= maximum

This means Price must be > 1 and <= 1000.

AllowedValues Attribute

Restricts a property to a specific set of allowed values:

public class Product
{
    [AllowedValues("S", "M", "L", "XL", "XXL")]
    public string Size { get; set; }
}

Use Case: Validates against an enum-like set of valid options without using an actual enum.

DeniedValues Attribute

Opposite of AllowedValues - specifies values that are NOT allowed:

public class Product
{
    [DeniedValues("Electronics", "Computers")]
    public string Category { get; set; }
}

Use Case: Blacklisting certain values while allowing everything else.

Base64String Attribute

Validates that a string is valid Base64 format:

public class Product
{
    [Base64String]
    public string Image { get; set; }
}

Use Case: Ensuring uploaded image data or encoded content is properly formatted.

Complete Example

public class Product
{
    public int Id { get; set; }

    [Length(2, 30)]
    [Required]
    public string Name { get; set; }

    [Length(2, 255)]
    public string Description { get; set; }

    [Range(1, 1000, MinimumIsExclusive = true, MaximumIsExclusive = false)]
    public decimal Price { get; set; }

    [AllowedValues("S", "M", "L", "XL", "XXL")]
    public string Size { get; set; }

    [DeniedValues("Discontinued", "Recalled")]
    public string Status { get; set; }

    [Base64String]
    public string ImageData { get; set; }
}

Using with ASP.NET Core

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpPost]
    public IActionResult Create([FromBody] Product product)
    {
        // ModelState automatically validates data annotations
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        // Product is valid, proceed with creation
        return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
    }
}

Custom Validation Messages

public class Product
{
    [Length(2, 30, ErrorMessage = "Name must be between 2 and 30 characters")]
    public string Name { get; set; }

    [AllowedValues("S", "M", "L", "XL", "XXL",
        ErrorMessage = "Size must be S, M, L, XL, or XXL")]
    public string Size { get; set; }
}

Combining with FluentValidation

For complex scenarios, combine data annotations with FluentValidation:

public class ProductValidator : AbstractValidator<Product>
{
    public ProductValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty()
            .Length(2, 30)
            .Must(NotContainSpecialCharacters)
            .WithMessage("Name cannot contain special characters");

        RuleFor(x => x.Price)
            .GreaterThan(0)
            .LessThanOrEqualTo(1000);
    }

    private bool NotContainSpecialCharacters(string name)
    {
        return name.All(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c));
    }
}

Best Practices

  1. Use data annotations for simple validation - Quick, declarative, works with model binding
  2. Use FluentValidation for complex rules - Better for cross-property validation, conditional logic
  3. Always validate on both client and server - Never trust client-side validation alone
  4. Provide meaningful error messages - Help users understand what went wrong

Sources

  • Arhitectura/data annotations.jpeg

📚 Related Articles