我正试图解决一个难题,但到目前为止还没有运气。
我有一篇文章(或博客文章)和评论实体,他们都有内容。为了支持内容的延迟加载(当我需要显示文章或评论列表时不需要加载内容)我决定将内容移动到单独的表并组织一对一映射。这是我想的一个例子:
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流畅的API来描述关系方向并组织级联删除。对我来说它看起来很难看,但我没有其他想法。
我的问题:我建议的解决方案是好还是可靠?我还可以看看其他选项吗?
提前致谢!
答案 0 :(得分:2)
你的所有想法都是正确的。您提出的解决方案是传统关系设计的唯一方法。当然缺点是需要多个互斥的可空FK。
我看到的其他选项如下:
(1)对持有Content
的实体使用EF inheritance。 e.g。
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)制作Content
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继承层次结构的一部分,但是具有公共属性(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");