Add DataAnnotations metadata to EF Core in a partial class

If you’re old like me, and can remember back to the old days of LINQ to SQL and early Entity Framework with .NET Framework, then you’ll likely remember using a Metadata class along with the MetadataType attribute on a partial class to add DataAnnotations to your models.

This allowed you to use your entities as models directly in your MVC controllers and Views when doing CRUD operations and provided you with in-built client-side and server-side validation.

[MetadataType(typeof(TeamMetaData))]
public partial class Team
{
    public string TeamDescription { get; set; } = null!;

    public int SeasonId { get; set; }
}

public class TeamMetaData
{
    [Required(ErrorMessage = "A Team Name is required")]
    public string TeamDescription { get; set; } = null!;

    [Required(ErrorMessage = "A season is required")]
    public int SeasonId { get; set; }
}

While this isn’t so much an issue these days for those who go with a code-first approach to their entity models, but for those of us who like to get our hands dirty in the database, and using a database-first approach, it’s more difficult.

I’ll typically create my database structure, including all the keys, and relationships I want, then I use the Scaffold-DBContext command from the Entity Framework Core tools to create my DbContext and entity classes in my project.

I do try to use a ViewModel approach rather than directly using entity objects, but sometimes for a simple CRUD application where the model matches the entity basically property for property, then I don’t see a lot of harm in using entities directly.

If you’ve tried to use the old style of decorating a metadata class with your DataAnnotations in a recent .NET app, like below, you’ll find that they just don’t work when you go to validate your input form.

Instead of using the MetadataType attribute, we now need to use TypeDescriptor.AddProviderTransparent() to add a new AssociatedMetadataTypeTypeDescriptionProvider() to our code to link the entity and the Metadata class.

So we can drop the MetadataType attribute.

public partial class Team
{
    public string TeamDescription { get; set; } = null!;

    public int SeasonId { get; set; }
}

public class TeamMetaData
{
    [Required(ErrorMessage = "A Team Name is required")]
    public string TeamDescription { get; set; } = null!;

    [Required(ErrorMessage = "A season is required")]
    public int SeasonId { get; set; }
}

But now, we need to register out AssociatedMetadataTypeTypeDescriptionProvider.

TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Team), typeof(TeamMetaData)), typeof(Team));

We can effectively do this anywhere in our code as long as it’s done before we try to use the DataAnnotations in our TeamMetaData class to validate some incoming data.

I’ve seen suggestions from people of adding it to controller constructors for MVC or in the OnInitialized function in a Blazor app, but I’ve found it’s just easier to create a small static helper method and call this in your Program.cs. That way all the associated metadata is linked right from the beginning.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top