了解 NPGSQL 和 Entity Framework 中一对多关系的行为

Understanding behavior of one-to-many relationship in NPGSQL and Entity Framework

我有两个 PostgreSQL 表,"stock_appreciation_rights" 和 "sar_vest_schedule",它们映射到 classes "StockAppreciationRights" 和 "SarVestingUnit"。关系是这样的:一个SarVestingUnit关联一个StockAppreciationRights,一个StockAppreciationRight关联多个SarVestingUnit。这是 SarVestingUnit class:

public class SarVestingUnit
{
    public string UbsId { get; set; }
    public short Units { get; set; }
    public DateTime VestDate { get; set; }
    public virtual StockAppreciationRights Sar { get; set; }
}

这是 StockAppreciationRights class:

public class StockAppreciationRights
{
    public StockAppreciationRights()
    {
        SarVestingUnits = new HashSet<SarVestingUnit>();
    }

    public string UbsId { get; set; }
    public DateTime GrantDate { get; set; }
    public DateTime ExpirationDate { get; set; }
    public short UnitsGranted { get; set; }
    public decimal GrantPrice { get; set; }
    public short UnitsExercised { get; set; }
    public virtual ICollection<SarVestingUnit> SarVestingUnits { get; set; }
}

还有来自我的 dbContext 的片段:

modelBuilder.Entity<SarVestingUnit>(entity =>
{
    entity.HasKey(e => new { e.UbsId, e.VestDate })
        .HasName("sar_vest_schedule_pkey");

    entity.ToTable("sar_vest_schedule");

    entity.Property(e => e.UbsId)
        .HasColumnName("ubs_id")
        .HasColumnType("character varying");

    entity.Property(e => e.VestDate)
        .HasColumnName("vest_date")
        .HasColumnType("date");

    entity.Property(e => e.Units).HasColumnName("units");

    entity.HasOne(d => d.Sar)
        .WithMany(p => p.SarVestingUnits)
        .HasForeignKey(d => d.UbsId)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("sar_vest_schedule_ubs_id_fkey")
        .IsRequired();
});

这是我不理解的行为。如果我像这样从我的 dbcontext 中获得 StockAppreciationRights:

var x = new personalfinanceContext();
//var test = x.SarVestingUnit.ToList();
var pgSARS = x.StockAppreciationRights.ToList();

我的 StockAppreciationRights 对象中的 SarVestingUnits 集合是空的。 同样,如果我只获得 SarVestingUnits 而不是像这样的 StockAppreciationRights:

var x = new personalfinanceContext();
var test = x.SarVestingUnit.ToList();
//var pgSARS = x.StockAppreciationRights.ToList();

我的 SarVestingUnit 对象中的 Sar 属性 为空。 但是,如果我像这样得到它们:

var x = new personalfinanceContext();
var test = x.SarVestingUnit.ToList();
var pgSARS = x.StockAppreciationRights.ToList();

然后一切都按原样填充。有人可以解释这种行为吗?显然我是 entity framework 和 PostgreSQL 的新手。

从您描述的行为来看,延迟加载似乎被禁用或不可用(即 EF Core <=2.1)

延迟加载会为其尚未加载的相关引用分配代理,以便如果以后访问这些引用,将对 DbContext 进行数据库查询以检索它们。这允许检索数据 "as required" 但会导致严重的性能损失。

或者,您可以预先加载相关数据。例如:

var pgSARS = context.StockAppreciationRights.Include(x => x.SarVestingUnits).ToList();

会告诉 EF 加载 StockAppreciation 权利并为每个条目预取任何归属单位。您可以使用 IncludeThenInclude 向下钻取并预取任意数量的依赖项。

您的最后一个示例似乎有效的原因是 EF 将自动填充上下文知道的关系。当您加载第一组时,它不会解析任何相关实体,但是,加载第二组并且 EF 已经知道相关的其他实体并将为您自动设置这些引用。

虽然 EF 的真正强大之处不是处理实体,例如 1 对 1 映射到表(editing/inserting 之外),而是利用投影来提取关系数据来填充您需要的内容。利用 Select 或 Automapper 的 ProjectTo 方法意味着您可以通过映射的 EF 关系提取您想要的任何数据,并且 EF 可以为您构建一个高效的查询来获取它,而不用担心延迟加载或急切加载。例如,如果您想要获得一份包含相关权利详细信息的归属单位列表:

var sars = context.StockAppreciationRights.Select(x => new SARSummary
{
    UbsId = x.UbsId,
    Units = x.Units,
    VestDate = x.VestDate,
    GrantDate = x.Sar.GrantDate,
    ExpirationDate = x.Sar.ExpirationDate,
    UnitsGranted = x.Sar.UnitsGranted,
    GrantPrice = x.Sar.GrantPrice
}).ToList();

Select 语句中,您可以访问任何相关详细信息以展平到满足您即时需求的视图模型中,甚至可以组成视图模型的层次结构,从而简化了 [=33= 】 需要。