我正在尝试解决一个谜题,但到目前为止还没有成功。
我有一篇文章(或博客文章)和评论实体,它们都有内容。为了支持内容的延迟加载(当我需要显示文章或评论列表时不需要加载内容),我决定将内容移动到单独的表并组织一对一映射。这是我的想法的一个例子:
public class Content {
[Key]
public int ID { get; set; }
public string RawContent { get; set; }
// a bunch of scalar properties, like content type and so on
}
public class BlogArticle {
[Key]
public int ID { get; set; }
public int ContentID { get; set; }
[ForeignKey(nameof(ContentID)]
public virtual Content Text { get; set; }
// other properties related to BlogArticle
}
public class Comment {
[Key]
public int ID { get; set; }
public int ContentID { get; set; }
[ForeignKey(nameof(ContentID)]
public virtual Content Text { get; set; }
// other properties related to comment
}
<...>
乍一看似乎没问题:我可以创建博客文章、评论和附加内容(显然,首先我插入内容)。更新也有效。但是,删除不起作用:当我删除博客文章或评论时,内容并没有被删除(但我想在删除博客文章或评论时删除它,而不是相反)。
据我了解,由于关系方向,我最大的问题是:在我的例子中,Content
实体是主体,BlogArticle
和 Comment
是依赖的目的。为了解决这个难题,我需要改变主/从关系。同样,根据我的理解,为了改变关系方向,我需要在 Content
实体中拥有外键,并使用流畅的 API 来描述谁是父级(主体)、谁是子级(依赖)。一对一的关系。由于许多表(可能还有其他具有 content 属性的实体)都指向 Content
表,因此看起来并不容易。我的理解正确吗?
我能想到的一个可能的解决方案是在 Content
表中创建多个外键并指向每个相关表:
public class Content {
[Key]
public int ID { get; set; }
public string RawContent { get; set; }
// foreign keys
public int BlogArticleID { get; set; }
public int CommentID { get; set; }
public int WebWidgetID { get; set; }
// other foreign keys if necessary
}
可能,外键必须可以为空(因为一次只使用单个外键)。然后使用Entity Framework Fluent API来描述关系方向并组织级联删除。对我来说它看起来很难看,但我没有其他想法。
我的问题:我提出的解决方案好/可靠吗?我还可以考虑其他选择吗?
提前致谢!
最佳答案
你的所有想法都是正确的。您提出的解决方案是传统关系设计的唯一方法。当然,缺点是需要多个互斥的可空 FK。
我看到的其他选项如下:
(1) 使用 EF inheritance对于持有内容
的实体。例如
public abstract class EntityWithContent
{
[Key]
public int ID { get; set; }
public virtual Content Text { get; set; }
}
public class BlogArticle : EntityWithContent
{
// other specific properties
}
public class Comment : EntityWithContent
{
// other specific properties
}
并使用共享 PK 关联或 FK 关联在 Content
(从属)和 EntityWithContent
(主体)之间配置一对一关系。
但是由于 EF Core 目前仅支持 TPH 策略(即所有派生实体共享一个且所有字段并集的同一张表),因此我不会推荐它。
(2) 制作内容
owned type .
这更接近意图,但不幸的是 EF Core 目前总是将拥有的实体数据与所有者数据一起加载(即使它们配置为由不同的数据库表提供),这违背了您最初的目标,所以我也不会建议这样做。
(3) 使用 table splitting功能。
如果主要目标很简单,就是支持受控(惰性/急切/显式)加载,并且 Content
始终必需,那么这可能是迄今为止最好的解决方案.
这需要更多的配置,但最后它会给您原始的表设计(每个实体一个表)以及所需的加载行为:
型号:
public abstract class Content
{
[Key]
public int ID { get; set; }
public string RawContent { get; set; }
// a bunch of scalar properties, like content type and so on
}
public class BlogArticle
{
[Key]
public int ID { get; set; }
public virtual BlogArticleContent Text { get; set; }
// other properties related to BlogArticle
}
public class BlogArticleContent : Content
{
}
public class Comment
{
[Key]
public int ID { get; set; }
public virtual CommentContent Text { get; set; }
// other properties related to comment
}
public class CommentContent : Content
{
}
请注意,这里的 Content
类不是 EF 继承层次结构的一部分,而是具有公共(public)属性的简单基类(并不强烈需要 abstract
修饰符)。实际的派生类可能会也可能不会定义自己的属性。
配置:
modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
modelBuilder.Entity<BlogArticle>()
.HasOne(e => e.Text)
.WithOne()
.HasForeignKey<BlogArticleContent>(e => e.ID);
modelBuilder.Entity<BlogArticleContent>().ToTable("BlogArticles");
modelBuilder.Entity<Comment>().ToTable("Comments");
modelBuilder.Entity<Comment>()
.HasOne(e => e.Text)
.WithOne()
.HasForeignKey<CommentContent>(e => e.ID);
modelBuilder.Entity<CommentContent>().ToTable("Comments");
关于entity-framework - 多个表中的一对一映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49230065/