控制器中的 LINQ 查询在详细信息视图中将子项显示为 null,但在索引视图中不显示自引用关系

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

我有联系人模型的自引用关系,例如联系人可以与另一个联系人建立关系。在控制器 GET:详细信息方法中,我有以下代码:

var contact = await _context.Contacts
                            .Include(r => r.RelationshipsAsParent)
                            .ThenInclude(t => t.RelationshipType)
                            .FirstOrDefaultAsync(m => m.Id == id);

我可以拿到

ChildId
,但是孩子下面应该有
FullName
Contact

GET
索引查询显示它不为空,因此我可以访问那里的
FullName

Index
方法:

return _context.Contacts != null 
           ? View(await _context.Contacts
                                .Include(r => r.RelationshipsAsParent)
                                .ThenInclude(t => t.RelationshipType)
                                .ToListAsync()) 
           : Problem("Entity set 'ApplicationDbContext.Contacts' is null.");

我不明白为什么

Child
对于
Details
方法为空,但对于
Index
方法则不然。

提前感谢您的帮助:)

型号

public class Contact
{
    public int Id { get; set; }

    public string? FirstName { get; set; }
    public string? LastName { get; set; }

    [NotMapped]
    public string? FullName { get { return $"{FirstName} {LastName}"; } }

    // Navigation properties for relationships where this contact is the parent
    public virtual List<Relationship>? RelationshipsAsParent { get; set; }

    // Navigation properties for relationships where this contact is the child
    public virtual List<Relationship>? RelationshipsAsChild { get; set; }
}

public class Relationship
{
    public int Id { get; set; }

    public int? ParentId { get; set; }
    public virtual Contact? Parent { get; set; }

    // Foreign key and navigation property for the child contact
    public int? ChildId { get; set; }
    public virtual Contact? Child { get; set; }

    // Navigation property for the relationship type (assuming there's a RelationshipType entity)
    public int? RelationshipTypeId { get; set; }
    public virtual RelationshipType? RelationshipType { get; set; }
}

public class RelationshipType
{
    public int Id { get; set; }
    public string? Type { get; set; }

    public virtual List<Relationship>? Relationships { get; set; } = new List<Relationship>();
}

流畅的API

modelBuilder.Entity<Relationship>(b =>
        {
            modelBuilder.Entity<Relationship>()
            .HasOne(r => r.Parent)
            .WithMany(c => c.RelationshipsAsParent)
            .HasForeignKey(r => r.ParentId)
            .OnDelete(DeleteBehavior.Restrict);

            modelBuilder.Entity<Relationship>()
            .HasOne(r => r.Child)
            .WithMany(c => c.RelationshipsAsChild)
            .HasForeignKey(r => r.ChildId)
            .OnDelete(DeleteBehavior.Restrict);
        });

详细视图

<h3>Relationships</h3>
    <ul>
        @if (@Model.RelationshipsAsParent.Count > 0)
        {
            @for (int j = 0; j < Model.RelationshipsAsParent.Count; j++)
            {
                <li>@Model.RelationshipsAsParent[j].ChildId: @Model.RelationshipsAsParent[j].RelationshipType.Type</li>
            }
        }
        else
        {
            <li>No Relationships</li>
        }
    </ul>

索引视图

<div class="row ms-5">
  <ul>
  @if (item.RelationshipsAsParent != null)
    {
    @for (int j = 0; j < item.RelationshipsAsParent.Count; j++)
    {
    <li>@item.RelationshipsAsParent[j].Child.FullName: @item.RelationshipsAsParent[j].RelationshipType.Type</li>
    }
    }
    else
   {
    <li>No Relationships</li>
   }
  </ul>
</div>
c# entity-framework linq
1个回答
0
投票

这可能是因为“子项”是作为 Index 的 ToList 调用的副产品加载的。当您使用 DbContext 实例加载单个记录并且未启用延迟加载时,上下文将仅获取您使用

Include
急切加载的内容。

例如,如果我有一组记录 A、B 和 C,其中 A 是 B 的父级,C 是 B 的子级,我会加载该记录列表:

var records = _context.Records.ToList();

当我查看记录 B 时,我会看到它的父级和子级已填充,因为当上下文加载所有适用的实体并填充引用时。

在记录 B 的详细视图中,如果我执行:

var record = _context.Records
    .Include(x => x.Parent)
    .Single(x => x.Id == "B");

这将获取记录 B,它是父记录 (A),但不会返回子记录“C”,我们没有请求它。这种行为可能会不一致,因为您可能会发现有时子进程“确实”被加载,而有时却没有。这取决于 DbContext 是否恰好正在跟踪引用。如果我们有一些代码导致“C”被加载,那么它将被填充。例如: var temp = _context.Records.Single(x => x.Id == "C"); // Some other logic.... var record = _context.Records .Include(x => x.Parent) .Single(x => x.Id == "B");

这里我们不引用临时记录C,并且在获取“B”时我们不会急切地加载它,但因为DbContext正在跟踪实例,所以当它填充我们的“B”记录时,它将填充子引用指向“C”。

如果您正在加载数据并期望引用可用,您应该立即加载它。因此,当读取“B”并且您想要其父级和子级时,将它们包含在查询中或更好,使用投影来获取您想要的数据想要进入 ViewModel 或 ViewModel 图形。 (相关视图模型集)投影有助于最大限度地减少拉取的数据,您根本不需要担心急切加载的数据。 (使用

Include

Select
(自动映射器)时,不要使用
ProjectTo
)。
    

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