66

I am new to EF so here goes.I have a class which contains the following

public class EmailTemplate
{
    public Guid Id { get; set; }

    [MaxLength(2000)]
    public string Html { get; set; }
}

Here is my mapping class

class EmailMapper : EntityTypeConfiguration<EmailTemplate>
    {
        public EmailMapper()
        {
            ToTable("EmailTemplate");

            HasKey(c => c.Id);
            Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(c => c.Id).IsRequired();
        }
    }

I am trying to call DbContext.SaveChanges(), but I get the following error :

Exception Details: System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Id', table 'AutoSendConnection.dbo.EmailTemplates'; column does not allow nulls. INSERT fails.

What am i doing wrong? Why won't EF auto create a unique GUID?

user2859298
  • 1,373
  • 3
  • 13
  • 28
  • You can set the default value for a column via fluent api or manually modify or create your migrations :) For SQL Server, just use `newid()` or `newsequentialid()` as default value In PostgreSQL you have lot of different choices, but `uuid_generate_v1()` should basically do the trick. Unfortunately, there are no easy solutions for this in MySQL and SQLite I believe. – ˈvɔlə Mar 20 '20 at 20:34
  • You can check this: http://www.codingfusion.com/Post/Entity-Framework-Auto-Generate-GUID-with-Code-Examples – MaxPayne Aug 04 '23 at 07:35

9 Answers9

84

Just decorate the Id field on your EmailTemplate class as below and SQL Server will automatically generate the value on insert.

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }

You can also remove your Mapper class as it's no longer needed.

Mark
  • 1,718
  • 11
  • 10
  • Actually, this is the same as what OP already did in their code. Although this is a workaround which fixes the problem, it doesn't answer the original problem. – Alisson Reinaldo Silva May 23 '17 at 01:42
  • 2
    If you use CodeFirst, EF might not detect this change. You can delete your table and add a new migration. – Virgar Poulsen Aug 19 '17 at 06:09
  • 1
    No default sql value set in the migration generated so this doesn't work (even when creating the table from scratch). – War May 01 '19 at 11:30
  • 1
    This answer works for EF Core only ... EF6 users will find this problematic. – War May 31 '19 at 17:18
  • You will get a clustered index on a GUID, and you should have a very good reason to want this, because it will insert new rows randomly, and not sequentially at the end of the table. What you should do is have an auto-incremented INT for the clustered index, see this answer for EF core: https://stackoverflow.com/questions/50362749/ef-core-code-first-clustered-index-and-identity-column#answer-50365955 – Richard Anderssen Oct 29 '19 at 18:51
  • 3
    This doesn't work in EF Core. Migration file doesn't have any identity information for this Id column. – Rahul Jul 23 '20 at 02:33
58

If using .Net core then this should work for you ...

Use fluent API

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Node>().Property(x => x.ID).HasDefaultValueSql("NEWID()");
}

or

modelBuilder.Entity<Student>().Property(p => p.StudentID)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

Here is a more comprehensive Cheat Sheet for entity framework

Adel Tabareh
  • 1,478
  • 14
  • 10
23

Addressing other answers here

None of these other options seem to work and I've questioned this time and time again with the EF team over on github ...

https://github.com/aspnet/EntityFramework6/issues/762

... for some reason the EF dev team seem to think that this is "working by design" and repeatedly close tickets questioning this "bug".

The EF team explanation

For some reason they seem to think that "generating Guids in SQL is considered not best practice and that to ensure the keys are available immediately we should be generating the keys in the app code".

The issue here of course is that highly populated tables run the risk of you taking further business actions consuming an invalid key.

In my case this could break some extremely complex multi server DTC transactions so I don't believe the advice from MS to be correct, that said EF Core doesn't currently support distributed transactions at all so in a focused context they may have a point.

My answer (which actually works)

In short, I solved this by "manually hacking" the generated migration after generating it ...

EF code first migrations, DB generated guid keys

To quote the other question the answer is as follows:

Generate the migration script as you normally would putting both attributes on the key property like this ...

public class Foo
{
     [Key]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public Guid Id { get; set; }
}

... declaratively speaking the entity is now correct.

The migration it will generate will look something like:

CreateTable(
    "dbo.Foos",
    c => new
        {
            Id = c.Guid(nullable: false),
            ...
        })
    .PrimaryKey(t => t.Id)
    ...;

... I have not been able to pin down why, but in some situations this will work and in others it won't (run the migration, perform an insert to test).

If it fails, roll the migration back then modify it to read something like ...

CreateTable(
    "dbo.Foos",
    c => new
        {
            Id = c.Guid(nullable: false, defaultValueSql: "newid()"),
            ...
        })
    .PrimaryKey(t => t.Id)
    ...;

... the extra code here tells SQL to generate the key as we would expect.

As a rule of thumb I would apply this change all the time for consistency reasons and it means that at a glance your migrations will show you exactly what keys are db generated and of course which ones don't.

War
  • 8,539
  • 4
  • 46
  • 98
  • 2
    This should be marked as the answer. Thank you so much! – carlos chourio Nov 27 '19 at 11:09
  • I think the key problem are the many different database providers They act very differently in some serious cases. – ˈvɔlə Mar 20 '20 at 20:29
  • The provider should handle this based on the specific type of DB endpoint it's talking to. I as a consumer of EF should be abstracted from that, that's literally why i'm using EF in the first place. Don't get a dog and spend all day barking at the neighbours cat. – War Mar 30 '20 at 17:03
  • 2
    The problem with this solution is then you have to know or remember to add it in every time you create a new table. Wish this would get resolved. – Jwags Apr 14 '20 at 23:34
  • Completely agree, I hate this solution but couldn't find a way that would cause the framework to just do this so this felt like a "best case from all the bad options". The EF dev team simply won't entertain the idea that this is a bug at all. – War Apr 16 '20 at 10:45
10

After a long investigation, I found out that in EF Core 3.1 you need to use

builder.Property(e => e.Id).ValueGeneratedOnAdd();
ˈvɔlə
  • 9,204
  • 10
  • 63
  • 89
  • 2
    @misha Which database provider do you use? – ˈvɔlə Mar 20 '20 at 20:10
  • I think you need to specify entity before property. For example: builder.Entity().Property(...).ValueGeneratedOnAdd(); – Petr Tomášek Jun 10 '20 at 10:17
  • @PetrTomášek this builder is called from inside an IEntityTypeConfiguration.Confiure method. These are called inside the DbContext.OnModelCreating, helps break up the OnModelCreating method. https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.ientitytypeconfiguration-1?view=efcore-3.1 – Paul Lawrence Jun 26 '20 at 07:20
5

Set the default sql value of the field to 'newsequentialid()' in the mapping configuration.

Liviu Mandras
  • 6,540
  • 2
  • 41
  • 65
  • Please see my mapping class in the amended question, Where would i put the newsequentialid() ? I am trying to do this via code first and just thought the EF would handle it? – user2859298 Aug 02 '14 at 12:47
  • This shows an example: http://blog.jongallant.com/2013/04/guid-comb-ef-code-first.html#.UmFhE_k8B8F – Liviu Mandras Aug 02 '14 at 13:05
2

You can also set Default Value of ID as NewID() in Sql Server itself and pass the GUID as null

I used to do it in SSMS.

Moons
  • 3,833
  • 4
  • 49
  • 82
  • 1
    Sequential ID causes less fragmentation of the indexes, hence better query performance – Liviu Mandras Aug 02 '14 at 12:43
  • Even if u set Default value of ID as NewID() in SQL server, U still need to modify your .edmx file if StoreGeneratedPattern property not yet set. ref: http://www.developerhandbook.com/entity-framework/entity-framework-use-a-guid-as-the-primary-key/ – Chinh Phan Mar 15 '16 at 07:16
  • @ChinhPhan In standard cases i dont think we need to change any XML. I use wizard to create EDMX files and everything worked fine. – Moons Mar 17 '16 at 13:12
1

I prefer to leave the database to generate the id automatically for example the following schema:

CREATE TABLE [dbo].[MyTable](
    [MyId] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Booking_BookingId]  DEFAULT (newsequentialid())
    )

Then in the code first mapping I specify the following to tell Entity Framework that the database will take care of generating the value on insert.

Property(a => a.MyId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
John
  • 3,512
  • 2
  • 36
  • 53
  • 2
    With code first we don't write the SQL the migration does, and the migration code you show there doesn't result in the SQL you've shown, see my answer for my details. – War May 01 '19 at 11:49
  • N.B. "With code first we don't write the SQL the migration does" Is not necessarily true. "Code First From Database" is what I use and I DO write my SQL schema and it IS "code first". The naming can be misleading. – MemeDeveloper Aug 13 '19 at 07:15
0

I encountered this issue in .NET 6. Some background is that we use int for primary keys but a new requirement ment that parts of the system needed to be in sync with other systems and we could no longer depend on our int keys. We decided to use a hybrid approach with auto-increment primary key integer id column and a GUID column as described here:

https://dba.stackexchange.com/a/96990/80960

Basically the model looked like this:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Guid { get; set; }

Migration looked like this:

        migrationBuilder.AddColumn<Guid>(
            name: "Guid",
            table: "Products",
            type: "uniqueidentifier",
            nullable: false,
            defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));

ApplicationDbContextModelSnapshot.cs:

                b.Property<Guid>("Guid")
                    .ValueGeneratedOnAdd()
                    .HasColumnType("uniqueidentifier");

I thought this meant that every old value would get 00000000-0000-0000-0000-000000000000 and new values would receive ValueGeneratedOnAdd. However when I tested to add a new value I still got value 00000000-0000-0000-0000-000000000000. See image below:

enter image description here

I then tried to use defaultValue: Guid.NewGuid()); instead of defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); in migration.

enter image description here

This meant that every old and every new value I added got the same Guid but not 00000000-0000-0000-0000-000000000000.

I then switched over to manually add defaultValueSql: "NEWID()" instead of defaultValue: new Guid("00000000-0000-0000-0000-000000000000") in migration.

Now everything started working as expected both with old values and new values:

enter image description here

Now I wanted this functionality to be default for every new Guid added. I therefore modified ApplicationDbContext.cs method protected override void OnModelCreating(ModelBuilder modelBuilder) like this:

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(Guid)))
{
    property.SetDefaultValueSql("NEWID()");
}

Model ended up looking like this since [DatabaseGenerated(DatabaseGeneratedOption.Identity)] did not work in this case:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }

public Guid Guid { get; set; }
Ogglas
  • 62,132
  • 37
  • 328
  • 418
-3

Entity Framework Core Update:

There is no need to use [DatabaseGenerated(DatabaseGeneratedOption.Identity)].
There is no need to use fluent API

EF Core automatically take care of it and generates Id for primary key

Example:

public class DummyEntity
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

}

Seeding

    _context.DummyEntities.Add(new DummyEntity
    {
        FirstName = "Abc",
        LastName = "Def",
        Postion = "User",
    });

    _context.SaveChanges();
TheKingPinMirza
  • 7,924
  • 6
  • 51
  • 81
  • 1
    On EntityFrameworkCore 1.0.4 this wasn't enough. I had to use entity.Property(e => e.Id).ValueGeneratedOnAdd(); which did the trick for me. – Martin Kunc Sep 06 '17 at 06:30
  • 3
    Down-voted because it does not appear this is accurate. When I generate my migration, the defaultValue is set to new Guid("00000000-0000-0000-0000-000000000000")). Please update your answer. – Rogala Oct 31 '17 at 18:03
  • 1
    You're right that ef core takes care of it but it doesn't build a database table that properly takes care of auto generating that guid, for example in SQL server it doesn't setup a default or enable the auto generating property. – Bailey Miller Apr 30 '19 at 18:18
  • 1
    This answer is both wrong and extremely bad practice. there's so much wrong with this I would need a complete forum thread to correct all the issues in it. – War May 01 '19 at 11:48