简体   繁体   中英

EntityFrameworkCore - Filter DbSet to a single property on another Entity

I have an Account entity, on which I want to have a Subscription property called 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; }
}

In reality, there could be multiple Subscription rows for a given account but a business rule dictates that there will only ever be one (or none) with an EndDate of null (the Account 's current subscription). In my model I do not care about the others when I am retrieving an account as I am only after the current one.

How can I tell EF to do this? Playing around with the fluent API doesn't suggest there is anything built in on PropertyBuilder and every example of HasDefaultValueSql I have found has trivial Sql like "GETDATE()" as opposed to something parameterised, which this would need to be ( ..where AccountId = ? ).

So I'm stuck.. any ideas?

This should work in your case:

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 -collection is automatically (lazy) loaded by Entity Framework if foreign key is proper set on Subscription class (or by 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; }
}

In reality, there could be multiple Subscription rows for a given account but a business rule dictates that there will only ever be one (or none) with an EndDate of null (the Account's current subscription). In my model I do not care about the others when I am retrieving an account as I am only after the current one. How can I tell EF to do this?

Entity Framework Core has exact solution that you are searching for and that is EF Core Relationships : One-to-One .

So your Subscription entity configuration should be as follows:

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

Moreover as there will be one or zero Subscription for Account so we can remove Id column from Subscription entity and make the AccountId as the primary key of the Subscription entity as follows:

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

Then your Subscription entity configuration should be as follows:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM