How to use Contains() function if field value is null in linq - c#

I am working on a project where I use Linq for fetching data. Now I have a scenario where I have to check field value contains given string for that I use Contains() function.
Everything works fine, But when any of the field is null then it creates a problem.
personData = personData.Where(x => x.FirstName.ToLower().Contains(q.ToLower()) || x.LastName.ToLower().Contains(q.ToLower())).Select(x => x).ToList();
Here when FirstName or LastName field have a null value it throw an error.
So, how can I overcome from this problem ?

Use following approach: x.FirstName?.Contains(substring) ?? false
Since C# 6 you can use null-conditional operators, that simplifies some queries greatly. You can read more about this topic here

Please try this
personData = personData.Where(x => (x.FirstName != null && x.FirstName.ToLower().Contains(q.ToLower())) || (x.LastName != null && x.LastName.ToLower().Contains(q.ToLower()))).Select(x => x).ToList();

You must check first if required values are null, Try using the null-coalescing operator...
personData = personData.Where(x => ((x.FirstName.ToLower() ?? "").Contains(q.ToLower())) || ((x.LastName.ToLower() ?? "").Contains(q.ToLower()))).Select(x => x).ToList();

What about using a simple string extension like the following:
public static string AsNotNull(this string value)
{
if (string.IsNullOrWhiteSpace(value))
return string.Empty;
return value;
}
And then use it like:
x.FirstName.AsNotNull()

I think you should prevent ability to add null FirstName or LastName at the beggining. This kind of row seems unuseful.

Related

Linq check value in SQL table always is false

I tried to check if the cost or benefit value exists for the selected Id, then I will get the cost or benefit value from the table.
In the code below my if statement doesn't work and seems to always be false but the return benefit value works fine. Where is the problem?
public string GetCostBenefitAmount(int id)
{
if (db.CostBenefits.Any(c => c.ID == id && !c.Cost.Equals(0)))
{
return db.CostBenefits.Select(c => c.Cost).First().ToString();
}
return db.CostBenefits.Where(c=> c.ID == id).Select(c => c.Benefit).First().ToString();
}
This is my code in windows form for fill txtAmount textBox by GetCostBenefitAmount(int id) method:
var stockIdList = db.CostBenefitRepository.GetAllID();
int id = stockIdList[listBox.SelectedIndex];
CostBenefit costBenefit = db.GenericCostBenefitRepository.GetById(id);
txtStockName.Text = listBox.SelectedItem.ToString();
txtSoldAmount.Text = costBenefit.SoldAmount.ToString();
ComboCostBenefit.SelectedItem = db.CostBenefitRepository.GetCostBenefitOperation(id);
txtAmount.Text = db.CostBenefitRepository.GetCostBenefitAmount(id);
The Object.Equals method determines whether two object instances are equal. Try if (db.CostBenefits.Any(c => c.ID == id && c.Cost != 0)). For more info on the Object.Equals function see this post.
Edit:
As #Gert Arnold commented, the issue was in the return db.CostBenefits.Select(c => c.Cost).First().ToString(); where there was no filtering done before returning.
I think equals is only when you are using in a join. So just use
c.Cost != 0
instead.

FirstOrDefault returns NullReferenceException if no match is found

Here is my code:
string displayName = Dictionary.FirstOrDefault(x => x.Value.ID == long.Parse(options.ID)).Value.DisplayName;
The code works fine if x.Value.ID matches options.ID. However, I get a NullReferenceException if it doesn't.
FirstOrDefault returns the default value of a type if no item matches the predicate. For reference types that is null. Thats the reason for the exception.
So you just have to check for null first:
string displayName = null;
var keyValue = Dictionary
.FirstOrDefault(x => x.Value.ID == long.Parse(options.ID));
if(keyValue != null)
{
displayName = keyValue.Value.DisplayName;
}
But what is the key of the dictionary if you are searching in the values? A Dictionary<tKey,TValue> is used to find a value by the key. Maybe you should refactor it.
Another option is to provide a default value with DefaultIfEmpty:
string displayName = Dictionary
.Where(kv => kv.Value.ID == long.Parse(options.ID))
.Select(kv => kv.Value.DisplayName) // not a problem even if no item matches
.DefaultIfEmpty("--Option unknown--") // or no argument -> null
.First(); // cannot cause an exception
You can use a combination of other LINQ methods to handle not matching condition:
var res = dictionary.Where(x => x.Value.ID == someID)
.Select(x => x.Value.DisplayName)
.DefaultIfEmpty("Unknown")
.First();
Simply use the question mark trick for null checks:
string displayName = Dictionary.FirstOrDefault(x => x.Value.ID == long.Parse(options.ID))?.Value.DisplayName ?? "DEFINE A DEFAULT DISPLAY NAME HERE";
That is because FirstOrDefaultcan return null causing your following .Value to cause the exception. You need to change it to something like:
var myThing = things.FirstOrDefault(t => t.Id == idToFind);
if(myThing == null)
return; // we failed to find what we wanted
var displayName = myThing.DisplayName;
To add to the solutions, here is a LINQ statement that might help
Utilities.DIMENSION_MemTbl.Where(a => a.DIMENSION_ID == format.ContentBrief.DimensionID).Select(a=>a.DIMENSION1).DefaultIfEmpty("").FirstOrDefault();
The result will be an empty string if the result of the query is a null..
This answer is for those of us who need a visual write up (like me :)
In the code screenshot below, a NullReferenceException will be thrown, the root cause is the ReferenceIdentification_02 property.
When debugging, we see that the orderLine.REF array, I am querying does not include a matching object whose ReferenceIdentificationQualifier_01 value == "RU", so at that point FirstOrDefault() return value is NULL
to prevent the NullReferenceException, I do a FirstOrDefault() on the orderLine.REF array first. If the returned value is not null then I retrieve the value.
i assume you are working with nullable datatypes, you can do something like this:
var t = things.Where(x => x!=null && x.Value.ID == long.Parse(options.ID)).FirstOrDefault();
var res = t == null ? "" : t.Value;
you can use with 'Where' statement with FirstOrDefault().
like this.
var modelItem = _dbcontext.ModelName.Where(n => n.NewsTagId == newsTag.Id).FirstOrDefault();
It returns first item if does not match query.
It is better practice to check the NULL after query.
if(modelItem == null)
{
return "Not Found."
}
else
{
// continue process
}

How to select null values with LINQ to SQL and DbLinq?

When I
bool? isApproved = null;
db.Table.Where(item => item.IsApproved == isApproved).Count();
the last line value is 0. But when I
db.Table.Where(item => item.IsApproved == null).Count();
the value is correct.
I'm using SQLite, DbLinq and DbMetal.
I have seen it done like this:
db.Table.Where(
item => item.IsApproved.HasValue == isApproved.HasValue &&
(!item.IsApproved.HasValue || item.IsApproved.Value==isApproved.Value )
).Count();
Well, I had this problem before, I remember that the problem is in converting the LINQ query to a SQL statement.
The second expression has an equal in SQL that:
Where IsAproved is null
but the first expression does not because it is a comparision between a value in the database with a C# nullable variable.
To solve it, I would suggest to try:
db.Table.Where(item => isApproved != null ? item.IsApproved == isApproved.Value
: item.IsApproved == null).Count();
See this post
You should use
db.Table.Where(item => item.IsApproved.Equals(isApproved)).Count();
Then you should contact Microsoft and let them know how terrible this behavior is.
I don't know about the performance hit, but it works
bool? isApproved = null;
db.Table.Where(item => item.IsApproved == isApproved ||
!(item.IsApproved.HasValue || isApproved.HasValue))
.Count();
Try :
db.Table.Where(item => item.IsApproved == isApproved.Value).Count();

How to return a 'Guid' from a 'Nullable<Guid>'?

I am trying to return a Guid value below. However the database(and my dbml) has that column as a nullable Guid and it is generating an exception on the .Select portion saying it can't convert from an IQueryable<System.Guid?> to a System.Guid.
I am guessing I need to make my return value "concrete" first???? True?
If so how do I do that with Guid's?
public static Guid GetCurrentWorkerByType(int enrollmentID, int staffTypeID)
{
using (var context = CmoDataContext.Create())
{
IQueryable<tblWorkerHistory> tWorkHist = context.GetTable<tblWorkerHistory>();
return (tWorkHist.Where(workHist =>
(workHist.EnrollmentID == enrollmentID) &&
(workHist.tblStaff.StaffTypeID == staffTypeID) &&
(workHist.EndDate == null || workHist.EndDate > DateTime.Now))
.Select(workHist => workHist.Worker));
}
}
}
// Get the Guid? itself, for sake of example from IQueryable<Guid?>
// (could be `null` from DB value or because queryable was empty)
Guid? maybeGuid = queryable.FirstOrDefault();
// Need to have *a* GUID or default it to something
// because a Guid is a value-type and needs *a* value.
Guid theGuid = maybeGuid ?? Guid.Empty;
Also see Nullable<T>.HasValue/Value -- A longer, but equivalent, method would be:
Guid theGuid = maybeGuid.HasValue ? maybeGuid.Value : Guid.Empty;
Observe that HasValue may be suitable in general if statements to change logic and also note that Value will throw an exception if maybeGuid is "has no value" -- is null -- which is why the guard is required.
Happy coding.
Pedantic detail: The equivalent method is not "thread safe". That is, assuming maybeGuid was shared, it could be assigned null between HasValue and Value. There are a number of SO questions that cover the "thread safety" of ?? (the coalesce operator) -- the generated IL effectively uses a temporary variable so the value may be stale but an exception can't be thrown.
Use
.Select(workHist => workHist.Worker).Single();
.Select() returns a query that has not run.
If you use .Select().ToList() then you you return a list.
If you use .Select().Single() then you return one item and it makes sure only one item is there
If you use .Select().SingleOrDefault() then you return one item and default. Query must not contain more than 1 item.
If you use .Select().First() then you return the first item. Query must contain at least 1 item.
If you use .Select().FirstOrDefault() then you return the first item or default. Query can contain 1 or more or no items.
Try
Change your return type from System.Guid to ?System.Guid // nullable of guid
Then add .FirstOrDefault() after the select call
A struct cannot be null, but the System.Nullable class wraps the struct in a class.
What you've got is a query that hasn't executed for one, and for two, it will return a list of Guids as far as I can tell. If you want to return the first Guid or default (which I believe is a zero'd out guid) you can say .FirstorDefault() after your select.
The closest you will get representing null with a Guid is to use Guid.Empty. So, for your query, you will probably want to first pick the value returned by FirstOrDefault, make a null check, and then return a reasnable value:
Guid? result = tWorkHist.Where(workHist =>
(workHist.EnrollmentID == enrollmentID)
&& (workHist.tblStaff.StaffTypeID == staffTypeID)
&& (workHist.EndDate == null || workHist.EndDate > DateTime.Now))
.Select(workHist => workHist.Worker)
.FirstOrDefault();
return result.HasValue ? result.Value : Guid.Empty;
Use FirstOrDefault with conjunticon with Guid.Empty
Try this:
public static Guid GetCurrentWorkerByType(int enrollmentID, int staffTypeID)
{
using (var context = CmoDataContext.Create())
{
IQueryable<tblWorkerHistory> tWorkHist = context.GetTable<tblWorkerHistory>();
var guid = (tWorkHist.Where(workHist => (workHist.EnrollmentID == enrollmentID) &&
(workHist.tblStaff.StaffTypeID == staffTypeID) &&(workHist.EndDate == null || workHist.EndDate > DateTime.Now))
.Select(workHist => workHist.Worker)
///####NOTICE THE USE OF FirstOrDefault
).FirstOrDefault();
return (guid.HasValue)?guid.Value:Guid.Empty
}
}

linq case insensitive (without toUpper or toLower)

public Articles GetByName(string name, Categories category, Companies company)
{
var query = from article in session.Linq<Articles>()
where article.Name == name &&
article.Category == category &&
article.Company == company
select article;
return query.FirstOrDefault();
}
how can query be case insensitive. I can use toLower or toUpper but i want with OrdinalIgnoreCase. Is it possible?
Use String.Equals with the appropriate parameters to make it case insensitive
mySource.Where(s => String.Equals(s, "Foo", StringComparison.CurrentCultureIgnoreCase));
Instead of == use the .Equals(name, StringComparison.OrdinalIgnoreCase) method.
var query = from article in session.Linq<Articles>()
where article.Name.Equals(name, StringComparison.OrdinalIgnoreCase) &&
article.Category.Equals(category) &&
article.Company.Equals(company)
select article;
return query.FirstOrDefault();
If this is a LINQ to SQL query against a database with a case-insensitive collation, then it already is case-insensitive. Remember that LINQ to SQL isn't actually executing your == call; it's looking at it as an expression and converting it to an equality operator in SQL.
If it's LINQ to Objects, then you can use String.Equals as the other posters have pointed out.
var query = from article in session.Linq<Articles>()
where string.Equals(article.Name,name, StringComparison.OrdinalIgnoreCase) &&
string.Equals(article.Category,category, StringComparison.OrdinalIgnoreCase) &&
string.Equals(article.Company,company, StringComparison.OrdinalIgnoreCase)
select article;
return query.FirstOrDefault();
It will also handle when Name,Category,Company is null
Use
String.Equals(article.Name, name, StringComparison.OrdinalIgnoreCase)
If you are using C# 6.0 you can define a short extension method to be used when constructing LINQ statements:
public static bool EqualsInsensitive(this string str, string value) => string.Equals(str, value, StringComparison.CurrentCultureIgnoreCase);
Usage:
query.Where(item => item.StringProperty.EqualsInsensitive(someStringValue));
For C# less than 6.0 it will look like this:
public static bool EqualsInsensitive(this string str, string value)
{
return string.Equals(str, value, StringComparison.CurrentCultureIgnoreCase);
}
Change it to
public Articles GetByName(string name, Categories category, Companies company)
{
var query = from article in session.Linq<Articles>()
where string.Equals(article.Name, StringComparison.CurrentCultureIgnoreCase) == name &&
string.Equals(article.Category, StringComparison.CurrentCultureIgnoreCase == category &&
string.Equals(article.Company, StringComparison.CurrentCultureIgnoreCase == company
select article;
return query.FirstOrDefault();
}
Use string.Equals(name, article.Name, StringComparison.OrdinalIgnoreCase) when you are sure that your database supports it.
E.g. SQLite with a collate of NOCASE will ignore the option.
Oracle uses session settings NLS_COMP, NLS_SORT that will be taken.
Else use ToLower() or ToUpper() when you have no culture issues.

Categories