Linq let and if - c#

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.

Related

How can I make this LINQ to SQL where clause with conditions faster?

I have the following LINQ to SQL query but I want to know if there is a faster way to validate data from a post action before adding them in a where clause? ex:
bookFilter = Informations coming from the post action
int count = from b in _libraryContext.Book
join ba in _libraryContext.BookAuthor
on b.Id equals ba.BookId
where (!string.IsNullOrWhiteSpace(bookFilter.Name) ?
b.Name.Contains(bookFilter.Name.ToUpper()) : 1 == 1 )
where (!string.IsNullOrWhiteSpace(bookFilter.Decription) ?
b.Description.Contains(bookFilter.Description.ToUpper()) : 1 == 1)
where (bookFilter.BookId > 0 ? ba.BookId == bookFilter.BookId : 1 == 1)
return count;
I've never used this type of syntax much so I'm sure if you can do it this way, but you can certainly do it with LINQ and build up your query step by step like so:
var query = _libraryContext.Set<Book>();
if(!string.IsNullOrWhiteSpace(bookFilter.Name))
{
query = query.Where(x => x.Name.Contains(bookFilter.Name.ToUpper()));
}
if(!string.IsNullOrWhiteSpace(bookFilter.Description))
{
query = query.Where(x => x.Description.Contains(bookFilter.Description.ToUpper()));
}
if(bookFilter.BookId > 0)
{
query = query.Where(x => x.BookId == bookFilter.Id);
}
return query.Count();
Note: I have omitted the JOIN here as it seems unnecessary, but you can of course do the join in this syntax too if you need it.
You have accepted the solution that #Akinzekeel provided above. However, in the case that you are trying to use a single LINQ query, I would go for the solution below:
bookFilter = Informations coming from the post action
Please note here bookFilter has to be IQueryable!
int count = from b in _libraryContext.Book
join ba in _libraryContext.BookAuthor
on b.Id equals ba.BookId
where (b.Name.Contains(bookFilter.Name.ToUpper()) || string.IsNullOrWhiteSpace(bookFilter.Name)) &&
(b.Description.Contains(bookFilter.Description.ToUpper()) || string.IsNullOrWhiteSpace(bookFilter.Decription)) &&
(ba.BookId == bookFilter.BookId || bookFilter.BookId == 0 )
return count;
If you trace the code above in SQL Profiler, you will see this will generate a single SQL query; something like (OR IS NULL) conditions.
In SQL you could use COALESCE to default ignore parameters -- like the following:
SELECT *
FROM book b
JOIN BookAuthor ba on b.id = ba.Bookid
WHERE COALESCE(UPPER(name), UPPER(b.name)) = UPPER(b.name)
AND COALESCE(UPPER(description), UPPER(b.description)) = UPPER(b.description)
AND COALESCE(bookid, b.book.id) = b.bookid
Recommended pattern is to put something like that in a SP and then call the SP.

How to think conditions in LINQ

I'm trying to perform a left join with LINQ and I'm not finding the correct structure (my original query came from a query built in Access).
I'm trying to think with the method available in LINQ but I'm not getting any useful result. I really appreciate any suggestion related LINQ architecture and how to think complex queries that the ones below.
Original Query in Access:
(reformatted)
SELECT [Check People].ID, [Check People].MMS_USER__C, [Check People].EMPLOYMENT_STATUS__C, [Check People].LASTLOGINDATE, [Check People].ENTERPRISE_ID__C, [Check People].ISACTIVE, [ExtractPrivateReports].NAME, [ExtractPrivateReports].LASTRUNDATE, [ExtractPrivateReports].ID
FROM ExtractPrivateReports
LEFT JOIN [Check People] ON [ExtractPrivateReports].OWNERID = [Check People].MMS_USER__C
WHERE
(
(([Check People].ISACTIVE) = "true")
And
(([ExtractPrivateReports].LASTRUNDATE) < Date() - 180)
)
Or
(
(([Check People].ISACTIVE)="true")
And
(
(([ExtractPrivateReports].LASTRUNDATE) Is Null)
And
(([ExtractPrivateReports].CREATEDDATE) < Date() - 180)
)
);
Note: the tables are [Check People] and [ExtractPrivateReports]
Query that I made with LINQ (doesn't retrieve the same number of records that retrieves the query above)
DateTime daysCalculation = today.AddDays(-166);
var query_3 = from privateReports in privateReportList
join checkPeople in this.getListaCheckPeople(p, u)
on privateReports.OWNERID equals checkPeople.Mms_user_c
into jn
from j in jn.DefaultIfEmpty()
where ((j.IsActve.Equals("true")) && (privateReports.LASTRUNDATE.CompareTo(daysCalculation) > 0))
|| ((j.IsActve.Equals("true")) && (privateReports.LASTRUNDATE == null))
&& (privateReports.CREATEDDATE.CompareTo(daysCalculation) > 0)
&& (!string.IsNullOrEmpty(privateReports.OWNERID))
select new ActivePeopleWithPrivateReportsNotRunInMoreThan180Days
{
EMPLOYMENT_STATUS__C = j.EmploymentStatus == null ? "no value" : j.EmploymentStatus,
LASTRUNDATE = privateReports.LASTRUNDATE,
CheckPeople_ID = j.PeopleKey_c == null ? "no value" : j.PeopleKey_c,
ENTERPRISE_ID__C = j.EnterpriseID_c == null ? "no value" : j.EnterpriseID_c,
ISACTIVE = j.IsActve == null ? "no value" : j.IsActve,
NAME = privateReports.NAME,
LASTLOGINDATE = j.LastLoginDate == null ? DateTime.ParseExact("0001-01-01", "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture) : j.LastLoginDate,
ExtractPrivateReports_ID = privateReports.ID,
MMS_USER__C = j.Mms_user_c == null ? "no value" : j.Mms_user_c
};

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 value in the result of a left outer join linq causes error

I have linq query, that left outer join two tables. I found if a value of a field returns null,, then I will get an error message:
"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."
I copied my linq below:
var SrvRef = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from r in r_join.DefaultIfEmpty()
where s.svhAccID == accId &&
s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
r.refID
};
bool FBeenPaid = SrvRef.FirstOrDefault().svhBalance == 0M; //this causes error
How can I fix this problem?
I'm slightly surprised at the kind of error you're getting, but there are two places you need to take account of the possibility of the result being null:
Within the query, where r can be null. (If you don't want to match when there are no elements in r_join matching s, you shouldn't be using a left outer join)
In the result itself: you're using FirstOrDefault() which will return null if SrvRef is empty.
So at first glance it should probably be something like:
var query = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from r in r_join.DefaultIfEmpty()
where s.svhAccID == accId && s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
refId = r == null ? 0 : r.refID // Adjust to the appropriate type of refID
};
var result = query.FirstOrDefault();
bool beenPaid = result != null && result.svhBalance == 0m;
With C# 6, you can change the bottom two lines to:
bool beenPaid = query.FirstOrDefault()?.svhBalance == 0m ?? false;
Having said that:
You're not currently using refId in the result anyway - why are you including it in the result?
Are you sure you want a left outer join at all?
Are you sure that taking the first result is really what you want? What if there are multiple results in the join?
Is there any reason you're not doing the whole thing in a query? Something like:
var paid = db.SrvHeads
.Where(s => s.svhAccID == accId && s.svhHeadCnt == HeadId)
.Any(s => db.Refererrants.Any(r => s.svhReferrer == r.refID
&& s.svhBalance == 0m);
.. but just for the precise semantics you want.
I had a similar issue.
Cause: You are using from "r" in r_join.DefaultIfEmpty(). You cannot use same alias name for left outer join.
Solution: Use different alias name if DefaultIfEmpty() cases. Eg: rEmpty
I modified the below query and its working.
var SrvRef = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from rEmpty in r_join.DefaultIfEmpty()
where s.svhAccID == accId &&
s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
refID = rEmpty == null ? 0 : rEmpty.refID
};
what i think is causing an error is that svhBalance is an int32 value type and you are accessing a null value returned by SrvRef.FirstOrDefault().
please try the following line and let me know if it helped you.
if svhBalance is an int value type
var SrvRefObj= SrvRef.FirstOrDefault(); bool FBeenPaid = (((SrvRefObj!=null)&&(SrvRefObj.svhBalance
!=null))?(SrvRefObj.svhBalance == 0):(false))
else if it's a decimal value type
var SrvRefObj= SrvRef.FirstOrDefault(); bool FBeenPaid = (((SrvRefObj!=null)&&(SrvRefObj.svhBalance
!=null))?(SrvRefObj.svhBalance == 0M):(false))

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

Categories