好的,以前我们使用 ADO 并创建实体,但现在 Visual Studio Pro 2009 不再与 .NET 8 一起使用,它似乎是当今时代的目标。无论如何,除此之外我还有一个简单的问题。
假设我创建了一个
Blog
类:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
public ICollection<Post> Posts { get; }
}
然后我创建一个
Post
类:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedOn { get; set; }
public bool Archived { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
在
DbContext
中我包括:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.HasPrincipalKey(e => e.Id);
}
问题是,我需要做什么才能使
Blog
类拥有一个额外的属性,显示与 BlogId
与 Post
表匹配的帖子数量?
基本上是一个显示博客有多少帖子的属性。我是否需要专注于修改此功能并将其添加到模型或
DbContext
类中?谢谢
不幸的是,许多 EF 示例要么使用实体效率低下,要么演示如何执行某些操作(例如急切加载集合)的最简单示例,但它被解释为“这就是访问数据的方式”。实体类充当数据域表示,同时您还将拥有视图域或传输域。
因此,博客和帖子的简单示例可能类似于:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string SiteUri { get; set; }
public DateTime CreatedDateTime { get; set; }
public virtual ICollection<Post> Posts { get; } = [];
[NotMapped]
public int PostCount => Posts.Count();
}
虽然这可行,但该方法存在一些问题。首先,除非 Posts 集合被急切加载,或者延迟加载可用,否则 PostCount 将不可用;如果没有急切加载,并且
DbContext
恰好正在跟踪与该博客相关的一些帖子,则可能是不完整/不准确的计数。
PostCount 不是一个数据问题。我们不会对博客表进行非规范化以包含 PostCount,然后担心如何在添加和删除博客的帖子时使其与帖子保持同步。 PostCount 是一个视图问题,最好通过为相关视图可能需要的域定义 POCO ViewModel 类来实现这一点。例如,如果我们想显示博客列表并包含帖子计数:
public class BlogSummaryViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int PostCount { get; set; }
}
然后当我们想要获取博客列表来填充视图时:
var blogs = await _context.Blogs
.AsNoTracking()
.OrderByDescending(b => b.CreatedDateTime)
.Select(b => new BlogSummaryViewModel
{
Id = b.Id,
Name = b.Name,
PostCount = b.Posts.Count()
}).Skip(pageNumber * pageSize)
.Take(pageSize)
.ToListAsync();
我们可以包含有关帖子的其他详细信息,例如获取最新的帖子日期等。Projection 使用实体关系来组成查询以从数据库中获取我们需要的数据。我们不需要担心急切加载关系,使用 Linq 表达式中的导航属性可以让 EF 计算出需要哪些表。
这是一个非常重要的细节,为什么开发人员应该避免像通用存储库模式这样的东西。抽象 EF
DbSet
会干扰投影等优势。
作为构建高效查询的一般规则,使用投影 (
Select()
) 进行读取操作。使用跟踪查询保留获取实体,包括在您想要修改这些实体的情况下预先加载相关实体。