In this article, we’ll see how we can apply unique constraints to a property in EF (Entity Framework) Core using the code-first approach.

To download the source code for this article, you can visit our GitHub repository.

Let’s start coding!

Minimal API Setup

For this article, we’ll create a mini-version of our solar system contained in a minimal API:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!
app.MapPost("/planets", async (Planet planet, SolarSystemDbContext context) =>
{
    try
    {
        await context.Planets.AddAsync(planet);
        await context.SaveChangesAsync();

        return Results.Created($"/planets/{planet.Id}", planet);
    }
    catch
    {
        return Results.BadRequest();
    }
})
.WithName("AddPlanet")
.WithOpenApi();

We have one POST endpoint that will return 201 Created if the planet was successfully added to our database or 400 Bad Request if there was an error.

Minimal APIs are a great way to reduce API projects to a minimal setup, if you want to know more about it check out our article Minimal APIs in .NET.

Our API uses SQL Server as a database, so we’ll also use Docker to host it:

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest

Using the docker run command we start a new SQL Server container. We opt out for a relational database as the in-memory provider doesn’t have referential integrity and our unique constraints wouldn’t work.

Next, we’ll see how we can add unique constraints to our planet’s Name property.

How to Add Unique Constraints to a Property in EF Core Using Attributes

We can use indexes to make a property in EF Core unique. Although indexes aren’t unique by default, we can still make them unique:

[Index(nameof(Name), IsUnique = true)]
public class Planet
{
    public required int Id { get; set; }
    public required string Name { get; set; }
    public required double Mass { get; set; }
    public required double Radius { get; set; }
    public required double OrbitalPeriod { get; set; }
}

Specifically, we use the Index attribute to decorate our Planet class. We pass two things to the attribute. First is the name of the property we want to create an index for. Meanwhile the second sets the index’s IsUnique property to true. In this way, we ensure that we won’t have more than one planet with the same name.

How to Add Unique Constraints to a Property in EF Core Using Fluent API

EF Core provides us with another option for adding unique constraints to a property and it is via the EF Fluent API:

public class PlanetConfiguration : IEntityTypeConfiguration<Planet>
{
    public void Configure(EntityTypeBuilder<Planet> builder)
    {
        builder.HasIndex(p => p.Name)
            .IsUnique();
    }
}

Initially, we create a PlanetConfiguration class that implements the IEntityTypeConfiguration<TEntity> interface, which has a Configure() method. Inside it, we use the HasIndex() method with which we specify which property we want for an index. Then we use the IsUnique() method to tell EF Core that we also want the index to be a unique one.

To learn more about EF Core, be sure to check out our Entity Framework Core Series.

There is one final thing we need to do:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}

Here, in our SolarSystemDbContext class, we override the OnModelCreating() method. In this method, we use the ModelBuilder‘s ApplyConfigurationsFromAssembly() method to apply all configurations from the current assembly. 

Note that we can have the configuration inside the OnModelCreating() method and not in a separate class, but by extracting configurations in separate classes we achieve better readability.

How to Add Unique Constraints Combining Several Properties in EF Core

We can also use composite indexes to make a combination of several properties a unique one. Moreover, by using composite indexes, we can speed up queries that filter on indexed columns but also queries that only filter on the first property covered by the index.

Creating Composite Indexes in EF Core Using Attributes

Attributes are a powerful tool that can help us set composite indexes:

[Index(nameof(Name), nameof(OrbitalPeriod), IsUnique = true)]
public class Planet
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public required double Mass { get; set; }
    public required double Radius { get; set; }
    public required double OrbitalPeriod { get; set; }
}

Using the Index attribute, we use nameof operator to specify which properties should be included in the index. In our case, those are the Name and OrbitalPeriod. Consequently, by using this approach we ensure that there will be exactly one planet with the same values for those properties.

Create Composite Indexes in EF Core Using Fluent API

We can achieve the same result with the Fluent API as well:

public class PlanetConfiguration : IEntityTypeConfiguration<Planet>
{
    public void Configure(EntityTypeBuilder<Planet> builder)
    {
        builder.HasIndex(p => new
            {
                p.Name,
                p.OrbitalPeriod
            })
            .IsUnique();
    }
}

We update the Configure() method in our PlanetConfiguration class, creating a new anonymous type that holds the properties we want to create an index for. This is the only thing we need to do to create unique composite indexes.

Conclusion

In this article, we explored two approaches for applying unique constraints to properties in EF Core using the code-first approach. By using either attributes or the EF’s Fluent API, we can ensure the uniqueness of properties in our databases. Regardless of whether we opt for the simplicity of attributes or the intuitiveness of the Fluent API, these techniques help in effectively modeling our data classes.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!