Force Entity Framework 6.1 to use exists instead of populating entire child object graph

Viewed 59

---------------- describing why this is not a duplicate -------------

EDIT: This is different than the question marked as a duplicate because I am not trying to re-query the list that is returned (and further refine it). E.G. If I change the ClientProviderAccountRep.GetByProviderID method from a .Where to a .First(), I would still expect the following to be optimized with an EXISTS:

If I change the GetByProviderID to this:

public ClientProviderAccount GetByProviderID(Guid providerID)
{
    return Db.ClientProviderAccounts.First(cpa => cpa.IsActive && cpa.ProviderID == providerID);
}

Then use that method here:

var clientProviderAccount = ClientProviderAccountRep.GetByProviderID(providerId);
var schedulesExist = clientProvider.Schedules.Any();

Our application uses entity framework 6.1. The entity can have a large collection of children (schedules). We have a method that is transforming the EF entity into a viewmodel entity. For the purpose of the ViewModel, it does not need all the schedules. It just needs to know if one exists. I expected using .Any() would not get the query and create the whole list of schedules.

var lst = ClientProviderAccountRep.GetByProviderID(providerID).Select(x => new ClientProviderAccountVM
            {
                ClientProviderAccountID = x.ClientProviderAccountID,
                ClientID = x.ClientID,
                ClientName = x.Client.Name,
                ProviderID = x.ProviderID,
                ProviderName = x.Provider.Name,
                Name = x.Name,
                LoginID = x.LoginID,
                Password = x.Password,
                StartRecordNumber = x.StartRecordNumber,
                IsActive = x.IsActive,
                SchedulesExist = x.Schedules.Any(s => s.IsActive == true)
            })

The problem line is:

SchedulesExist = x.Schedules.Any(s => s.IsActive == true)

I would have expected a really quick EXISTS call, but it is getting all the schedule objects first.

Things I have tried:

  1. foreach instead of the .GetByProviderID(providerID).Select(x. then creating ViewModel with the object initializer syntax.
  2. in a loop, creating a new ViewModel and setting each property explicitly.

I am pretty sure I tried other permutations, but have not been successful.

here is the generated DbContext:

public partial class AppLogDb : DbContext
{
    public AppLogDb()
        : base("name=AppLogDb")
    {
        this.Configuration.LazyLoadingEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<ApplicationLog> ApplicationLogs { get; set; }
    public virtual DbSet<AuditLog> AuditLogs { get; set; }
    public virtual DbSet<LogDetail> LogDetails { get; set; }
    public virtual DbSet<LoginLog> LoginLogs { get; set; }
}

Here is the GetByProviderID method. The Db is the database context. I know there is a too list, but I don' think that is it because it is still lazy loading the schedules after returning from this method. The ToLis(), just realizes the top parent object (I think).

public IEnumerable<ClientProviderAccount> GetByProviderID(Guid providerID)
{
    var clientProviderAccount = Db.ClientProviderAccounts
        .Where(cpa => cpa.IsActive && cpa.ProviderID == providerID)
        .ToList();

    return clientProviderAccount.OrderBy(cpa => cpa.Name);
0 Answers
Related