I'm playing wit EF Core 2.1 Preview 2.
I have troubles with HasData (Seed) method in OnModelCreating(ModelBuilder modelBuilder)
My model is simple POCO class that has no annotation.
public class Tenant {
public int TenantID {get; set;}
public string Name {get; set;}
}
in my DbContext
inside OnModelCreating
method is DB model defined as
modelBuilder.Entity<Tenant>(e => {
e.HasKey(m => m.TenantID)
.HasName("PK_Tenants");
e.Property(m => m.TenantID)
.UseSqlServerIdentityColumn();
e.Property(m => m.Name)
.IsRequired()
.HasMaxLength(256);
}
and the seed mehod is defined as:
modelBuilder.Entity<Tenant>().HasData(new []{
new Tenant {
TenantID = 0,
Name = "SystemTenant",
}
});
During startap, when ctx.Database.Migrate() is run, I got exception: The seed entity for entity type 'Tenant' cannot be added because there was no value provided for the required property 'TenantID
The exception is little bit misleading. There must be some mechanism inside, that tests required properties so they must be different to a default values.
The only change I had to do was specifying TenantID != 0
.
modelBuilder.Entity<Tenant>().HasData(new []{
new Tenant {
TenantID = 1, // Must be != 0
Name = "SystemTenant",
}
});
I have created little "hack" to bypass 0 PK value restriction after I reverse engineering the EF Core code and I found this line of code. Related to @Tomino answer. Here is my snipped extension code:
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace EntityFrameworkCore.CustomMigration
{
public static class CustomModelBuilder
{
public static bool IsSignedInteger(this Type type)
=> type == typeof(int)
|| type == typeof(long)
|| type == typeof(short)
|| type == typeof(sbyte);
public static void Seed<T>(this ModelBuilder modelBuilder, IEnumerable<T> data) where T : class
{
var entnty = modelBuilder.Entity<T>();
var pk = entnty.Metadata
.GetProperties()
.FirstOrDefault(property =>
property.RequiresValueGenerator()
&& property.IsPrimaryKey()
&& property.ClrType.IsSignedInteger()
&& property.ClrType.IsDefaultValue(0)
);
if (pk != null)
{
entnty.Property(pk.Name).ValueGeneratedNever();
entnty.HasData(data);
entnty.Property(pk.Name).UseSqlServerIdentityColumn();
}
else
{
entnty.HasData(data);
}
}
}
}
And you can use it like this in OnModelCreating
method:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Seed(new List<Tenant> {
new Tenant() {TenantID = 0 , Name = string.Empty},
new Tenant() {TenantID = 1 , Name = "test"}
//....
);
//....
}