I have a table which has an integer field which is supposed to be unique and autoincremented.
After doing some research I came up with two different approaches:
Case A
modelBuilder.Entity<MyTable>()
.Property(x => x.MyIntegerField)
.ValueGeneratedOnAdd()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
modelBuilder.Entity<MyTable>()
.HasIndex(x => x.MyIntegerField)
.IsUnique();
This works, but I have questions regarding it:
- When does it increment the value of the MyIntegerField? Does it create it on the App level or on the Database level? Why would I ask this is because imagine
two different HTTP requests
are coming to the app. I'm afraid it might throwUniqueConstraint exception
if value generation happened on app-level.
Case B
modelBuilder.Entity<MyTable>()
.Property(x => x.MyIntegerField)
.ValueGeneratedOnAdd()
.UseIdentityColumn();
modelBuilder.Entity<MyTable>()
.HasIndex(x => x.MyIntegerField)
.IsUnique();
This works too, but the again I have questions:
- Does this make the field managed on the Database level?
In the docs it says UseIdentityColumn()
is for Primary keys, but MyIntegerField is not a primary key. I did not mark it as one, and in the schema after migration I see it didn't create it as one.
- Are there any possible problems I might face using this method?
What is the correct way of solving my issue?
P.S: I am using GUID
for Primary Key
3条答案
按热度按时间zfycwa2u1#
Looks like both do the same... I tested both cases, and checked for Identity column using the following command:
It resulted with Yes in both cases Now I don't know why exactly they mentioned "for key properties" in docs for UseIdentityColumn() method, but both seem to be working the same way. Creating an Identity inside the DB, which means DB itself is gonna manage the insert values.
z6psavjg2#
I have made a quick experiment trying out the second approach.
Those are versions of packages I used:
I started with this DbContext:
And created a fresh migration using
dotnet ef migrations add InitialCreate
getting this migration file:Now I changed the DbContext by overloading
OnModelCreating
method:now calling
dotnet ef migrations add Incremental
resulted in an error:The properties 'CustomEntity.Id', 'CustomEntity.Incremental' are configured to use 'Identity' value generation and are mapped to the same table 'CustomEntities', but only one column per table can be configured as 'Identity'. Call 'ValueGeneratedNever' in 'OnModelCreating' for properties that should not use 'Identity'.
It seems like ChatGPT was lying once again, and this approach is not valid for code first. It should be possible to do it when working in database first though since nothing prevents you from manually adding a second identity column to a table. I think you are stuck with solving the problem on C# side.
oalqel3c3#
I have answered previously for a problem when Id is also an identity column, now I'm answering for a case when Id is a
Guid
.This is the DbContext I used:
And this is a part of migration script I got from it:
now I tried getting the client side
Incremental
value into the database:I got an exception.
Cannot insert explicit value for identity column in table 'CustomEntities' when IDENTITY_INSERT is set to OFF.
All was fine when I didn't provide the
Incremental
value.The Query sent to the database on
SaveChanges
looked correct.The DbContext created the row. Allowed
Incremental
to be filled by the identity logic from inside the SQL Server, and query ended with returning theIncremental
value from the database back to DbContext. So afterSaveChanges
the entity within C# should have updated value.And it seems like it does.
In conclusion. Second approach looks completely correct and safe. You will not end up with duplicate values inside the identity column even in case of a race condition since EFCore is simply letting SqlServer handle it with its internal mechanisms.