如何使 3 个实体之间的一对多关系与 Entity Framework Core 2.1 一起工作 - 代码优先
How to make one-to-many relationship between 3 entities work with Entity Framework Core 2.1 - code-first
我有 3 个实体通过一对多关系相互映射,其中一个应该映射到它们两个,这导致了不需要的多对多关系。
映射背后的逻辑如下:
- 用户可以拥有多个Post; Post 只有一个用户。
- 用户可以有多个类别;类别只有一个用户。
- 类别可以有很多帖子; Post 只有一个类别。
这是我的代码:
用户
Table("Users")]
Public Class User
{
[Key]
public int UserId { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string UserName { get; set; }
public DateTime CreatedAt { get; set; }
public ICollection<Posts> Posts { get; set; }
public ICollection<Category> Categories { get; set; }
}
Post
[Table("Posts")]
public class Post
{
[Key]
public int PostId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Text { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
类别
[Table("Categories")]
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public ICollection<Post> Posts { get; set; }
}
我的数据库上下文
public class BlogContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Post> Posts{ get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.HasMany<Post>(s => s.Posts)
.WithOne(u => u.User)
.HasForeignKey(s => s.UserId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasMany<Category>(c => c.Categories)
.WithOne(u => u.User)
.HasForeignKey(c => c.UserId)
.OnDelete(DeleteBehavior.Restrict);
entity.Property(u => u.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
.HasForeignKey(s => s.PostId);
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
.HasForeignKey(s => s.PostId);
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
modelBuilder.Entity<Category>(entity =>
{
entity.HasMany<Post>(c => c.Posts)
.WithOne(s => s.Category)
.HasForeignKey(c => c.PostId)
.OnDelete(DeleteBehavior.Restrict);
entity.Property(c => c.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
}
}
我运行插入帖子时遇到这个问题:
The property 'PostId' on entity type 'Post' has a temporary value
我的理解是,我用实现 DbContext 的方式造成了多对多关系。我该如何解决?感谢您的帮助。
你在
中犯了一个错误
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
.HasForeignKey(s => s.PostId);
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
.HasForeignKey(s => s.PostId);
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
Table类别模型没有PostId,User模型也没有。纠正这个。
您没有正确配置外键。让我们回顾一下您的代码 Entity<Post>(entity=>{ /* ... */ })
:
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
.HasForeignKey(s => s.PostId); // line A: it is not correct!
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
.HasForeignKey(s => s.PostId); // line B: it is not correct!
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
请注意,A 行和 B 行不正确。在这两种情况下,依赖实体都是 Post
。您的代码将产生如下外键:
CREATE TABLE [Posts] (
[PostId] int NOT NULL,
[Title] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NOT NULL,
[Text] nvarchar(max) NOT NULL,
[CreatedAt] datetime2 NOT NULL DEFAULT (SYSUTCDATETIME()),
[UpdatedAt] datetime2 NULL,
[UserId] int NOT NULL,
[CategoryId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId]),
CONSTRAINT [FK_Posts_Categories_PostId] FOREIGN KEY ([PostId]) REFERENCES [Categories] ([CategoryId]) ON DELETE NO ACTION,
CONSTRAINT [FK_Posts_Users_PostId] FOREIGN KEY ([PostId]) REFERENCES [Users] ([UserId]) ON DELETE NO ACTION
);
因此您需要如下更改代码:
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
//.HasForeignKey(s => s.PostId);
.HasForeignKey(s => s.CategoryId);
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
//.HasForeignKey(s => s.PostId);
.HasForeignKey(s => s.UserId);
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
因为同样的原因,你也应该把Entity<Category>(entity=>{ /* ... */ })
的部分改成如下:
modelBuilder.Entity<Category>(entity =>
{
entity.HasMany<Post>(c => c.Posts)
.WithOne(s => s.Category)
//.HasForeignKey(c => c.PostId) // remove this line
.HasForeignKey(c => c.CategoryId) // add this line
.OnDelete(DeleteBehavior.Restrict);
entity.Property(c => c.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
我有 3 个实体通过一对多关系相互映射,其中一个应该映射到它们两个,这导致了不需要的多对多关系。
映射背后的逻辑如下:
- 用户可以拥有多个Post; Post 只有一个用户。
- 用户可以有多个类别;类别只有一个用户。
- 类别可以有很多帖子; Post 只有一个类别。
这是我的代码:
用户
Table("Users")]
Public Class User
{
[Key]
public int UserId { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string UserName { get; set; }
public DateTime CreatedAt { get; set; }
public ICollection<Posts> Posts { get; set; }
public ICollection<Category> Categories { get; set; }
}
Post
[Table("Posts")]
public class Post
{
[Key]
public int PostId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public string Text { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
类别
[Table("Categories")]
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public ICollection<Post> Posts { get; set; }
}
我的数据库上下文
public class BlogContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Post> Posts{ get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.HasMany<Post>(s => s.Posts)
.WithOne(u => u.User)
.HasForeignKey(s => s.UserId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasMany<Category>(c => c.Categories)
.WithOne(u => u.User)
.HasForeignKey(c => c.UserId)
.OnDelete(DeleteBehavior.Restrict);
entity.Property(u => u.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
.HasForeignKey(s => s.PostId);
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
.HasForeignKey(s => s.PostId);
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
modelBuilder.Entity<Category>(entity =>
{
entity.HasMany<Post>(c => c.Posts)
.WithOne(s => s.Category)
.HasForeignKey(c => c.PostId)
.OnDelete(DeleteBehavior.Restrict);
entity.Property(c => c.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
}
}
我运行插入帖子时遇到这个问题:
The property 'PostId' on entity type 'Post' has a temporary value
我的理解是,我用实现 DbContext 的方式造成了多对多关系。我该如何解决?感谢您的帮助。
你在
中犯了一个错误 modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
.HasForeignKey(s => s.PostId);
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
.HasForeignKey(s => s.PostId);
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
Table类别模型没有PostId,User模型也没有。纠正这个。
您没有正确配置外键。让我们回顾一下您的代码 Entity<Post>(entity=>{ /* ... */ })
:
modelBuilder.Entity<Post>(entity => { entity.HasOne<Category>(s => s.Category) .WithMany(c => c.Posts) .HasForeignKey(s => s.PostId); // line A: it is not correct! entity.HasOne<User>(s => s.User) .WithMany(u => u.Posts) .HasForeignKey(s => s.PostId); // line B: it is not correct! entity.Property(s => s.CreatedAt) .HasDefaultValueSql("SYSUTCDATETIME()"); });
请注意,A 行和 B 行不正确。在这两种情况下,依赖实体都是 Post
。您的代码将产生如下外键:
CREATE TABLE [Posts] (
[PostId] int NOT NULL,
[Title] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NOT NULL,
[Text] nvarchar(max) NOT NULL,
[CreatedAt] datetime2 NOT NULL DEFAULT (SYSUTCDATETIME()),
[UpdatedAt] datetime2 NULL,
[UserId] int NOT NULL,
[CategoryId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId]),
CONSTRAINT [FK_Posts_Categories_PostId] FOREIGN KEY ([PostId]) REFERENCES [Categories] ([CategoryId]) ON DELETE NO ACTION,
CONSTRAINT [FK_Posts_Users_PostId] FOREIGN KEY ([PostId]) REFERENCES [Users] ([UserId]) ON DELETE NO ACTION
);
因此您需要如下更改代码:
modelBuilder.Entity<Post>(entity =>
{
entity.HasOne<Category>(s => s.Category)
.WithMany(c => c.Posts)
//.HasForeignKey(s => s.PostId);
.HasForeignKey(s => s.CategoryId);
entity.HasOne<User>(s => s.User)
.WithMany(u => u.Posts)
//.HasForeignKey(s => s.PostId);
.HasForeignKey(s => s.UserId);
entity.Property(s => s.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});
因为同样的原因,你也应该把Entity<Category>(entity=>{ /* ... */ })
的部分改成如下:
modelBuilder.Entity<Category>(entity =>
{
entity.HasMany<Post>(c => c.Posts)
.WithOne(s => s.Category)
//.HasForeignKey(c => c.PostId) // remove this line
.HasForeignKey(c => c.CategoryId) // add this line
.OnDelete(DeleteBehavior.Restrict);
entity.Property(c => c.CreatedAt)
.HasDefaultValueSql("SYSUTCDATETIME()");
});