EF Core 8:获取子表计数作为模型的属性

问题描述 投票:0回答:1

好的,以前我们使用 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
类中?谢谢

database asp.net-core entity-framework-core migration dbcontext
1个回答
0
投票

不幸的是,许多 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()
) 进行读取操作。使用跟踪查询保留获取实体,包括在您想要修改这些实体的情况下预先加载相关实体。

© www.soinside.com 2019 - 2024. All rights reserved.