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.
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:
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.
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.
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.