c# record constructor validation

c# record constructor validation
In this article [Show more]

    Implementing Constructor Validation in C# Records

    C# records, designed for creating immutable data structures, simplify defining objects with value-based equality. However, ensuring these records always hold valid data requires effective constructor validation. This article delves into best practices for implementing constructor validation in C# records, ensuring that each instance meets specified criteria right from initialization.

    Importance of Constructor Validation in Records

    Constructor validation is crucial for maintaining data integrity, especially when using records to model domain entities or data transfer objects (DTOs) in an application. Proper validation prevents invalid data states and helps catch errors early in the development process, leading to more robust and maintainable code.

    Techniques for Constructor Validation in C# Records

    C# records automatically generate a constructor based on the defined properties. While this feature is convenient, it means that custom validation logic needs to be explicitly defined if not all property assignments are straightforward. Here’s how to implement this validation.

    Adding Validation Directly in the Constructor


    You can include validation logic directly within the primary constructor to check the validity of parameters before they are used to set properties.

    public record Person(string FirstName, string LastName, int Age)
    {
       public Person(string firstName, string lastName, int age)
       {
           if (string.IsNullOrWhiteSpace(firstName))
           {
               throw new ArgumentException("First name cannot be empty.", nameof(firstName));
           }
           if (string.IsNullOrWhiteSpace(lastName))
           {
               throw new ArgumentException("Last name cannot be empty.", nameof(lastName));
           }
           if (age <= 0)
           {
               throw new ArgumentException("Age must be greater than zero.", nameof(age));
           }
           FirstName = firstName;
           LastName = lastName;
           Age = age;
       }
    }

    In this example, the Person record includes validation in the constructor to ensure that FirstName is not empty, LastName is not empty, and Age is greater than zero, throwing an exception if any of these conditions are not met.

    By placing all validation checks within the primary constructor, you ensure that any instance of the Person record is created in a valid state right from the start, preventing invalid data states and helping to catch errors early in the development process. This method maintains the integrity and robustness of your code. 

     

    Using Init-Only Setters for Validation

    With C# 9.0 and later, records support init accessors that allow properties to be mutable during the object initialization phase. You can use these to include validation logic:

    public record Person
    {
        private string _firstName;
        public string FirstName
        {
            get => _firstName;
            init
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("First name cannot be empty.", nameof(FirstName));
                }
                _firstName = value;
            }
        }
    
        public string LastName { get; init; }
        public int Age { get; init; }
    }
    

    Here, validation for the FirstName ensures it is not null or whitespace. The init accessor allows this check to occur right when the property is being initialized.

    Best Practices for Constructor Validation in Records

    • Consistent Validation: Ensure that all constructors (if multiple are used) validate data consistently. This prevents any bypass in the object's integrity.
    • Use Meaningful Error Messages: Provide clear and descriptive error messages that help quickly identify what went wrong during validation.
    • Consider Using Fluent Validation: For complex records, consider using a validation framework like FluentValidation to decouple validation rules from the data model and keep the validation logic clean and scalable.

    Example of Record Validation in Action

     

    var person = new Person("John", "", -5);  // This will throw exceptions due to invalid LastName and Age.
    

    Conclusion

    Constructor validation in C# records is a powerful mechanism to ensure that all instances of a record are created in a valid state. By incorporating rigorous validation checks directly in the constructor or init accessors, developers can prevent the propagation of invalid data throughout an application, enhancing its reliability and maintainability. Effective validation practices, combined with the immutability and simplicity of records, make C# a robust choice for modeling complex domain logic and data structures.

    Author Information
    • Author: Ehsan Babaei

    Send Comment



    Comments

    avatar
    Mark
    May 10, 2024
    Adding Validation Directly in the Constructor - is incorrect
    avatar
    Ehsan Babaei
    May 28, 2024

    Hi Mark, Thanks for your feedback. You’re right, the validation method in the constructor section was incorrect. I’ve updated it to ensure proper validation in the primary constructor. Best regards.