EntityFrameworkCore — фильтровать DbSet по одному свойству другого объекта

#c# #entity-framework-core

#c# #entity-framework-core

Вопрос:

У меня есть Account объект, для которого я хочу вызвать Subscription свойство CurrentSubscription .

 public class Account
{
    public int Id { get; set; }
    public Subscription CurrentSubscription { get; set; }
}

public class Subscription
{
    public int Id { get; set; }
    public int AccountId { get; set; }
    public Account Account { get; set; }
    public DateTime Start { get; set; }
    public DateTime? End { get; set; }
}
 

На самом деле для данной учетной записи может быть несколько Subscription строк, но бизнес-правило диктует, что когда-либо будет только одна (или ни одной) с EndDate of null ( Account текущая подписка). В моей модели меня не волнуют другие, когда я получаю учетную запись, поскольку я только после текущей.

Как я могу сказать EF, чтобы сделать это? Игра с fluent API не предполагает, что в него что-то встроено PropertyBuilder , и каждый HasDefaultValueSql найденный мной пример имеет тривиальное Sql значение, "GETDATE()" в отличие от чего-то параметризованного, что для этого нужно было бы ( ..where AccountId = ? ) .

Итак, я застрял .. есть идеи?

Комментарии:

1. » Как я могу сказать EF сделать это? » Вы не можете. То, что вы описываете, — это представление Account объекта, имеющего коллекцию Subscription s. Следовательно, не может быть отображен на уровне объекта и должен быть смоделирован как view (DTO / ViewModel и т. Д.) В общем случае модель сущности EF представляет модель хранилища, а не модель бизнес-правил.

Ответ №1:

Это должно сработать в вашем случае:

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

    //Collection automatically loaded by EF
    public virtual List<Subscription> Subscriptions { get; set; }

    // Returns Only (or none) subscription with null End date 
    public Subscription CurrentSubscription => Subscriptions.SingleOrDefault(r=>r.End == null);
}
 

Account.Subscriptions -коллекция автоматически (лениво) загружается Entity Framework, если внешний ключ правильно установлен в классе подписки (или с помощью Fluent API):

 public class Subscription
{
    public int Id { get; set; }
    public int AccountId { get; set; }
    [ForeignKey("AccountId")]
    public Account Account { get; set; }
    public DateTime Start { get; set; }
    public DateTime? End { get; set; }
}
 

Ответ №2:

На самом деле для данной учетной записи может быть несколько строк подписки, но бизнес-правило диктует, что когда-либо будет только одна (или ни одной) с конечной датой null (текущая подписка учетной записи). В моей модели меня не волнуют другие, когда я получаю учетную запись, поскольку я только после текущей. Как я могу сказать EF, чтобы сделать это?

Ядро Entity Framework имеет точное решение, которое вы ищете, и это отношения EF Core: взаимно однозначный.

Итак, ваша Subscription конфигурация объекта должна быть следующей:

 protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     base.OnModelCreating(modelBuilder);

     modelBuilder.Entity<Subscription>().HasOne(s => s.Account)
            .WithOne(a => a.CurrentSubscription).HasForeignKey<Subscription>(s => s.AccountId);
}
 

Более того, поскольку для so будет один или ноль Subscription Account , мы можем удалить Id столбец из Subscription объекта и сделать AccountId его первичным ключом Subscription объекта следующим образом:

 public class Subscription
{
    public int AccountId { get; set; }
    public Account Account { get; set; }
    public DateTime Start { get; set; }
    public DateTime? End { get; set; }
}
 

Тогда ваша Subscription конфигурация объекта должна быть следующей:

 protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     base.OnModelCreating(modelBuilder);

     modelBuilder.Entity<Subscription>(s =>
     {
        s.HasKey(sc => sc.AccountId); // <-- AccountId as primary key
        s.HasOne(sc => sc.Account).WithOne(a => a.CurrentSubscription)
                .HasForeignKey<Subscription>(sc => sc.AccountId);
     });
}