LINQ Expression not evaluating correctly - c#

I have the following LINQ expression that's not returning the appropriate response
var query = from quote in db.Quotes
where quote.QuoteStatus == "Estimating" || quote.QuoteStatus == "Rejected"
from emp in db.Employees
where emp.EmployeeID == quote.EmployeeID
orderby quote.QuoteID descending
select new
{
quote.QuoteID,
quote.DateDue,
Company = quote.Company.CompanyName,
Attachments = quote.Attachments.Count,
Employee = emp.FullName,
Estimator = (quote.EstimatorID != null && quote.EstimatorID != String.Empty)
? db.Employees.Single (c => c.EmployeeID == quote.EstimatorID).FullName
: "Unassigned",
Status = quote.QuoteStatus, Priority = quote.Priority
};
The problem lies in the Estimator = (quote.EstimatorID != null && quote.EstimatorID != String.Empty) ? db.Employees.Single(c => c.EmployeeID == quote.EstimatorID).FullName : "Unassigned" part.
I NEVER get it to evalueate to "Unassigned", on the ones that are supposed to, it just returns null. Have I written this wrong?

Try this and see if you're receiving the same values:
Estimator = ((!string.IsNullOrEmpty(quote.EstimatorID) && !string.IsNullOrEmpty(quote.EstimatorID.Trim())
? (db.Employees.Single(c => c.EmployeeID == quote.EstimatorID)).FullName
: "Unassigned")
If that doesn't work, try replacing the check in the ternary expression (!string.IsNullOrEmpty part) with false and see if you reach "Unassigned". I've found that sometimes, when you use a ternary expression in a LINQ query, you have to wrap the whole thing in parentheses.

I think your expression is correct, although I would advise changing it to !string.IsNullOrEmpty(quote.EstimatorID) for clarity.
Note however that your expression could still return null if db.Employees.Single(c => c.EmployeeID == quote.EstimatorID).FullName returns null.

Then it looks like quote.EstimatorID is not null or empty string. Is db.Employees.Single() returning null here? Debug it - put breakpoints into those parts of the expression (putting them on separate lines, if necessary). If you can't do that wrap methods around them that call Debug.WriteLine() or similar.

Related

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))

Avoiding null exception in EF that is thrown by your query

I have a query like this :
result =
firstIdeaRepository.FindBy(
i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
.AsEnumerable()
.Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
{
Name =
companyRepository.FindBy(i => i.UserId == j.UserId)
.FirstOrDefault()
DateOfMeeting =
calenderRepository.ConvertToPersianToShow(
meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
.FirstOrDefault()
.Date),
DateOfExit =
calenderRepository.ConvertToPersianToShow(j.DateOfExit.Value),
ReasonOfExit = j.ReasonOfExit,
}).ToList();
return result;
As you can see i use FirstOrDefault() and j.DateOfExit.Value and sometimes my Date doesn't have any values or sometime my other variables are null too because i use firstordefaut() like
companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault().
So my query throws a null exception and the result can't be created ,how can i handle this exception and for example if the .NET detects the null value ignores it by default or uses a default values for that ?
Best regards.
I would make the following changes:
result =
firstIdeaRepository.FindBy(
i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
.AsEnumerable()
.Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
{
Name =
companyRepository.FindBy(i => i.UserId == j.UserId)
.FirstOrDefault()
DateOfMeeting =
callenderRepository.ConvertToPersianToShow(
meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
// project a new sequence first, before calling `FirstOrDefault`:
.Select(s => s.Date)
.FirstOrDefault(),
DateOfExit =
j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value) :
null,
ReasonOfExit = j.ReasonOfExit,
}).ToList();
When you use FirstOrDefault, there's a possibility that you'll get null back (in the case of reference types), and so you need to plan for that in your code.
For example, when assigning DateOfMeeting, you could project the results (using .Select) before using .FirstOrDefault, so that you're not ever accessing the Date property on what could be a null value.
As for DateOfExit, I've used the conditional operator to determine whether to call the calendarRepository's method at all. This assumes that DateOfExit is nullable.
Unrelated: "Calendar" is spelled with one "l" and not two.
Since you're using a nullable date, you can try filtering by values that have date, something like:
.FindBy(s => s.FirstIdeaId == j.Id && s.Date.HasValue)
This will ensure that you don't get any records with null date.
As I mentioned in comments, other cases need to be handled on case-by-case basis. Judging by the code you've shown, maybe you can handle Name as:
Name = companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault() ?? "anonymous";
and so on.
Another example:
If you do want to get the record even if DateOfMeeting is null, then add a check for HasValue in subsequent part or default it to some date:
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: (DateTime)null, // you need to make `DateOfExit` nullable and then handle that downstream
// or (default with current date)
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: callenderRepository.ConvertToPersianToShow(DateTime.Now),
// or (default with empty date)
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: callenderRepository.ConvertToPersianToShow(new DateTime()),
Moral of the story: figure out what the default value should be in case of null and then substitute that accordingly in the query when calling FirstOrDefault().
The broadest solution would be to use the idea of an null object with the DefaultIfEmpty<T>(T DefaultValue) method in your query. An example would be:
var defaultMeeting = new Meeting() { Date = new DateTime() };
var dateOfMeeting = meetingRepository.FindBy(s => s.FirstIdeaId == j.Id)
.DefaultIfEmpty(defaultMeeting)
.FirstOrDefault()
.Date;

A method that searches for entities, a certain property of which matches a given criteria in Entity Framework

Okay, I'm not sure whether the title of the question is clear enough, but I couldn't think of a simpler explanation in one sentence.
I'm making a search page for a ASP.NET web application. I want to allow the users to search using different filters.
I came up with a method that filters the Publications (these are the entities that the user will retrieve) based on the type of Categories that the user has chosen.
List<Publication> publications =
(from p in GetPublications()
where categories.Contains((int)p.CategoryId)
select p).ToList();
The above linq query is equivalent to the following sql one:
select * from Publication p where p.CategoryId in (#chosenCategoryIds)
Everything works perfect but the problem is that I want to allow the user to choose from more filters, for example, not only categories, but users, tags, titles and etc.
So, if I want this, I should make a lot of conditional statements checking whether the user has provided given filters or not, something like:
if (categories != null)
{
var publications = (from p in GetPublications()
where categories.Contains((int)p.CategoryId)
select p).ToList();
if (users != null)
{
publications = (from p in publications
where users.Contains((int)p.UserId)
select p).ToList();
}
}
else
{
// Handle other situations..
}
So, it turns out that I'm doing basically one and the same thing for different filters. My question is whether somebody knows or can think of a better way to achieve this? If not, let's take a look at the following method:
public List<InterpretumDAL.Publication> FilterPublications(List<InterpretumDAL.Publication> oldList, int[] values)
{
List<InterpretumDAL.Publication> newList =
(from p in oldList
where values.Contains((int)p.CategoryId)
select p).ToList();
return newList;
}
What I try to achieve with the method above is to filter a given list by a given array of values. The thing is that I don't know how to tell the method which property to compare, so I can call it something like this:
newList = FilterPublications(publications, categories, CATEGORY_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, users, USER_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, tags, TAG_PROPERTY_OF_PUBLICATION);
Edit:
According to LiquidPony's answer I did it this way:
var publications = (from p in GetPublications()
where (
(categories == null || categories.Length == 0 || (p.CategoryId.HasValue && categories.Contains((int)p.CategoryId)))
&& (users == null || users.Length == 0 || users.Contains(p.UserId))
&& (tags == null || tags.Length == 0 || tags.Contains(p.TagId))
)
select p).ToList();
Taking LiquidPony's answer, I think it would be better to do it like this by using ternary operators.
var query = from p in GetPublications()
where (
(categories != null ? categories.Contains((int)p.CategoryId) : true)
&& (users != null ? users.Contains((int)p.UserId) : true)
&& (tags != null ? tags.Contains((int)p.TagId) : true)
) select p;
If one of the variables isn't provided, that statement will simply evaluate to true and not be included in the SQL evaluation (as far as I know).
You want to avoid using OR's unless absolutely necessary. They are quite inefficient in SQL, and you can usually always find an alternative to get what you want without using them.
The simplest way would be to do something like this:
var query = from p in GetPublications()
where (
(categories == null || categories.Contains((int)p.CategoryId))
&& (users == null || users.Contains((int)p.UserId))
&& (tags == null || tags.Contains((int)p.TagId))
) select p;
That would work if you want to match all criteria. You can switch the && to || if you want to return entities which only match one criteria.

c# dealing with all possible null and non null values

I have the following method:
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p => p.CountryFrom.CountryName.Equals(CountryFrom,
StringComparison.OrdinalIgnoreCase));
}
What is the best way to write the where clause that would filter all the possible combinations of input parameters in one statement:
BOTH CountryFrom and CountryLoc = null
Only CountryFrom null
Only CountryLoc null
BOTH CountryFrom and CountryLoc are not null.
Soon .. I would need to filter out profiles by Age, Gender, Profession .. you name it.
I am trying to find a way to write it efficiently in C#. I know how to do it in a clean manner in TSQL. I wish I knew the way. Thanks for all the responses so far.
A good old binary XNOR operation will do the trick here:
db.Profiles.Where(p => !(p.CountryFrom == null ^ p.CountryTo == null))
It's effectively equating two booleans, though to me it's more direct, less convoluted even, than writing ((p.CountryFrom == null) == (p.CountryTo == null))!
I would use this simple LINQ syntax...
BOTH CountryFrom and CountryLoc = null
var result = from db.Profiles select p
where (p.CountryFrom == null) && (p.CountryLoc == null)
select p
Only CountryFrom null
var result = from db.Profiles select p
where (p.CountryFrom == null) && (p.CountryLoc != null)
select p
Only CountryLoc null
var result = from db.Profiles select p
where (p.CountryFrom != null) && (p.CountryLoc == null)
select p
BOTH CountryFrom and CountryLoc are not null.
var result = from db.Profiles select p
where (p.CountryFrom != null) && (p.CountryLoc != null)
select p
Hope it helps ;-)
I wouldn't call this elegant:
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p =>
{
p.ContryFrom != null &&
p.CountryFrom.CountryName != null &&
p.CountryFrom.CountryName.Equals(CountryFrom, StringComparison.OrdinalIgnoreCase)
});
}
I may be missing something, but as written, your combination of operators will either let all values through or no values through depending on whether you use || or && to combine them together.
I'm in favor of not trying to cram too much logic into a linq expression. Why not contain your comparison logic in a separate function like this?
EDIT: I provided an example implementation of the MatchesCountry function.
class Example
{
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p => p.MatchesCountry(CountryFrom, CountryLoc));
}
}
public static class ProfileExtensions
{
public static bool MatchesCountry(this Profile profile, string CountryFrom, string CountryLoc)
{
// NOTE: Your comparison logic goes here. Below is an example implementation
// if the CountryFrom parameter was specified and matches the profile's CountryName property
if(!string.IsNullOrEmpty(CountryFrom) && string.Equals(profile.CountryName, CountryFrom, StringComparison.OrdinalIgnoreCase))
return true; // then a match is found
// if the CountryLoc parameter was specified and matches the profile's CountryCode property
if (!string.IsNullOrEmpty(CountryLoc) && string.Equals(profile.CountryCode, CountryLoc, StringComparison.OrdinalIgnoreCase))
return true; // then a match is found
// otherwise, no match was found
return false;
}
}

LINQ WHERE clause using if statements

I am using c#.net
I have two textboxes which if !empty need to be part of a WHERE clause within a LINQ query.
Here is my code
var result = from a in xxxx select a;
if(!string.IsNullOrEmpty(personName))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName)
}
else if(!string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.appStartDateTime >= dateFrom.Date)
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
return result.Where(a >= a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date);
}
I thought this would work but it doesn't like the .Where and I cant access the 'a' for example a.forename (The name 'a' does not exist in the current context)
What am I going wrong, or can this not actually be done?
Thanks in advance for any help.
Clare
Instead of this:
result.Where(a.forename.Contains(personName))
Try this:
result.Where(a => a.forename.Contains(personName))
You appear to be missing the Lambda operator (=>).
try this
var result = from a in xxxx select a
where (string.IsNullOrEmpty(personName) || a.forename.Contains(personName)
|| a.surname.Contains(personName))
&& (string.IsNullOrEmpty(dateFrom)
|| a.appStartDateTime >= DateTime.Parse(dateFrom).Date);
dateFrom appears to be a string so you have to parse it to get a date time.
This logic should work but I have not tested it. I could be wrong.
You seem to only care if the forename or surname contain personName if the personName is not null or empty. So you can rewrite it to return things if the personName is null or empty or the fore or sur name contains person name. Since the || operator is short circuiting it will not check Contains if the personName is null or empty.
You can also combine the predicates and make the logic shorter and easier to read:
var result = from a in xxxx select a;
if (!string.IsNullOrEmpty(personName))
result = result.Where(a => a.forename.Contains(personName) || a.surname.Contains(personName)
if (!string.IsNullOrEmpty(dateFrom))
result = result.Where(a >= a.appStartDateTime >= dateFrom.Date)
return result;
This method of combining predicates works well with 'AND' conditions. If you need to 'OR' conditions, it's a little bit more involved. Thankfully, Joe Albahari has created PredicateBuilder (as part of LINQKit) that helps with this greatly.
Hope this helps.
..or with just one exit point:
var result = from a in xxxx select a;
Func<string, bool> func = null;
if(!string.IsNullOrEmpty(personName))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName)};
}
else if(!string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.appStartDateTime >= dateFrom.Date};
}
else if(!string.IsNullOrEmpty(personName) && !string.IsNullOrEmpty(dateFrom))
{
func = (a) => {a.forename.Contains(personName) || a.surname.Contains(personName) && a.appStartDateTime >= dateFrom.Date;};
}
return result.Where(func);

Categories