EF Include doesn't work on a iterative query - c#

I'm working with C# EF 7.0.2, Z.EntityFrameworkPlus and SQL Server. I have a database that could be grown a lot and I want to make it scalable; To do this, I figure out to work with "chunks" to avoid timeout issues.
I have 4 entities
Products
Attributes
ProductAttribute (n:m)
RequestedProducts (List of user's requests to process products)
I was trying to process the products joining the entities Products and RequestedProducts, querying them by chunks (I'm using Take to avoid use OrderBy function, cuz in the local test that increment a lot the query result time) including ProductAttribute and Attributes entities. Then, when the parallel process ends, I'm using BatchUpdate to change de "Finished" flag so that next query doesn't include processed products (I'd understood that IQueryable doesn't execute the query until is necesary)
The first run of the query it works fine, but the next iteration the entities result doesn't include the ProductAttribute entities
IQueryable<Product> products = this.context.Products
.Join(
this.context.RequestedProducts.Where(x=>!x.Finished),
p => p.Id,
pp => pp.ProductId,
(p, pp) => p)
.Include(x => x.Attributes)
.ThenInclude(x => x.Attribute)
.Include(x => x.Classifications);
IQueryable<StepProduct> queryToRun = products
.Take(productLimit);
while(queryToRun){
queryToRun
.AsParallel()
.WithDegreeOfParallelism(maxDegree)
.ForAll(product =>
{
//Here is the process products code
}
this.context.RequestedProducts
.Where(g => queryToRun.Select(q => q.ProductId).Contains(g.Id))
.Update(new RequestedProducts(){ Finished=true });
queryToRun = products
.Take(productLimit);
}
I tried to asign the full IQueryable to the queryToRun var, then I tried put the Include sentences in queryToRun and not in products, but it doesn't work. The first time all of the products entities have Attributes, but the next chunk, Attributes ever was empty.

Related

Include doesn't work with Select in query

I'm trying to use Include() in a query but it doesn't work as I expect.
I have a table called Bes who has this columns:
Id
Name
Pesta
CeId
OpId
The CeId and OpId are foreign keys to this tables:
Ces:
Id
Name
Ops:
Id
Name
I want to run this query (which works but doesn't fill the Ces and Ops tables)
var bes = await _context.Bes
.Include(x => x.Ops)
.Include(x => x.Ces)
.Where(x => besWithXPs.Contains(x.Pesta))
.GroupBy(x => x.Pesta)
.Select(x => x.First())
.ToListAsync();
I try to use this query without the Select() and the Ops and Ces where fill but I only want one Bes per Pesta (that's why the GroupBy and Select)
Anyone knows what is happening?
Btw I'm using Entity Framework Core with .NET Core 2
I think you should add
[JsonIgnore]
public Ces Ce { get; set; }
[JsonIgnore]
public Ods Od { get; set; }
to your model in order to let include them and then do:
var bes = await _context.Bes.AsNotracking()
.Include(x => x.Ops)
.Include(x => x.Ces)
.Where(x => besWithXPs.Contains(x.Pesta))
.GroupBy(x => x.Pesta)
.FirstOrDefaultAsync();
1 - What AsNoTracking Does:
Entity Framework exposes a number of performance tuning options to help you optimise the performance of your applications. One of these tuning options is .AsNoTracking(). This optimisation allows you to tell Entity Framework not to track the results of a query. This means that Entity Framework performs no additional processing or storage of the entities which are returned by the query. However, it also means that you can't update these entities without reattaching them to the tracking graph.
2 - Change ".Select(x => x.First()).ToListAsync();" to ".FirstOrDefaultAsync();". Use .Select only if you want to get some of the properties or if you need a new class filled with the data that you receive.

Select the record with highest date and INCLUDE relevant tables with it - LINQ

I have a table called EmployementContracts, I want to retrieve each contract with highest StartDate for every employee, included some other properties from linked/relevant tables. I tried as bellow:
_dbContext.EmployementContracts
.Include(x => x.Employee)
.Include(x => x.Department)
.Where(x => x.SupervisorId == 1)
.GroupBy(c => c.EmployeeId)
.Select(g => new
{
StartDate = g.Max(x => x.StartDate)
}).ToList();
I am able to get only the StartDate column from this (the highest dates for every record), but how to get some other properties from included tables?
for example I need:
EmployeeName from Employee and DepartmentName from Department tables as well.
I would suggest you use the following code:
_dbContext.EmployeeContracts
.Where(x => x.SuperVisorId == 1)
.GroupBy(x => x.EmployeeId)
.Select(x=>x.OrderByDescending(y=>y.StartDate).FirstOrDefault())
.Include(x => x.Employee)
.Include(x => x.Department)
.ToList();
The trick is the only retrieve the relevant record from the grouping. Then each record from each group is exactly the employeeContract you want to look at. Notice also that the .Include() statements are moved to the back. This is because the grouping changes the Queryable structure and removes the included navigation properties, so you'll have to Include once you have the structure you desire.
Your select statement is the result you're going to get. Your result is just an anonymous object with one field, StartDate. You can just call .Select() there to return your entire search and the tables and data to the anonymous object.

C# LINQ Filter deep nested list

I've a structure based of list containing other list. I need to filter the list based on the value of a property based in the deepest part of the list.
Right now I'm doing this:
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts);
queryable = queryable
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)));
I join the needed tables, then I filter them based on the ActionTypeId based 3 levels below the top list.
First off all, is it possible to do this in 1 step ( include the filtering with the joins ), second of all, the second part is not working as my list gets empty, but I'm certain that actions with that type get values.
Using .NET Core 2.0.3 btw!
To answer your first part, you can do this
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts)
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)))
To your second part, it should be working, id check your data, as this is pretty straight forward

Linq Where then Select on a HashSet

I'm trying to get a list of products that match a certain category id. The problem I'm having is that I'm using the select clause before the where, essentially trying to filter once I've already got all the results. Usually its straight forward but since this navigation property is a HashSet its proving more tricky. My repo.GetAll() gets all the products from my database. CategoryProducts is a linking table between products and Categories, its also a navigation property on the product table
ProductRepository repo = new ProductRepository();
var products =
repo.GetAll()
.Select(c => c.CategoryProducts
.Where(p => p.CategoryId == 35));
The above just returns all my products, any help is appreciated.
Your query returns an enumerable that corresponds to all your products, each item of that enumerable is an enumerable itself, containing zero or more categories with ID of 35.
You can change your query to get only products that have category 35 in them:
var products = repo
.GetAll()
.Where(p => p.CategoryProducts.Any(c => c.CategoryId == 35));

How to include() nested child entity in linq

How do I include a child of a child entitiy?
Ie, Jobs have Quotes which have QuoteItems
var job = db.Jobs
.Where(x => x.JobID == id)
.Include(x => x.Quotes)
.Include(x => x.Quotes.QuoteItems) // This doesn't work
.SingleOrDefault();
Just to be clearer - I'm trying to retrieve a single Job item, and it's associated Quotes (one to many) and for each Quote the associated QuoteItems (One Quote can have many QuoteItems)
The reason I'm asking is because in my Quote Index view I'm trying to show the Total of all the Quote items for each Quote by SUMming the Subtotal, but it's coming out as 0. I'm calling the Subtotal like this:
#item.QuoteItem.Sum(p => p.Subtotal)
I believe the reason I have this issue is that my Linq query above isn't retrieving the associated QuoteItems for each Quote.
To get a job and eager load all its quotes and their quoteitems, you write:
var job = db.Jobs
.Include(x => x.Quotes.Select(q => q.QuoteItems))
.Where(x => x.JobID == id)
.SingleOrDefault();
You might need SelectMany instead of Select if QuoteItems is a collection too.
Note to others; The strongly typed Include() method is an extension method so you need to include using System.Data.Entity; at the top of your file.
The method in the accepted answer doesn't work in .NET Core.
For anyone using .NET Core, while the magic string way does work, the cleaner way to do it would be ThenInclude:
var job = db.Jobs
.Where(x => x.JobID == id)
.Include(x => x.Quotes)
.ThenInclude(x => x.QuoteItems)
.SingleOrDefault();
Source: Work with data in ASP.NET Core Apps | Microsoft Learn
This will do the job (given that we are talking entity framework and you want to fetch child-entities):
var job = db.Jobs
.Include(x => x.Quotes) // include the "Job.Quotes" relation and data
.Include("Quotes.QuoteItems") // include the "Job.Quotes.QuoteItems" relation with data
.Where(x => x.JobID == id) // going on the original Job.JobID
.SingleOrDefault(); // fetches the first hit from db.
For more information about the Include statement have a look at this: https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectquery-1.include
This answer has been getting upvotes throught the years, so I'd just like to clarify, try https://stackoverflow.com/a/24120209/691294 first. This answer is for those cases where all else fails and you have to resort to a black magic solution (i.e. using magic strings).
This did the trick for me as #flindeberg said here .
Just added checking if there are children in each parent item in the list
List<WCF.DAL.Company> companies = dbCtx.Companies.Where(x=>x.CompanyBranches.Count > 0)
.Include(c => c.CompanyBranches)
.Include("CompanyBranches.Address")
.ToList();

Categories