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
Related
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
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
}
I am getting:
Non-static method requires a target.
The problem is that Status is null. I don't understand why, because there is a condition which clearly indicates if Status is null return 1.
var filterstatus = (from bq in basequery
let LastStatus = Status == null ? 1
: ((from sd in ems.SampleDatas
where sd.Reference_id == Status.id
&& sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
It's because it's converting the entire expression into SQL, and not doing the short-circuit in memory (the short circuit would be handled by the database).
You can write something like this, which will properly short-circuit in the database (but still generate the right hand side of the query).
var statusID = Status == null ? (int?)null : Status.id;
var filterstatus = (from bq in basequery
let LastStatus =
statusID == null ?
1 :
((from sd in ems.SampleDatas
where sd.Reference_id == statusID && sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
Ideally, though, you'd have two separate queries, depending on Status, as it's already known at that point whether the right hand side is required or not.
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)
Well,
I am not sure if I am wrong or if the linq parsed mistakes, but the following linq query returns what I DON'T want if I don't use additional parenthesis.
int? userLevel;
Guid? supervisorId;
List<int> levelsIds = new List<int>();
string hierarchyPath;
// init the vars above
// ...
var qry = from f in items
where userLevel.HasValue
? (f.IsMinLevelIdNull() || (levelsIds.Contains(f.MinLevelId)))
: true
&& supervisorId.HasValue
? (f.IsSupervisorIdNull() || (!f.ChildrenVisibility && (f.SupervisorId == supervisorId.Value))
|| (f.ChildrenVisibility && (hierarchyPath.IndexOf(f.SupervisorId.ToString()) >= 0)))
: true
select f;
The idea here is to run a query in AND between two blocks of conditions 'activated' by the presence of the variables 'userLevel' and 'supervisorId'.
For example, if both userLevel and supervisoId are null the query becomes:
var qry = from f in items
where true && true
select f;
Well, this query works well only if I enclose in additional parentheses the 2 trigraphs:
var qry = from f in items
where (userLevel.HasValue
? (f.IsMinLevelIdNull() || (levelsIds.Contains(f.MinLevelId)))
: true)
&& (supervisorId.HasValue
? (f.IsSupervisorIdNull() || (!f.ChildrenVisibility && (f.SupervisorId == supervisorId.Value))
|| (f.ChildrenVisibility && (hierarchyPath.IndexOf(f.SupervisorId.ToString()) >= 0)))
: true)
select f;
The question is: why the extra parenthesis are required? My opinion is that there is a problem in the linq parser.
From 7.2.1 Operator precedence and associativity && gets evaluated before ? :
The additional parentheses would be required as the precedence is evaluated in the wrong order for example your code would be evaluated as the following because there is no break between true && supervisorId.HasValue...
var qry = from f in items
where
1st: userLevel.HasValue
?
2nd: (f.IsMinLevelIdNull() || (levelsIds.Contains(f.MinLevelId)))
:
3rd: true && supervisorId.HasValue
? (f.IsSupervisorIdNull() || (!f.ChildrenVisibility && (f.SupervisorId == supervisorId.Value))
|| (f.ChildrenVisibility && (hierarchyPath.IndexOf(f.SupervisorId.ToString()) >= 0))) **)**
: true
select f;