首页 > 解决方案 > 为什么 .Include() 上没有加载相关实体?

问题描述

谁能告诉我为什么Article没有加载相关实体.Include(a => a.Article)NULL即使ArticleId确实具有价值,它们也总是如此。FrontPageItem和之间的关系Article是1-0..1。AnArticle可以存在而与 a 没有任何联系FrontPageItem,但 aFrontPageItem必须有一个Article

在一个相当丑陋的解决方法中,我已经foreach通过列表中的所有返回项目并Article手动添加,正如您在下面的索引方法中看到的那样。

public async Task<IActionResult> Index()
{
    List<FrontPageItem> items = await db.FrontPageItems
        .Include(a => a.Article)
            .ThenInclude(c => c.CreatedBy)
                .ThenInclude(m => m.Member)
        .Include(a => a.Article)
            .ThenInclude(e => e.EditedBy)
                .ThenInclude(m => m.Member)
        .Include(a => a.Article)
            .ThenInclude(e => e.PublishReadyBy)
                .ThenInclude(m => m.Member)
        .Include(p => p.WebPage)
        .OrderByDescending(o => o.DatePublished)
        .ToListAsync();
    // I don't want to foreach, but without it, Article is always NULL for all items.
    foreach (FrontPageItem item in items)
    {
        item.Article = await db.Articles
            .Where(a => a.Id == item.ArticleId).FirstOrDefaultAsync();
    }
    List<FrontPageItemViewModel> vm = 
        auto.Map<List<FrontPageItemViewModel>>(items);
    return View(vm);
}

这些是模型:

public class FrontPageItem
{
    public int Id { get; set; }
    // ... some more properties
    public int? ArticleId { get; set; }
    public Article Article { get; set; }

    public AdminUser CreatedBy { get; set; }
    public AdminUser EditedBy { get; set; }
    public AdminUser PublishedBy { get; set; }

}

public class Article
{
    public int Id { get; set; }
    // ... some more properties
    public int? FrontPageItemId { get; set; }
    public FrontPageItem FrontPageItem { get; set; }

    public AdminUser CreatedBy { get; set; }
    public AdminUser EditedBy { get; set; }
    public AdminUser PublishReadyBy { get; set; }
}

public class AdminUser
{
    public int Id { get; set; }
    // ... some more properties
    public int MemberId { get; set; }
    public Member Member { get; set; }
}

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    // ... some more properties
    public AdminUser AdminUser { get; set; }
}

这是模型构建器:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Article>()
        .HasOne(p => p.FrontPageItem)
        .WithOne(i => i.Article)
        .HasForeignKey<Article>(b => b.FrontPageItemId);
}

标签: c#linqasp.net-coreentity-framework-core

解决方案


首先你需要了解 Eager Loading、Lazy Loading、Explicit loading

急切加载

Eager Loading 可帮助您一次加载所有需要的实体;即,您的所有子实体都将在单个数据库调用中加载。这可以使用 Include 方法来实现,该方法将相关实体作为查询的一部分返回,并且一次加载大量数据。

延迟加载

这是实体框架的默认行为,其中子实体仅在第一次访问时才加载。它只是延迟加载相关数据,直到您提出要求。

显式加载

在实体框架中有禁用延迟加载的选项。关闭延迟加载后,您仍然可以通过显式调用相关实体的 Load 方法来加载实体。有两种方法可以使用 Load 方法 Reference(加载单个导航属性)和 Collection(加载集合)

因此,要加载关系实体,您可以使用 Microsoft.EntityFrameworkCore.Proxies 包来启用延迟加载。因此,当您查询数据库时,EF 将根据它们的关系返回关系数据库

我的示例代码看起来像这样

services.AddDbContextPool<ApplicationDbContext>(options =>
  options.UseLazyLoadingProxies().UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
        b => b.MigrationsAssembly("AwesomeCMSCore")).UseOpenIddict());

如果您有任何问题,请告诉我


推荐阅读