I am having trouble seeding unidirectional relationships. The seeding looks as follows:
I'm receive this error on configuration
Unhandled exception. System.InvalidOperationException: The seed entity for entity type 'User' with the key value 'Id:1' cannot be added because it has the navigation 'Posts' set. To seed relationships, add the entity seed to 'Post' and specify the foreign key values {'UserId'}.
This error makes no sense to me, as I am already explicitly specifying the foreign key value Post.UserId...
Any help is much appreciated!
Here is my configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Models.User>()
.HasMany(user => user.Posts)
.WithOne()
.HasForeignKey(post => post.UserId)
.IsRequired(false);
Models.User user = new() { Id = 1, Username = "user" };
Models.Post post = new() { Id = 1, Body = "Hello from the other side", UserId = 1 };
user.Posts.Add(post);
modelBuilder.Entity<Models.User>().HasData(user);
modelBuilder.Entity<Models.Post>().HasData(post);
}
And here are my class definitions
public abstract class Base
{
[Key]
public int Id { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? UpdateAt { get; set; }
}
public class Post : Base
{
[Required] public string Body { get; set; }
public int? UserId { get; set; }
}
public class User : Base
{
[Required] public string Username { get; set; }
[ForeignKey(nameof(Post.UserId))] public ICollection<Post> Posts { get; set; } = new List<Post>();
}
I found the error in my implementation for anyone experiencing the same.
In the creation phase, you are not supposed to populate virtual collections, in this case User.Posts
, as the foreign key is what EF uses to populate the collections later on. Basicailly remove user.Posts.Add(post);
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Models.User>()
.HasMany(user => user.Posts)
.WithOne()
.HasForeignKey(post => post.UserId)
.IsRequired(false);
Models.User user = new() { Id = 1, Username = "user" };
Models.Post post = new() { Id = 1, Body = "Hello from the other side", UserId = 1 };
modelBuilder.Entity<Models.User>().HasData(user);
modelBuilder.Entity<Models.Post>().HasData(post);
}
On top of that, EF implements lazy loading behind the scenes, so when fetching data from the DB, you must include your virtual collections to populate them. This is in repositories/User.cs
:
public static Models.User? Get(string username)
{
using Contexts.Main context = new();
return context.Users.Include(user => user.Posts).SingleOrDefault(user => user.Username == username);
}