Entity Framework CORE Seeding Joining table
There is not much to say about the concrete issue (which btw is not specific to joining entity, but any entity model seeding):
I am seed
FileTypeId
but not sure why EF core migration throwing error...
as the cause of the issue is included at the beginning of the error message:
because it has the navigation 'FileType' set.
And your entity has
public FileType FileType { get; set; } = new FileType();
// ^ ^ ^
// the problem
and the same for
public JobProfile JobProfile { get; set; } = new JobProfile();
which will be the next error if you resolve the original.
Remove both navigation property initializers (= new ...
) and the problem will be gone.
As a general rule, you should never initialize reference navigation properties because it causes many side effects and/or improper behaviors (not only for seeding, but also eager/lazy/explicit data loading). Initializing collection navigation properties is arbitrary, but ok. Only reference navigation property initialization must be avoided. For more info, see EF codefirst : Should I initialize navigation properties? - quite old EF topic, but still applies.
If you are trying to resolve NRT warnings (as I guess), initializing with new
is definitely not a proper way. One reason I don't like NRT is because it is forcing people to use "workarounds" for preventing compiler warnings, which in fact break the primary functionality. Specifically in EF Core, enabling NRT also changes the optional/required attribute of some properties, hence database column types (most noticeable for string
properties/columns and reference navigations). You could read more about this in the Working with Nullable Reference Types topic in the official EF Core documentation, but in general I would just disable NRT for EF entity model classes.
How to seed an Admin user in EF Core 2.1.0?
As user cannot be seeded in a normal way in Identity just like other tables are seeded using .HasData()
of .NET Core 2.1.
Microsoft Recommendation: For data that requires calls to external API, such as ASP.NET Core Identity users creation it is recommended to use custom initialization logic.
Seed Roles in .NET Core 2.1 using code given below in ApplicationDbContext
Class :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Admin", NormalizedName = "Admin".ToUpper() });
}
Seed Users With Roles by Following the steps given below.
Step 1: New class creation
public static class ApplicationDbInitializer
{
public static void SeedUsers(UserManager<IdentityUser> userManager)
{
if (userManager.FindByEmailAsync("abc@xyz.com").Result==null)
{
IdentityUser user = new IdentityUser
{
UserName = "abc@xyz.com",
Email = "abc@xyz.com"
};
IdentityResult result = userManager.CreateAsync(user, "PasswordHere").Result;
if (result.Succeeded)
{
userManager.AddToRoleAsync(user, "Admin").Wait();
}
}
}
}
Step 2: Now Modify ConfigureServices
method in Startup.cs
class.
Before Modification:
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
After Modification:
services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Step 3: Modify parameters of Configure
Method in Startup.cs
class.
Before Modification :
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//..........
}
After modification :
public void Configure(IApplicationBuilder app, IHostingEnvironment env, UserManager<IdentityUser> userManager)
{
//..........
}
Step 4 : Calling method of our Seed (ApplicationDbInitializer
) class:
ApplicationDbInitializer.SeedUsers(userManager);
Note: You can also Seed Roles just like users by Injecting the RoleManager
along with UserManager
.
How to Seed self referencing tree using Entity Framework Core 2.2
The exception message is telling you that you cannot use navigation properties when seeding with HasData
method, but instead you could specify the relationships only via FK properties.
In other words, you can't use SubGenres
and ParentGenre
navigation properties for specifying the relations, they can be specified only via ParentId
property.
So remove the
SubGenres = DefaultSubGenresFactories.Action
line, and either consolidate the DefaultSubGenresFactories.Action
and DefaultGenresFactories.Action
to a single list of Genre
and use that list in HasData
call, or if you want to keep the DefaultGenresFactories
and DefaultSubGenresFactories
classes separate as they are currently, simply call HasData
for both (it's additive):
modelBuilder.Entity<Genre>().HasData(DefaultGenresFactories.Action);
modelBuilder.Entity<Genre>().HasData(DefaultSubGenresFactories.Action);
How to properly write seed method in Entity Framework Core?
Maybe it's because people only read the title, but everybody jumped to how to seed in Entity Framework, period. Next, some came up with AddOrUpdate
which doesn't exist in EF core.
Seeding in EF core has changed dramatically compared to EF6. In EF6's Seed
method it was possible to save an object graph, i.e. objects containing references and collections. However, the AddOrUpdate
method had a couple of issues (I won't spell them out here) that made its behavior very hard to follow or even get right if you weren't aware of them.
As an (over)reaction to that, the EF team decided to no longer let EF determine whether an entity should be added or updated. Seeding should either add an entity if it's non-existing, or do nothing otherwise. Never update. These considerations lead to a greatly (over)simplified seeding mechanism in EF core:
- Entities should be identified by their hard-coded primary keys
. If necessary, they're inserted withIDENTITY INSERT ON
(in Sql Server). - Foreign keys should also be hard coded.
- Navigation properties can't be populated. If they are, EF will throw an exception like
InvalidOperationException: The seed entity for entity type 'Studio' cannot be added because it has the navigation 'Games' set. To seed relationships you need to add the related entity seed to 'Game' and specify the foreign key values {'StudioId'}.
That means that you have to add StudioId
to Game
. And StudioId
and GenreId
to Person
. Independent associations (a Studio
reference without StudioId
) aren't supported. (I think this is a far-reaching architectural decision).
Doing that, your seeding code, simplified a bit, could look like:
var games = new[]
{
new Game{ Id = 1, Name = "Game1", StudioId = 1 },
new Game{ Id = 2, Name = "Game2", StudioId = 1 },
};
var studio = new Studio
{
Id = 1,
Name = "Studio1",
};
modelBuilder.Entity<Studio>().HasData(studio);
modelBuilder.Entity<Game>().HasData(games);
The circular reference Studio
⟷ Game
doesn't matter here because it represents only one foreign key.
However, circular references over two foreign keys are impossible. Suppose Studio
had a Director
property of type Person
referred to by DirectorId
and the director is also an employee:
var director = new Person { Id = 1, Name = "Director1", StudioId = 1 }; // Employee of Studio1
var studio = new Studio
{
Id = 1,
Name = "Studio1",
DirectorId = 1 // Director is "Director1"
};
Now there's a chicken-and-egg problem: both entities can only be inserted if the other one is inserted first.
InvalidOperationException: Unable to save changes because a circular dependency was detected in the data to be saved.
I think that's another far-reaching consequence of this design decision. In EF6, even with its crippled AddOrUpdate
, at least it was possible to call SaveChanges
twice to accommodate this scenario.
Considering that on top of all this, seeding isn't migration-friendly my stance on data seeding is: either don't support it (I wouldn't mind) or support it well.
How to seed entity framework core data with related data
About your questions:
- You just provide Id since you are seeding initial data
- You have to insert Stores, there is no such thing as "cascade insert", what would EF insert as Name in automatically generated Stores?
So you need also to seed your Stores like you are seeding Items:
modelBuilder.Entity<Store>().HasData(new Store {<properties>});
What would I also do, is to change you models like this:
public class Item
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int StoreId { get; set; } //or LocationId if you prefer
[Required]
public Store Location { get; set; }
}
public class Store
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
and you need to set this as foreign key for your Location object:
modelBuilder.Entity<Item>().HasOne(e => e.Location)
.WithMany(e => e.Items)
.HasForeignKey(e => e.StoreId)
.OnDelete(DeleteBehavior.Restrict);
after that you can just adjust your seed method to:
modelBuilder.Entity<Item>().HasData(new Item { Id = 1, StoreId = 1 });
Note that there is no need to instantiate Location object.
Related Topics
C# Optional Parameters on Overridden Methods
How to Bring My Application Window to the Front
Getting Serial Port Information
How to Convert Ticks to a Date Format
Is There a Generic Constraint I Could Use for the + Operator
Very Slow Compile Times on Visual Studio 2005
What Represents a Double in SQL Server
Using Process.Start() to Start a Process as a Different User from Within a Windows Service
ASP.NET MVC: How to Redirect a Non Www to Www and Vice Versa
Console.Writeline and Generic List
Signing and Verifying Signatures with Rsa C#
What Is the Real Overhead of Try/Catch in C#
How to Obfuscate My C# Code, So It Can't Be Deobfuscated So Easily
Is Inaccessible Due to Its Protection Level
How to Have an Enum Bound Combobox with Custom String Formatting for Enum Values
How to Compare Only Date Without Time in Datetime Types in Linq to SQL with Entity Framework