Entity Framework: How to optimize this below linq query? - c#

I need some suggestions that how to improve this below query.
from o in this.DbContext.Set<School>().AsNoTracking()
from s in o.Teachers.DefaultIfEmpty()
where SchoolCodes.Contains(o.Code)
select new TabularItem
{
SchoolId = o.Id,
SchoolCode = o.Code,
SchoolPurchaseOrderReference = o.PurchaseOrderReference,
SchoolDescription = o.OrderDescription,
SchoolActivityStatus = o.ActivityStatusesInternal.FirstOrDefault(os => os.ActivityName == orderLoggingActivity),
Type = o.TypesAsString,
CustomerCode = o.CustomerCode,
TeacherId = s == null ? (Guid?)null : s.Id,
TeacherCode = s == null ? null : s.Code,
TeacherCustomerReference = s == null ? null : s.CustomerReference,
TeacherIsImported = s == null ? (bool?)null : s.IsImported,
TeacherIsRegisteredUnderModification = s == null ? (bool?)null : s.IsRegisteredUnderModification,
TeacherStatus = s == null ? null : s.StatusAsString,
TeacherStatusChangeDate = s == null ? (DateTimeOffset?)null : s.StatusChangeDate,
IsReportInProgress = s == null ? false : s.IsReportInProgress,
TeacherActivityStatus = s == null ? null : s.ActivityStatusesInternal.FirstOrDefault(ss => ss.ActivityName == orderLoggingActivity),
TeacherHasUnresolvedIssue = s.TeacherIssuesInternal.Any(si => unresolvedIssueStatuses.Contains(si.StatusAsString)),
TeacherHasAdvancePaymentInProgressInvoiceableItem = s.FractionsInternal.SelectMany(x => x.TestPRepetitionsInternal).Any(x => x.InvoiceableItem.IsAdvancePaymentInProgress),
TeacherHasInvoicingInProgressInvoiceableItem = s.FractionsInternal.SelectMany(x => x.TestPRepetitionsInternal).Any(x => x.InvoiceableItem.IsInvoicingInProgress && x.InvoiceableItem.InvoicingStatusAsString != doNotInvoiceStatus),
HasSchoolBasedInvoiceableItems = s.School.InvoiceableItemsInternal.Any(item => item.InvoicingStatusAsString != orderBasedInvoiceableItemStatus),
SchoolHasInvoicingInProgressInvoiceableItem = s.School.InvoiceableItemsInternal.Any(x => x.IsInvoicingInProgress && x.InvoicingStatusAsString != doNotInvoiceStatus)
};
Here School--> Teacher --> Fraction --> TestPRepetition --> InvoiceableItem relation between tables.
please suggest me where i can improve performance. This will hit only once, so i cant use compiled query. there is no use.

Simple. DO not load all the data.
Teacher -> Fraction -> TestRPepetition multiplies the amount odf data you pull.
Ef is meant to pull the data you need, now load a lot of related data into memory IN CASE YOU MAY NEED IT ONE DAY.
Pull the minimum amount of data you need in this moment, go back to the database when you need more. Optimize from there when you run into problems by adding preloads, but always keep to the minimum you need.
Right now you load all data related to all tachers within a specific code. This is likely a ridiculous amount of data that mostly is noise and not properly used in further processing.

There are a couple options, but few are in EF
Clean up the null values in the database
Build the implied entity relations like TeacherHasInvoicingInProgressInvoiceableItem into the database (via foreign key or mapping table)
Prefetch repeat statements like o.ActivityStatusesInternal.FirstOrDefault

Related

Linq Query using navigation properties and Where clause

I am trying to compose a linq query using navigation properties. I am selecting properties from 3 entities:
Lockers
SectionColumn
Contracts
I require ALL rows from the Lockers table where all the following conditions are met: the LockerTypeId = "308", .OutOfOrder != true, x.SectionColumn.SectionId == "52").
The query below without the condition x.SectionColumn.SectionId == "52" works and returns exactly what I require except rows with Section id of any value are returned as I would expect.
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ?
false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
When I add the condition 'x.SectionColumn.SectionId == "52"' as below I get the error "The cast to value type 'System.Int32' failed because the materialized value is null". Either the result type's generic parameter or the query must use a nullable type" in linqpad. SectionId is a string (varchar in SQL Server).
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
I would be grateful for assistance in correctly writing this query.
First off, your code might be a little more straight forward if you stick to pure LINQ. In that case, your code should look something like the following.
var results = from l in Lockers
where l.LockerTypeId == "308" && l.OutOfOrder != true && l.SectionColumn.SectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
If l.SectionColumn.SectionId represents valid navigational properties and is of type string, then this should work correctly.
You really haven't done a thorough job of describing the issue (and it looks like you didn't stick around to field questions), but if l.SectionColumn is nullable, you should be able to update your code to something like this.
var results = from l in Lockers
let sectionId = (l.SectionColumn != null) ? l.SectionColumn.SectionId : null
where l.LockerTypeId == "308" && l.OutOfOrder != true && sectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}

The following code has the same result, but whichever is faster in bringing the result

The first code was used "join"
But in the second code is not used "join"
Note that the result is the same.
So I have several questions
Which is better?
Which is faster?
Code01:
(from Member in dContext.TB_FamilyCardMembers
select new
{
Member.FamilyCard_ID,
Member.TB_FamilyCard.NoFamilyCard,
CardType = Member.TB_FamilyCard.Is_Card == true ? "دفتر عائلة" : "بيان عائلي",
FirsN = Member.TB_Person.FirstName,
FatherN = Member.TB_Person.FatherName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Father_ID.ToString())).FirstName : Member.TB_Person.FatherName,
LastN = Member.TB_Person.LastName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Father_ID.ToString())).LastName : Member.TB_Person.LastName,
MotherN = Member.TB_Person.MotherName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Mother_ID.ToString())).FirstName : Member.TB_Person.MotherName,
MotherLN = Member.TB_Person.MotherLastName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Mother_ID.ToString())).LastName : Member.TB_Person.MotherLastName
}).ToList();
______________________________________________
Code02:
(from Member in dContext.TB_FamilyCardMembers
join Card in dContext.TB_FamilyCards on Member.FamilyCard_ID equals Card.ID
join Person in dContext.TB_Persons on Member.Person_ID equals Person.ID
select new
{
Member.FamilyCard_ID,
Card.NoFamilyCard,
CardType = Card.Is_Card == true ? "دفتر عائلة" : "بيان عائلي",
FirsN = Person.FirstName,
FatherN = Person.FatherName == null ? SelectPersonByID(int.Parse(Person.Father_ID.ToString())).FirstName : Person.FatherName,
LastN = Person.LastName == null ? SelectPersonByID(int.Parse(Person.Father_ID.ToString())).LastName : Person.LastName,
MotherN = Person.MotherName == null ? SelectPersonByID(int.Parse(Person.Mother_ID.ToString())).FirstName : Person.MotherName,
MotherLN = Person.MotherLastName == null ? SelectPersonByID(int.Parse(Person.Mother_ID.ToString())).LastName : Person.MotherLastName
}).ToList();
All roads lead to Rome
Just because there is no join in the Linq code, does not mean there is no join in the final query.
Once you use
Member.TB_Person.FirstName
Linq-2-sql will post probably add a join to that generated SQL.
A lot of coders explicitly add the join because they are coding LINQ-2-SQL in a real SQL kind of way. Most of the times it is not needed (assuming the proper foreign keys are in place) because L2S will figure the join out for you.
To really answer your question you need to profile the actual SQL generated. Only then you will see the difference in the query that is actually sent to the database. Chances are it is identical. If not, select the most efficient of the two.
How to view the SQL:
How to view LINQ Generated SQL statements?

??(null-coalescing operator) between EF6 Queries

I am developing an MVC 5 app using EF 6. I want to query my database and store a value in a variable and if there is null as per the the given condition then another query should be executed to return a value. In my method I have as follows: D1 is coming as a parameter and it has the current date.
int otherYear = D1.Year + 1;
lastNo = (db.ABC.ToList().LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear).S1) ?? (db.ABC.ToList().LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year).S1);
Now the first query before ?? operator executes but when there is null against the condition specified the system throws an error of object reference and does not execute the second query after ?? operator. How can I solve it?
I know that it can be solved by making an if-else condition and within that I should first check that if there is any data using .Any() function. But in that case I have to query my database for minimum of 2 times. Once in .Any() to check the availability of data and second to fetch that data. But I have a hefty database and I don't want to make extra queries.
Regards
You should not call db.ABC.ToList() before applying LastOrDefault() because it will load the whole database to memory and do the processing from there.
You're getting null reference exception because db.ABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear) is null so that you cannot get S1 property.
For your question, I think you can use this code:
int otherYear = D1.Year + 1;
lastNo = (db.ABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear)?.S1) ?? (db.ABC.LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year)?.S1);
To avoid the double query issue, store the result of the query in a list:
int otherYear = D1.Year + 1;
List<T> myABC = db.ABC.ToList();
lastNo = (myABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear).S1) ?? (myABC.LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year).S1);

Entity Framework causing Timeout Error

I am working with the following Entity Framework query. I know there's a lot going on here but am hoping it's clear enough that someone might be able to spot the issue.
var lineItems = from li in Repository.Query<CostingLineItem>()
let cid = (li.ParentCostingPackage != null) ?
li.ParentCostingPackage.ParentCostingEvent.ProposalSection.Proposal.Costing.Id :
li.ParentCostingEvent.ProposalSection.Proposal.Costing.Id
where cid == costingId &&
li.OriginalProductId.HasValue &&
(li.Quantity.HasValue && li.Quantity.Value > 0) && // li.QuantityUnitMultiplier
Classifications.Contains(li.OriginalProduct.ClassificationEnumIndex)
let selectedChoiceId = li.OriginalPackageOptionId.HasValue ?
(from c in li.OriginalPackageOption.CostingLineItems
orderby (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id
select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault() :
0
where selectedChoiceId == 0 || (li.OriginalPackageOptionChoiceId.HasValue && li.OriginalPackageOptionId.Value == selectedChoiceId)
let hasProviderAvailable = li.OriginalProductItem.ProductItemVendors.Any(
piv => piv.ProductPricings.Any(pp => pp.ProductItemVendor.CompanyId != null || pp.ProductItemVendor.HotelId != null))
select new
{
LineItem = li,
ProductItem = li.OriginalProductItem,
Product = li.OriginalProduct,
Vendors = li.CostingLineItemVendors,
HasProviderAvailable = hasProviderAvailable
};
As is, this query generates the following run-time error:
The wait operation timed out
If I change the section that declares selectedChoiceId to the following, the error goes away:
let selectedChoiceId = 0
Can anyone see how that code is consistently causing a time-out error?
(Note: This code is part of a large application that has been running for several years. So I really don't think this has anything to do with the connection string or anything like that. If I make the change above, it works consistently.)
The query can be simplified in a number of ways, which should make it easier to optimize by the database engine.
Firstly, you can remove a number of null checks (HasValue), because they're not relevant in SQL, but they do bloat the generated SQL.
Secondly, I think this check involving selectedChoiceId can be greatly simplified. This is what I think the statement could look like:
from li in Repository.Query<CostingLineItem>()
let cid = (li.ParentCostingPackage != null) ?
li.ParentCostingPackage.ParentCostingEvent.ProposalSection.Proposal.Costing.Id :
li.ParentCostingEvent.ProposalSection.Proposal.Costing.Id
where cid == costingId &&
li.OriginalProductId.HasValue &&
li.Quantity > 0 && // no null check
Classifications.Contains(li.OriginalProduct.ClassificationEnumIndex)
let selectedChoiceId = (from c in li.OriginalPackageOption.CostingLineItems
orderby c.IsIncluded ? -2 : c.IsDefaultItem ? -1 : c.Id // no null checks
select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault()
where !li.OriginalPackageOptionId.HasValue || li.OriginalPackageOptionId == selectedChoiceId
let hasProviderAvailable = li.OriginalProductItem.ProductItemVendors.Any(
piv => piv.ProductPricings.Any(pp => pp.ProductItemVendor.CompanyId != null || pp.ProductItemVendor.HotelId != null))
select new
{
LineItem = li,
ProductItem = li.OriginalProductItem,
Product = li.OriginalProduct,
Vendors = li.CostingLineItemVendors,
HasProviderAvailable = hasProviderAvailable
}
For the rest, of course there are the usual suspects. Better indexes may become more important as the database volume increases. Checking for (and fixing) database fragmentation can also have a significant impact.
I think this will give you a better performance but not sure if it'll fix the problem :
let selectedChoiceId = li.OriginalPackageOptionId.HasValue
? (from c in li.OriginalPackageOption.CostingLineItems
let cOrder = (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id
orderby cOrder
select (int) c.OriginalPackageOptionChoiceId).FirstOrDefault()
: 0

Linq if-elseif-else using linq query in asp.net

In my asp.net application i am using linq. I need a help what is the syntax for if-elseif-else using linq in single line.
genericReportList =
(from CD in list
select new GENERICREPORT
{
CITATIONNO = CD.CITATIONNO,
DATE = CD.DATE,
LOCATION = CD.LOCATION,
//STATUS = CD.STATUS,
PLATENO = Utilities.DecryptData(CD.PLATENO),
PSOURCE = CD.PSOURCE,
MAKE = CD.MAKE,
ID = Utilities.DecryptData(CD.ID),
NATIONALITY = CD.NATIONALITY,
SOURCE = CD.SOURCE,
NAME = Utilities.DecryptData(CD.NAME),
VIOLATION = CD.VIOLATION,
STATUS = CD.STATUS == short.Parse("1") ? "Complete" : "Incomplete"
}).ToList();
If STATUS = CD.STATUS == short.Parse("1") ? "Complete" : and 2 for "Incomplete" and 3 for "Void"
I don't understand why you are doing short.Parse("1"). This will always be 1. If you want multiple if-else in a one-liner, combine ternary operators:
STATUS = CD.STATUS == 1 ? "Complete" : CD.STATUS == 2 ? "Incomplete" : "Void"
If this is going to be used in the context of Entity Framework (or other ORM with IQueryable support), it will translate to a CASE WHEN SQL statement.
What I understand from your question if I'm not wrong, is that you might be asking about where clause.
if yes then you can always use multiple where in your query.
example :
Collection.Where(x => x.Age == 10)
.Where(x => x.Name == "Fido")
.Where(x => x.Fat == true)
more about LINQ query
http://msdn.microsoft.com/en-us/library/gg509017.aspx
You can just continue with what you already have.
STATUS = CD.STATUS == 1 ? "Complete" : (CD.STATUS == 2 ? "Incomplete" : "InProgress")
There nothing wrong with writing your own method and using it in a LINQ query. It will be far more understandable and readable than a long conditional operator.
Consider using something like:
private String GetStatus(int value)
{
if (value == 1)
return "Complete";
if (value == 2)
return "Incomplete";
if (value == 3)
return "Void";
}
Then you would call it like:
STATUS = GetStatus(CD.STATUS)

Categories