事实上,您想要的是:将实体拆分为一个通用的代表性部分和一个您并不总是想从数据库中提取的特殊部分。这不是一个不常见的要求。想想产品和图像、文件及其内容,或者拥有公共和私人数据的员工。
实体框架核心支持两种方式来实现这一点:拥有类型和表拆分。
拥有的类型
拥有的类型是包装在另一种类型中的类型。它只能通过其所有者访问。这是它的样子:
public class Post
{
public int ID { get; set; }
public Blog Blog { get; set; }
public string Title { get; set; }
public PostContent Content { get; set; }
}
public class PostContent
{
public string Content { get; set; }
}
还有自有类型映射:
modelBuilder.Entity<Post>().OwnsOne(e => e.Content);
Blog 在哪里
public class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public int ID { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
}
但是,根据docs:
在查询所有者时,默认情况下将包含拥有的类型。
这意味着像...这样的语句
var posts = context.Posts.ToList();
...总会让你发布和他们的内容。因此,自有类型可能不适合您。我还是提到了,因为我发现当Posts 是Included...
var blogs = context.Blogs.Include(b => b.Posts).ToList();
...所拥有的类型PostContents,不包括在内(免责声明:我不确定这是错误还是功能...)。在这种情况下,当应该包含拥有的类型时,ThenInclude 是必需的:
var blogs = context.Blogs.Include(b => b.Posts)
.ThenInclude(p => p.Content).ToList();
因此,如果始终通过Blogs 查询Posts,则拥有类型可能是合适的。
我认为这不适用于这里,但当拥有自己的类型的孩子与他们的父母有识别关系时(经典示例:Order-OrderLine)。
分表
通过表拆分,数据库表被拆分为两个或多个实体。或者,从对象方面:两个或多个实体映射到一个表。该模型几乎相同。唯一的区别是PostContent 现在有一个必需的主键属性(ID,当然与Post.ID 具有相同的值):
public class Post
{
public int ID { get; set; }
public Blog Blog { get; set; }
public string Title { get; set; }
public PostContent Content { get; set; }
}
public class PostContent
{
public int ID { get; set; }
public string Content { get; set; }
}
还有分表映射:
modelBuilder.Entity<Post>()
.HasOne(e => e.Content).WithOne()
// or .WithOne(c => c.Post) if there is a back reference
.HasForeignKey<PostContent>(e => e.ID);
modelBuilder.Entity<Post>().ToTable("Posts");
modelBuilder.Entity<PostContent>().ToTable("Posts");
现在Posts 将始终在默认情况下不包含其内容进行查询。 PostContent 应始终明确为 Include()-ed。
另外,PostContent 现在可以在没有其所有者Post 的情况下进行查询:
var postContents = context.Set<PostContent>().ToList();
我认为这正是您想要的。
当然,如果您在想要获取没有内容的帖子时总是使用投影,那么您可以不使用这些映射。