LINQ and check for !=null - c#

Never used LINQ within C# before until 30 mins ago and am struggling to find an answer to my query online (probably due to my lack of understanding).
I have a simple query
var variableName = from a in tableName.AsEnumerable()
where a.column1 == item1
&& a.column2 == item2
&& a.column3 != null
select a;
The SQL column is defined as an int, null.
When the code encounters a record that is null on the database for column3, the following error is generated "the value for column3 in table <tableName> is DBNull".
Instead of checking for != null, i guess i should be using something else, but have attempted checking for DBNull.Value but the compiler states "Operation != cannot be applied to operands of type int and system.DBNull".
Any ideas?

This looks like a typed dataset, which : yeuch - stop using those, but I digress.
As such, accessing a.column3 will always raise an exception if that value is DBNull. You would need to use the typed-dataset pattern:
&& !c.Iscolumn3Null()

tableName.AsEnumerable() makes the query in-memory, so all table rows are downloaded from DB and the conditions are checked on application.
Try that:
var variableName = from a in tableName
where a.column1 == item1
&& a.column2 == item2
&& a.column3 != null
select a;
It should be translated into an SQL query and download only necessary rows.

Try this..
var variableName = from a in tableName.AsEnumerable()
where a.column1 == item1
&& a.column2 == item2
&& a.column3 != dbnull.value
select a;

try
var variableName = from a in tableName.AsEnumerable()
where a.column1 == item1
&& a.column2 == item2
&& !DBNull.Value.Equals(a.column3)
select a;
edit apparently I need to read up on typed data sets :) and why I should never use them

Related

How to check in a linq query for null?

I am trying in a linq query to check if one of the fields is null but i get this error whatever i do.
"Non-static method requires a target."
This is my code :
var users = from s in db.Users
where s.DepartmentId == booking.Item.DepartmentId && s.UserEmail != null
select s;
is any way to go through this error and be able to actually check if UserEmail is null?
p.s : i am using asp.net mvc entity framework.
Be sure that the first 2 letters are uppercase (DBNull.Value).
EDIT:
Try to copy your booking item into a local variable.
var departmentId = booking.Item.DepartmentId;
var users = from s in db.Users
where s.DepartmentId == departmentId && s.UserEmail != null
select s;
var users = from s in db.Users
where s.DepartmentId == booking.Item.DepartmentId && s.UserEmail != DbNull.Value
select s;
You need to compare it to DbNull.Value

LINQ - Result not null if querying a nonexistent element

Currently, i have a table with the data Fruits, grade and price. The grade available in my table are A , B and C. Please look at the code snippet. As what you can see, i put D in the where filter. I suppose the query will be null. But after i run the code, the program prompt the "Not Null? WTH?". So, may i know whats in the query and how to detect the D if it's not in the table?
p/s: i'm newbie
C# code snippet:
var query = (from p in db
where p.grade == "D"
select p.price).ToArray();
if (query == null)
System.Console.WriteLine("You get Null in the query");
else if (query != null)
System.Console.WriteLine("Not Null? WTH?");
Since you are doing ToArray, it will never be null. You may check the length.
if(query.Length <= 0)
Or even better if you do:
var query = (from p in db
where p.grade == "D"
select p.price).Count();
if(query > 0)
or
var ifItemExist = (from p in db
where p.grade == "D"
select p.price).Any();
Or shorter:
var ifItemExist = db.Any(p=> p.grade == "D");
You don't get null because this method will still return an array. The array will be empty, but the array will not be null. (This is in fact why null exists... it is to differentiate between a "zero" value and the absence of a value.
If you want to test if there are any elements in the result you can use:
if (query.Any())
Actually, it is not null, it's returning an empty array which is different from null.
alternately, you can do this,
int query = (from p in db
where p.grade == "D"
select p.price).Count();
if (query > 0)
{
System.Console.WriteLine("Found");
}
else
{
System.Console.WriteLine("Not Found");
}

Searching for text in a database with Entity Framework

I'm writing a UI that allows a someone to lookup users by their first and/or last name. For example, if you typed in "Mike" for the first name and "Jo" for the last name, it would return "Mike Jones", "Mike Johnson" and "Mike Jobs". I use the following LINQ statement for this search:
var users = (from u in context.TPM_USER
where u.LASTNAME.ToLower().Contains(LastName.ToLower())
&& u.FIRSTNAME.ToLower().Contains(FirstName.ToLower())
select u);
(There may or may not be a better way to do a case-insensitive like clause, but this seems to work)
The problem is if the user types in a first or last name, but then leaves the other field empty. If I type in "Mike" for the first name and leave the Last Name field blank, I want to return all Mikes regardless of their last name. The above query returns no results unless both fields are filled in with at least something.
I tried:
var users = (from u in context.TPM_USER
where (LastName == "" || u.LASTNAME.ToLower().Contains(LastName.ToLower()))
&& (FirstName == "" || u.FIRSTNAME.ToLower().Contains(FirstName.ToLower()))
select u);
However, I still get no results unless both fields are filled out. I've verified under the debugger that LastName == "" is indeed true.
UPDATE:
I did some more debugging and this is actually an Oracle issue. The query being generated is:
--Replaced the field list with * for brevity
SELECT * FROM TPMDBO.TPM_USER "Extent1"
WHERE (('jones' = '') OR ((INSTR(LOWER("Extent1".LASTNAME), LOWER('jones'))) > 0)) AND (('' = '') OR ((INSTR(LOWER("Extent1".FIRSTNAME), LOWER(''))) > 0))
Which at first glance appears to be correct. However, Oracle does not seem to correctly short-circuit the phrase ('' = ''). In fact, if I do:
select * from TPM_USER where '' = ''
I get zero rows. I'm not enough of an Oracle expert to know how this query should be written, but either way it's an Entity Framework dialect bug.
Just add the predicates conditionally:
var users = from u in context.TPM_USER select u;
if (!string.IsNullOrWhiteSpace(FirstName))
users = users.Where(u => u.FIRSTNAME.ToLower().Contains(FirstName.ToLower()));
if (!string.IsNullOrWhiteSpace(LastName))
users = users.Where(u => u.LASTNAME.ToLower().Contains(LastName.ToLower()));
Or only the LASTNAME predicate as conditional one.
Later addition:
An expression like Where(u => u.FIRSTNAME.ToLower()... is better to be avoided. They cause any indexes on FIRSTNAME to be ignored, because the field value is converted first and then compared (see here for more details).
There's a big chance you don't need these lower-case conversions. Check the database collation of the field. If it's case-insensitive (CI), which it probably is, you don't need these conversions.
Are you sure that FirstName and LastName aren't null?
You might try writing it like this instead...
string LowerFirstName = (FirstName + "").ToLower();
string LowerLastName = (LastName + "").ToLower();
var users = (from u in context.TPM_USER
where (LowerLastName == "" || u.LASTNAME.ToLower().Contains(LowerLastName))
&& (LowerFirstName == "" || u.FIRSTNAME.ToLower().Contains(LowerFirstName))
select u);
FYI, if anyone runs into this issue with Oracle, here's a workaround:
var users = (from u in context.TPM_USER
where (LastName == null|| u.LASTNAME.ToLower().Contains(LastName.ToLower()))
&& (FirstName == null || u.FIRSTNAME.ToLower().Contains(FirstName.ToLower()))
select u);
This will get converted to:
'' is null
In SQL, which Oracle interprets as true.
You could simply create a conditional statement around your query:
if (String.IsNullOrWhiteSpace(LastName) && !String.IsNullOrWhiteSpace(FirstName))
{
var users = (from u in context.TPM_USER
where (u.FIRSTNAME.ToLower().Contains(FirstName.ToLower()))
select u);
}
else if (String.IsNullOrWhiteSpace(FirstName) && !String.IsNullOrWhiteSpace(LastName))
{
var users = (from u in context.TPM_USER
where (u.LASTNAME.ToLower().Contains(LastName.ToLower()))
select u);
}
May be you can try checking the length of the search terms to see if it is working in Oracle PL/SQL.
var users = (from u in context.TPM_USER
where ((LastName ?? "").Trim().Length == 0 || u.LASTNAME.ToLower().Contains(LastName.ToLower()))
&& ((FirstName ?? "").Trim().Length == 0 || u.FIRSTNAME.ToLower().Contains(FirstName.ToLower()))
select u);

LinQ Query Where Condition

I am trying to import data into a new database, but some of the columns in the old database are null.
In one of my methods, I am using the query below to get the records, like I said already there is a column in the rows which has a null value for some records.
Guid nameGuid= new guid('CCECE54B-EE14-4463-8A0B-02C72679334A')
MySubQuery = from a in MainQuery
where a.Table1.Id.Equals(nameGuid)
Select a;
I want to check for a.Table1.Id value, if it is equal to null, then I still want the row but ignore the where condition.
Any suggestion for using the ternary operator in Linq query or any other approaches for my task.
Sounds like you want:
MySubQuery = from a in MainQuery
where a.TableId.Id Is Nothing OrElse a.Table1.Id.Equals(nameGuid)
That's assuming my VB is correct... in C# I'd just write:
var query = from a in mainQuery
where a.TableId.Id == null || a.TableId.Id == nameGuid
select a;
or using the extension method directly:
var query = mainQuery.Where(a => a.TableId.Id == null ||
a.TableId.Id == nameGuid);
How about:
MySubQuery = from a in MainQuery
where a.Table1.Id == null || a.Table1.Id.Equals(nameGuid)
select a;
Or am I missing something?
EDIT:
The thing I am missing is what Mr Skeet spotted. It's VB. Well, I'll let this stick around as a C# sample.
var datarows = (from row in dt.AsEnumerable()
where row.Field<Guid>("Id") == new
Guid(ddlCode.SelectedValue.ToString())
select row);
After that
DataTable newDt=datarows.CopyToDataTable();
var sampleName=newDt.Rows[0]["SampleName"].ToString();

"IN" Operator in Linq

I am trying to convert an old raw Sql query in Linq with Entity Framework here.
It was using the IN operator with a collection of items. The query was something like that:
SELECT Members.Name
FROM Members
WHERE Members.ID IN ( SELECT DISTINCT ManufacturerID FROM Products WHERE Active = 1)
ORDER BY Members.Name ASC
Since the return of the subquery is not a single string but a collection of strings I can't use the String.Contains() method.
I thought about doing something like :
var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);
and then
var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));
but it stops at the contains saying it has invalid arguments ... I can't select activeProducts.ManufacturerID because obviously the proprety is not there since it returns an IQueryable...
Bottom line what I'm trying to do here is to return a list of members who have at least one active product.
Any hint ?
[edit]
Here's the full query code ... I tried with the contains on the second expression, Linq didn't seem to like it :
Server Error in '/' Application.
LINQ to Entities does not recognize the method 'Boolean Contains[String](System.Linq.IQueryable``1[System.String], System.String)' method, and this method cannot be translated into a store expression.
var activeProduct =(from product in Master.DataContext.ProductSet
where product.Active == true
&& product.ShowOnWebSite == true
&& product.AvailableDate <= DateTime.Today
&& ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
select product.ManufacturerID.ToString() );
var activeArtists = from artist in Master.DataContext.ContactSet
where activeProduct.Contains(artist.ID.ToString())
select artist;
NumberOfArtists = activeArtists.Count();
artistsRepeater.DataSource = activeArtists;
artistsRepeater.DataBind();
[More details]
ManufacturerID is a nullable GUID apparently...
For some reason the ContactSet class do not contain any reference to the products I guess I will have to do a join query, no clues here.
var activeMembers = (
from member in db.ContactSet
where activeProducts.Select(x=>x.ID).Contains(member.ID));
Try where activeProducts.Contains(member.ID).
EDIT: Did you try it without any ToStrings?
You can do it in one query:
var q = from member in db.ContactSet
where member.Products.Any(p => p.IsActive)
select member;
Try the solution posted by Colin Meek at: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/095745fe-dcf0-4142-b684-b7e4a1ab59f0/. It worked for me.
What about this:
from m in members
where products.FirstOrDefault(prod => prod.IsActive == 1 && prod.Id == m.Id) != null
select m;
you can chain any number of conditions required in the where clause using &&
Ash..
from m in members
where products.Any(p => p.Active && p.ManufacturerID == m.ID)
select m
or
from m in members
join p in products on m.ID equals p.ManufacturerID
where p.Active
select m
Instead of this:
var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));
Try this:
var activeMembers = (
from member in db.ContactSet
where activeProducts.Contains(member.ID));
What if you swap the statement (untested)?
where activeProducts.Contains(member.ID)
How about this...
var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);
var activeMembers = (
from member in db.ContactSet
where activeProducts.Contains(member.ID.ToString()));
A helper or extension method will work fine when querying against objects in memory. But against an SQL database, your LINQ code will be compiled into an expression tree, analysed and translated into an SQL command. This functionality has no concept of custom-made extension methods or methods of other objects like .Contains(...).
It could be easily implemented into the standard LINQ-To-SQL functionality by Microsoft though. But as long as they don't want, we're helpless as long it's not an open source functionality.
All you can do is create your own QueryProvider that goes against an SQL database. But it will be hard and it would be only for that one in feature alone that you're missing.
However, if you really wanna go that route, have fun: LINQ: BUILDING AN IQUERYABLE PROVIDER SERIES
Finally I managed to code something really ugly, but that actually works! (lol)
var activeProduct =(from product in Master.DataContext.ProductSet
where product.Active == true
&& product.ShowOnWebSite == true
&& product.AvailableDate <= DateTime.Today
&& ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
select product.ManufacturerID ).Distinct();
var artists = from artist in Master.DataContext.ContactSet
select artist;
List<Evolution.API.Contact> activeArtists = new List<Evolution.API.Contact>();
foreach (var artist in artists)
{
foreach(var product in activeProduct)
{
if (product.HasValue && product.Value == artist.ID)
activeArtists.Add(artist);
}
}
NumberOfArtists = activeArtists.Count();
artistsRepeater.DataSource = activeArtists;
artistsRepeater.DataBind();
I have already posted about the same at
http://www.codeproject.com/Tips/336253/Filtering-records-from-List-based-similar-to-Sql-I
var q = (from p in db.DOCAuditTrails
where p.ActionUser == "MyUserID"
&& p.ActionTaken == "Not Actioned"
&& p.ActionDate > DateTime.Parse("2011-09-13")
select p.RequisitionId).Distinct();
var DocAuditResults = db.DOCAuditTrails.Where(p
=> q.ToArray().Contains(p.RequisitionId));
Without know the exact mappings it is hard to tell what can be done and what can't. I will assume that there isn't any casting involved. Firstly you have to remember that everything in the Linq Expression tree must have an equivalent in SQL. As some others have noted, you have a object.ToString() in your Linq Statements.
However it seems that what people have neglected to mention is that you have TWO usages of object.ToSting(), both of which must be removed.
I would also make an extra variable to change the closure's capture type to be explicitly of DataContext (since the Linq statement is like a lambda, and delayed evaluated. It will need to take the whole of the Master variable. Earlier I stated that everything in your Linq must have an equivalent in SQL. Given that Master can't possibly exist in SQL, there is no DataContext property/column/mapping for the type of Master).
var context = Master.DataContext;
var activeProduct = from product in context.ProductSet
where product.Active == true
&& product.ShowOnWebSite == true
&& product.AvailableDate <= DateTime.Today
&& ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
select product.ManufacturerID;
var activeArtists = from artist in context.ContactSet
where activeProduct.Contains(artist.ID)
select artist;
I hope the above changes work for you.
In many cases issues with Linq to ORMs can be traced back to your Linq Expression capturing a non primative (DateTime, int, string etc) and non ORM based class (DataContext/EntityObject etc). The other major gotcha is usage of functions and operators that aren't exposed by the ORM (it is possible to map user defined functions to .net function through the ORM, but I would not recommend it due to indexing issues).

Categories

Resources