Linq To Sql Entity Framework Getting Nested Entities - entity-framework

I'm trying to do an include of a nested entity. I assumed the below code would work but it does not recognize the CapitalMailOrders entitycollection. Can anyone point me in the right direction to include these?
var result = db.Contacts
.Include(a => a.IDXPageLinks)
.Include(b => b.ReboGatewayLoginInfoes)
.Include(c => c.SocialMedias)
.Include(d => d.WebSiteInfoes)
.Include(e => e.ContactImages)
.Include(f => f.RealtorSetUpProcesses)
.Include(h => h.RealtorSetUpProcesses.CapitalMailOrders) // getting compile time error here. Doesn't recognize Entity
.Include(g => g.Contact_CarrierCode_Assignments)
.FirstOrDefault(c => c.ContactID == id);
Thanks

The extra entity level is accessed from a collection and so you need to add a Select in order to bring all the entries into the include.
.Include(h => h.RealtorSetUpProcesses.Select(h2 => h2.CapitalMailOrders)

Related

EF Core: Getting the `SUM` of nested collection

I have this code:
decimal returned = await db.ReturnSalesDeliveries
.Include(m => m.Items).ThenInclude(m => m.ReturnSCItem)
.Include(m => m.ReturnSalesContract)
.Where(m => m.ReturnSalesContract.SalesDeliveryId == sdId)
.Select(m => m.Items.Sum(x => x.InQty * x.ReturnSCItem.Price)).DefaultIfEmpty().SumAsync();
It was fine when I write this using EF, but it was not working on EF Core.
Apparently it cannot be translated to SQL.
This is the error:
Cannot perform an aggregate function on an expression containing an
aggregate or a subquery.
How can I rewrite this?
Rewrite query in the following way and note that Includes are not needed if you do not load whole entity.
decimal returned = await db.ReturnSalesDeliveries
.Where(m => m.ReturnSalesContract.SalesDeliveryId == sdId)
.SelectMany(m => m.Items)
.SumAsync(x => x.InQty * x.ReturnSCItem.Price);

Entity Framework Core .Include on related entities but wanting one of the Includes to be conditional?

I am loading a customer and its related entities using the following (this is .NET Core 3.1 and Entity Framework Core).
var customer = loyaltyContext.Customer
.Include(c => c.ContactInformation)
.Include(c => c.Address)
.Include(c => c.MarketingPreferences)
.Include(c => c.ContentTypePreferences)
.Include(c => c.ExternalCards)
.Where(c => c.CustomerInternalId == customerInternalId).First();
However, a customer can have more than one ExternalCard, but only one card will be 'ACTIVE', so in pseudo code, what I'd really like to do is:
var customer = loyaltyContext.Customer
.Include(c => c.ContactInformation)
.Include(c => c.Address)
.Include(c => c.MarketingPreferences)
.Include(c => c.ContentTypePreferences)
.Include(c => c.ExternalCards WHERE CardStatus == "ACTIVE")
.Where(c => c.CustomerInternalId == customerInternalId).First();
So I don't return the last 10 cards the customer had (but which are now inactive). Is this possible in any way using EF Core? I can't seem to find the right syntax for this. At least not without having to create additional queries (whereas I'd rather just bring back less data to begin with).
ExternalCards is an ICollection< ExternalCards > as one customer can have many cards.
Filtering include option is not available in EF Core version <= 3.1. It's a new feature in EF Core 5.0. Here it is for EF Core 5.0 : Filtered Include
For EF Core <=3.1, You have to do the filtering in memory as follows:
var customer = loyaltyContext.Customer
.Include(c => c.ContactInformation)
.Include(c => c.Address)
.Include(c => c.MarketingPreferences)
.Include(c => c.ContentTypePreferences)
.Include(c => c.ExternalCards)
.Where(c => c.CustomerInternalId == customerInternalId).First();
customer.ExternalCards = customer.ExternalCards.Where(ec => ec.CardStatus == "ACTIVE").ToList();

How to select top N rows for each group in a Entity Framework GroupBy with EF 3.1

I need to get top 10 rows for each group in a table with entity framework.
Based on other solution on SO, I tried 2 things:
var sendDocuments = await context.Set<DbDocument>
.Where(t => partnerIds.Contains(t.SenderId))
.GroupBy(t => t.SenderId)
.Select(t => new
{
t.Key,
Documents = t.OrderByDescending(t2 => t2.InsertedDateTime).Take(10)
})
.ToArrayAsync();
error:
System.InvalidOperationException: 'The LINQ expression
'(GroupByShaperExpression: KeySelector: (d.SenderId),
ElementSelector:(EntityShaperExpression:
EntityType: DbDocument
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False ) )
.OrderByDescending(t2 => t2.InsertedDateTime)' could not be translated. Either rewrite the query in a form that can be translated,
> or switch to client evaluation explicitly by inserting a call to
> either AsEnumerable(), AsAsyncEnumerable(), ToList(), or
> ToListAsync().
and
var sendDocuments2 = await context.Set<DbDocument>
.Where(t => partnerIds.Contains(t.SenderId))
.GroupBy(t => t.SenderId)
.SelectMany(t => t.OrderByDescending(t2 => t2.InsertedDateTime).Take(10))
.ToArrayAsync();
error:
System.InvalidOperationException: 'Processing of the LINQ expression
't => t
.OrderByDescending(t2 => t2.InsertedDateTime)
.AsQueryable()
.Take(10)' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core.
Any other idea?
Update (EF Core 6.0):
EF Core 6.0 added support for translating GroupBy result set projection, so the original code for taking (key, items) now works as it should, i.e.
var query = context.Set<DbDocument>()
.Where(e => partnerIds.Contains(e.SenderId))
.GroupBy(e => e.SenderId)
.Select(g => new
{
g.Key,
Documents = g.OrderByDescending(e => e.InsertedDateTime).Take(10)
});
However flattening (via SelectMany) is still unsupported, so you have to use the below workaround if you need such query shape.
Original (EF Core 3.0/3.1/5.0):
This is a common problem, unfortunately not supported by EF Core 3.0/3.1/5.0 query translator specifically for GroupBy.
The workaround is to do the groping manually by correlating 2 subqueries - one for keys and one for corresponding data.
Applying it to your examples would be something like this.
If you need (key, items) pairs:
var query = context.Set<DbDocument>()
.Where(t => partnerIds.Contains(t.SenderId))
.Select(t => t.SenderId).Distinct() // <--
.Select(key => new
{
Key = key,
Documents =
context.Set<DbDocument>().Where(t => t.SenderId == key) // <--
.OrderByDescending(t => t.InsertedDateTime).Take(10)
.ToList() // <--
});
If you need just flat result set containing top N items per key:
var query = context.Set<DbDocument>()
.Where(t => partnerIds.Contains(t.SenderId))
.Select(t => t.SenderId).Distinct() // <--
.SelectMany(key => context.Set<DbDocument>().Where(t => t.SenderId == key) // <--
.OrderByDescending(t => t.InsertedDateTime).Take(10)
);

Linq include nested collection and orderby

I have nested entities like Property -> Facility -> Farm -> License etc.
I want to get the most recent license and my original query looks like this.
return _db.Properties
.Include(c => c.Contact)
.Include(f => f.Facilities.Select(c => c.Contact))
.Include(f => f.Facilities.Select(ff => ff.Farms.Select(s => s.Species)))
.Include(x => x.Facilities.Select(l => l.Licenses))
.SingleOrDefault(x => x.PropertyID == id);
Since I want to get the most recent license I tried
return _db.Properties
.Include(c => c.Contact)
.Include(f => f.Facilities.Select(c => c.Contact))
.Include(f => f.Facilities.Select(ff => ff.Farms.Select(s => s.Species)))
.Include(x => x.Facilities.Select(l => l.Licenses.OrderByDescending(d => d.IssueDate)))
.SingleOrDefault(x => x.PropertyID == id);
which fails of course because you cant use an orderby clause there.
If I try an orderby clause at the end of the linq it will start at Property and I will need to drill down to the licenses again.
What is the preferred convention for this?
--EDIT--
Based on Robert's suggestion I flipped the query to get the Licenses first but I guess I need to do some projection since this query only gives me the Property entity and no child entities.
return _db.Licenses.OrderByDescending(x => x.IssueDate)
.Include(f => f.Facility)
.Include(f => f.Facility.Property)
.Include(f => f.Facility.Property.Contact)
.Include(c => c.Facility.Contact)
.Include(f => f.Facility.Farms)
.Include(f => f.Facility.Farms.Select(s => s.Species))
.Where(x => x.Facility.Property.PropertyID == id)
.Select(x => x.Facility.Property).SingleOrDefault();
Any ideas to get all children?

EF eager load multiple child child collections

I can't seem to find how to eager load multiple child child collections in EF. So I can do:
blawConext.Blaws
.Include(b => b.ChildCollection
.Select(cc => cc.ChildChildCollection)
)
I can even go deeper and deeper without a problem but I can't get the umm peer? collection, The below does not work
blawConext.Blaws
.Include(b => b.ChildCollection
.Select(cc => cc.ChildChildCollection1)
.Select(cc => cc.ChildChildCollection2)
)
You can specify multiple includes:
blawConext.Blaws
.Include(b => b.ChildCollection.Select(cc => cc.ChildChildCollection1))
.Include(b => b.ChildCollection.Select(cc => cc.ChildChildCollection2))