Mubeen
Mubeen

Reputation: 431

Entity Framework Core Auto Generated guid

Can some One guide me I want primeryKey of a table as guid having db generated value on insert.

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

but it's giving error

The seed entity for entity type 'User' cannot be added because there was no value provided for the required property 'Id'.

Here is my actual model classes and DbContxt class:

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

     [Required]
     [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
     public DateTime CreatedOn { get; set; } = DateTime.UtcNow;


     [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
     public DateTime? UpdatedOn { get; set; }

     [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
     public DateTime LastAccessed { get; set; }
 }

 public class User : BaseModel
 {
        [Required]
        [MinLength(3)]
        public string Name { get; set; }

        [Required]
        [MinLength(3)]
        [EmailAddress]
        public string Email { get; set; }

        [Required]
        [MinLength(6)]
        public string Password { get; set; }
  }

Then in the MyDbContext:

public class MyDbContext: DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder mb)
        {
            base.OnModelCreating(mb);
            
            mb.Entity<User>().HasData(
                new User() { Email = "[email protected]", Name = "Mubeen", Password = "123123" },
                new User() { Email = "[email protected]", Name = "Tahir", Password = "321321" },
                new User() { Email = "[email protected]", Name = "Cheema", Password = "123321" }
                );
        }

        public DbSet<User> User { get; set; }
    }

Any help please!

Upvotes: 26

Views: 92706

Answers (8)

Marc Annous
Marc Annous

Reputation: 527

If all you need is getting an automatic value when creating and saving a new entity, you can also do it in the constructor:

public class BaseModel
{
   public BaseModel()
   {
      Id = new Guid();
   }

   [Key]
   public Guid Id { get; set; }

   public DateTime CreatedOn { get; set; } = DateTime.UtcNow;

   public DateTime? UpdatedOn { get; set; }

   public DateTime LastAccessed { get; set; }
}

Upvotes: 2

Diomedes Dom&#237;nguez
Diomedes Dom&#237;nguez

Reputation: 1101

  1. Create an extension method for easy use:
namespace Microsoft.EntityFrameworkCore
{
    using System;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;
    using System.Reflection;
    public static class ModelBuilderExtensions
    {
        public static void SetDefaultValuesTableName(this ModelBuilder modelBuilder)
        {
            foreach (var entity in modelBuilder.Model.GetEntityTypes().Where(x => x.ClrType.GetCustomAttribute(typeof(TableAttribute)) != null))
            {
                var entityClass = entity.ClrType;

                foreach (var property in entityClass.GetProperties().Where(p => (p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(Guid)) && p.CustomAttributes.Any(a => a.AttributeType == typeof(DatabaseGeneratedAttribute))))
                {
                    var defaultValueSql = "GetDate()";
                    if (property.PropertyType == typeof(Guid))
                    {
                        defaultValueSql = "newsequentialid()";
                    }
                    modelBuilder.Entity(entityClass).Property(property.Name).HasDefaultValueSql(defaultValueSql);
                }
            }
        }
    }
}
  1. Modify your OnModelCreating method
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.SetDefaultValuesTableName();
    }
  1. Profit

Source

Upvotes: 1

NamPT
NamPT

Reputation: 59

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
        {
            b.Property<string>("Id").HasMaxLength(36)
                .ValueGeneratedOnAdd();
});

Upvotes: 1

cmorgan091
cmorgan091

Reputation: 571

Rather than manually updating the migration, you could also implement an update in the OnModelCreating that adds it into the migration for you, e.g.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // guid identities
    foreach (var entity in modelBuilder.Model.GetEntityTypes()
        .Where(t =>
            t.ClrType.GetProperties()
                .Any(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DatabaseGeneratedAttribute)))))
    {
        foreach (var property in entity.ClrType.GetProperties()
            .Where(p => p.PropertyType == typeof(Guid) && p.CustomAttributes.Any(a => a.AttributeType == typeof(DatabaseGeneratedAttribute))))
        {
            modelBuilder
                .Entity(entity.ClrType)
                .Property(property.Name)
                .HasDefaultValueSql("newsequentialid()");
        }
    }

}  

Upvotes: 11

Ivan Stoev
Ivan Stoev

Reputation: 205939

The problem you are experiencing is not specific for autogenerated Guids. The same happens for any autogenerated key values, including the commonly used auto increment (identity) columns.

It's caused by a specific Data Seeding (HasData) requirement:

This type of seed data is managed by migrations and the script to update the data that's already in the database needs to be generated without connecting to the database. This imposes some restrictions:

  • The primary key value needs to be specified even if it's usually generated by the database. It will be used to detect data changes between migrations.
  • Previously seeded data will be removed if the primary key is changed in any way.

Note the first bullet. So while for normal CRUD your PK will be auto generated, you are required to specify it when using HasData fluent API, and the value must be constant (not changing), so you can't use Guid.NewGuid(). So you need to generate several Guids, take their string representation and use something like this:

mb.Entity<User>().HasData(
    new User() { Id = new Guid("pre generated value 1"), ... },
    new User() { Id = new Guid("pre generated value 2"), ... },
    new User() { Id = new Guid("pre generated value 3"), ... }
    );

Upvotes: 27

Kıvan&#231; B.
Kıvan&#231; B.

Reputation: 152

You can use defaultValueSql: "newid()" in your Code First Migration file.

For Example;

 public override void Up()
    {
        CreateTable(
            "dbo.ExampleTable",
            c => new
            {
                Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"),               
                RowGuid = c.Guid(nullable: false, defaultValueSql: "newid()"),

            })
            .PrimaryKey(t => t.Id);           
    }

Upvotes: 8

TanvirArjel
TanvirArjel

Reputation: 32159

[DatabaseGenerated(DatabaseGeneratedOption.Identity)] on GUID field works on Entity Framework 6.x, may be not in EF Core yet!

So the solution is:

1) First write your BaseModel class as follows:

public class BaseModel
{
    [Key]
    public Guid Id { get; set; }

    public DateTime CreatedOn { get; set; } = DateTime.UtcNow;

    public DateTime? UpdatedOn { get; set; }

    public DateTime LastAccessed { get; set; }
}

2) Then OnModelCreating() method in your DbContext should be as follows:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      base.OnModelCreating(modelBuilder);

      modelBuilder.Entity<YourEntity>().Property(x => x.Id).HasDefaultValueSql("NEWID()");

      modelBuilder.Entity<User>().HasData(
            new User() { Id  = Guid.NewGuid(), Email = "[email protected]", Name = "Mubeen", Password = "123123" },
            new User() { Id = Guid.NewGuid(), Email = "[email protected]", Name = "Tahir", Password = "321321" },
            new User() { Id = Guid.NewGuid(),  Email = "[email protected]", Name = "Cheema", Password = "123321" }
            );
 }

Now create a brand new migration and update the database accordingly. Hope your problem will be solved!

Upvotes: 17

Saif
Saif

Reputation: 2689

GUID is handled by Identity framework and not by EF, so to use GUID you need to do as the following

[Key]
public Guid Id { get; set; }

and then prepare the model before insert into table

new User() { Id = Guid.NewGuid(), Email = "[email protected]", Name = "Mubeen", Password = "123123" },

Upvotes: 4

Related Questions