Понимание поведения отношения один-ко-многим в NPG SQL и Entity Framework - PullRequest
0 голосов
/ 10 февраля 2020

У меня есть две PostgreSQL таблицы, «stock_appreciation_rights» и «sar_vest_schedule», которые сопоставлены с классами «StockAppreciationRights» и «SarVestingUnit». Это соотношение: один SarVestingUnit связан с одним StockAppreciationRights, а один StockAppreciationRight связан со многими SarVestingUnit. Вот класс SarVestingUnit:

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:

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();
});

Вот поведение Я не понимаю Если я получу только StockAppreciationRights из моего dbcontext следующим образом:

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

Коллекция SarVestingUnits пуста в моих объектах StockAppreciationRights. Аналогично, если я просто получу SarVestingUnits, а не StockAppreciationRights, как это:

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

Свойство Sar в моих объектах SarVestingUnit равно нулю. Однако, если я получу их обоих так:

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

Тогда все будет заполнено так, как должно быть. Может кто-нибудь объяснить это поведение? Очевидно, что я новичок в структуре сущностей и PostgreSQL.

1 Ответ

0 голосов
/ 10 февраля 2020

Из вашего описанного поведения кажется, что отложенная загрузка либо отключена, либо недоступна (т. Е. EF Core <= 2.1) </p>

Ленивая загрузка назначит прокси для связанных ссылок, которые не были загружены, так что если эти к последующему доступу, запрос БД к DbContext будет сделан для их получения. Это позволяет получать данные «по мере необходимости», но может привести к значительному снижению производительности.

В качестве альтернативы вы можете загружать связанные данные. Например:

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

сообщит EF, чтобы загрузить права StockAppreciation и предварительно выбрать любые единицы передачи для каждой из этих записей. Вы можете использовать Include и ThenInclude для детализации и предварительной выборки любого количества зависимостей.

Причина, по которой ваш последний пример работает, заключается в том, что EF автоматически заполняет отношения, о которых знает контекст. Когда вы загружаете первый набор, он не разрешит ни одну из связанных сущностей, однако, загружая второй набор, EF уже знает о связанных других сущностях и автоматически установит эти ссылки для вас.

Реальная сила EF, однако, заключается не в том, чтобы иметь дело с такими объектами, как отображение 1: 1 в таблицы (вне редактирования / вставки), а в том, чтобы использовать проекцию для извлечения реляционных данных для заполнения того, что вам нужно. Использование Select или методов ProjectTo Automapper означает, что вы можете извлекать любые данные, которые вы хотите, через сопоставленные отношения 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 вы можете получить доступ к любым связанным деталям, чтобы сгладить их в модель представления, которая служит ваши непосредственные потребности или даже составьте иерархию моделей представлений, упрощенную для того, что нужно представлению / потребителю.

...