C# lambda IN clause error - c#

I’m having issues creating an IN clause using C# and lambdas.
I have the following method GetUserList(string filtersByRoles)
The variable string filtersByRoles can hold a comma-delimited value such as: “1,2” or “1,2,3” or “1,3,4” etc...each number represents the unique number of a Role (in other words, RoleId).
I then have the following C# lambda query:
var query = _userRepository.GetUserList();
Which returns an IQueryable<User> where User is a table from my EntityFramework.
Once I verify if the filtersByRoles parameter is not null-or-empty, I need to make an IN clause such as:
if (!string.IsNullOrEmpty(filtersByRoles))
{
//Convert *filtersByRoles* to an array of integers
int[] myArray = filtersByRoles.Split(',').Select(x => int.Parse(x)).ToArray();
//Make the IN clause
query = query.Where(u => myArray.Contains(u.RoleId));
}
The above code compiles...but at RUNTIME it fails with the following error message:
LINQ to Entities does not recognize the method 'Boolean
Contains[Int32](System.Collections.Generic.IEnumerable`1[System.Int32],
Int32)' method, and this method cannot be translated into a store
expression.
I’ve manage to find a workaround but it involves making a call to the .ToList() method which I believe fetches all the data from my database and then, adds a Where() clause.
But wouldn’t that defeat the purpose or create some performance issues?
This is what I’ve done:
if (!string.IsNullOrEmpty(filtersByRoles))
{
string[] myArray = filtersByRoles.Split(',');
query = query.ToList().Where(u => myArray.Contains(u.RoleId.ToString())).AsQueryable();
}
I would prefer not to make the .ToList() call and avoid fetching all the data.
Is there another way to achieve this?
EDIT:
I'm using Entity Framework 1.0 and .NET Framework 3.5
Thanks
Sincerely
Vince

Here are my 2 cents:
Maybe the Dynamic LinQ will help solve your problem:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
You could build your Where clause as a string, say something like :
string sWhereClause = "1 = 1";
foreach(string rId in filtersByRoles.Split(','))
sWhereClause += " OR RoleId = " + rId;
(I would suggest to use StringBuilder instead of +concat, but for the purpose of this answer, it doesn't matter)
and then
query = query.Where(sWhereClause);
I haven't tryed it though, but it sounds fair for solving your problem. Even though it looks like SQL injection... Well, improvements can be brought.
EDIT:
As a second thought I manage to come with this new idea:
string filterByRoles = "1,2,3";
query = query.Where(new Func<User, bool>(u => {
return filterByRoles.Contains(u.RoleId.ToString());
})).AsQueryable();
This way, you can add whatever code you want in the Func{ ... } delegate, as long as it returns a boolean (I assumed here your TInput was a "User" class, of course change it to use the one corresponding to you needs).
Hope this helps!

This is supported with EF 4 : http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx

Based on some of your replies, I’ve manage to pull up something like this:
int[] myArray = filtersByRoles.Split(',').Select(x => int.Parse(x)).ToArray();
int count = myArray.Count();
int role1;
int role2;
int role3;
int role4;
switch (myArray.Length)
{
case 1:
role1 = myArray[0];
query = query.Where(u => u.RoleId.Equals(role1));
break;
case 2:
role1 = myArray[0];
role2 = myArray[1];
query = query.Where(u => u.RoleId.Equals(role1)
|| u.RoleId.Equals(role2));
break;
case 3:
role1 = myArray[0];
role2 = myArray[1];
role3 = myArray[2];
query = query.Where(u => u.RoleId.Equals(role1)
|| u.RoleId.Equals(role2)
|| u.RoleId.Equals(role3));
break;
case 4:
role1 = myArray[0];
role2 = myArray[1];
role3 = myArray[2];
role4 = myArray[3];
query = query.Where(u => u.RoleId.Equals(role1)
|| u.RoleId.Equals(role2)
|| u.RoleId.Equals(role3)
|| u.RoleId.Equals(role4));
break;
}
When directly trying with the myArray[xxx]:
query = query.Where(u => u.RoleId.Equals(myArray[0]));
I was getting this:
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to
Entities.
Hence the creation of the 4 (integer) variables!
It now works but may need some optimization…
Thanks

Related

how to search record from single table with multiple parameters using LINQ?

I am trying to search record(s) from table by appying multiple search parameters.
as per below snap.
here by using various parameters as per above snap i want to filter the records.
here user could enter any combination of parameter(s) to search record.
i tried something like below code hich works for single condition but fails for combination of any search paramets.
public List<students> SearchStudents(students search)
{
var result = new List<students>();
var records= from stud in db.students
where stud.enrollmentNumber== search.enrollmentNumber
|| stud.enrollmentDate==search.enrollmenttDate
|| stud.enrollmentType==search.enrollmentType
|| stud.className==search.className
select new Search()
{
enrollmentNumber= stud.enrollmentNumber,
enrollmentDate = stud.enrollmentDate,
enrollmentType = stud.enrollmentType,
Name = stud.Name,
className=stud.className,
Description = stud.Description
};
result = records.ToList();
return result;
}
but this is not working properly. means it returns same result whatever parameters I pass.
Like in the table i ahve 20 records and the enrollment number is the unique value field in DB so here when i am passing enrollment number thats like "2018-0001" it returns all records when it should return only single reocrd.
can someone guide me with this?
Without further explanation in your question about how this isn't working, the best we can do is guess. However, one very plausible reason for this is because you're including parameters you don't want to be filtering on.
Because you're using ORs in your statement, if any of those other properties are defaulted in the database, you're going to be returning those records. What you need to be doing is conditionally including your pieces of the WHERE clauses for only the properties that you want to search on. Unfortunately, that is not possible with the "SQL syntax" version of LINQ, so you will need to convert your query to that. (Good news: It's slightly more performant as well as it usually has to convert the SQL to the method syntax.)
Because of deferred execution, your query will not be sent to the database until you call a .ToList() or something to actually start processing the results. This allows you to chain method calls together, even if they are completely different C# statements. This is what you'll want to do:
public List<students> SearchStudents(students search)
{
var query = db.students;
if (!string.IsNullOrWhiteSpace(search.enrollmentNumber))
{
query = query.Where(s => s.enrollmentNumber == search.enrollmentNumber);
}
if (search.enrollmentDate != DateTime.MinValue)
{
query = query.Where(s => s.enrollmentDate == search.enrollmentDate);
}
if (!string.IsNullOrWhiteSpace(search.enrollmentType))
{
query = query.Where(s => s.enrollmentType == search.enrollmentType);
}
if (!string.IsNullOrWhiteSpace(search.className))
{
query = query.Where(s => s.className == search.className);
}
return query.Select(stud => new Search
{
enrollmentNumber= stud.enrollmentNumber,
enrollmentDate = stud.enrollmentDate,
enrollmentType = stud.enrollmentType,
Name = stud.Name,
className=stud.className,
Description = stud.Description
})
.ToList();
}
You may need to adjust the if statements in there to accommodate different data types than what is intuitive from the names, but this will only add the filter if a value has been provided.

Dynamic Linq "Where statement" with Relation

I'm trying to build a query using Dynamic Linq and a where string statement calculated by myself. For example:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery = "(Id = 1 and Number = 2)";
IQueryable<Publication> query = db.Publications.Include(i => i.Product);
query = query.Where(filterQuery);
results = query.OrderBy(orderQuery).ToList();
This is working great and I get a List of Publications with Products. Now... the question is. How can I make a string statement to get results based on a relation to Product using Dynamic Linq and a string statement?
Something like:
string filterQuery = "(Id = 1 and Number = 2 and Products.Id = 1)"
After a lot of research and trying things, this is an easy and friendly way using the same Dynamic Linq library:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery = "(Id = 1 and Number = 2)";
string filterQueryChildren = "Products.Any(Id == 1)"
IQueryable<Publication> query = db.Publications.Include(i => i.Product).Where(filterQueryChildren);
query = query.Where(filterQuery);
results = query.OrderBy(orderQuery).ToList();
I am not sure why you're doing this, but the following should work:
List<Publication> results = new List<Publication>();
// Just an example, previously calculated dynamically
string filterQuery1 = "(Id = 1)"
string filterQuery2 = "(Id = 1 and Number = 2)";
IQueryable<Publication> query = db.Publications.
Where(filterQuery1).
Include(i => i.Product);
query = query.Where(filterQuery2);
results = query.OrderBy(orderQuery).ToList();
To make dynamic LINQ query, you need a library that support it or doing it yourself with Expression Tree
See: Entity Framework Dynamic Query Library
Disclaimer: I'm the owner of the project Eval-Expression.NET
This library allows you to evaluate, compile, and execute code at runtime.
The library also contains extension method for dynamic LINQ
Wiki: Eval Dynamic LINQ
Example
// using Z.Expressions; // Don't forget to include this.
// The filterQuery must use the C# syntax
string filterQuery = "x.Id == 1 && x.Number = 2";
IQueryable<Publication> query = db.Publications.Include(i => i.Product);
query = query.Where(x => filterQuery);
EDIT: Answer sub question
Great but how can I filter by Product?
Entity Framework doesn't support filter in Include method.
However, EF+ do
Disclaimer: I'm the owner of Entity Framework Plus
See: EF+ Query IncludeFilter
By combining both libraries, you can achieve your desired result:
// Extension method must be registered, otherwise the library cannot be aware of which extension method exists!
EvalManager.DefaultContext.RegisterExtensionMethod(typeof (QueryIncludeFilterExtensions));
string where1 = "x.Id == 1 && x.Number == 2";
string where2 = "y.Id == 3";
var left2 = ctx.Publications
.Where(x => where1)
.Execute<IQueryable<Publication>>("IncludeFilter(x => x.Product.Where(y => " + where2 + "))")
.ToList();
If you want to try this solution, make sure you download the latest version of Eval-Expression.NET. We just have fixed few min ago an issue we found by trying your scenario.
EDIT2: Answer sub question
Here is what I recommend you.
Try the query without our library then try it dynamically after.
That's easier to find the compilation error or what you want to do really.
By example, we probably didn't understand the initial problem correctly and suggested IncludeFilter which you don't want. I assumed you wanted to filter multiple products. However, it looks you only have one product per publication, so the Where clause method obviously doesn't exist.
Maybe this is more what you are looking for:
string where1 = "x.Id == 1 && x.Number == 2 && x.Product.Id == 3";
var left2 = ctx.Publications
.Include(x => x.Product)
.Where(where1)
.ToList();
So in short, try the query by hardcoding it (without dynamic) then you will know how to use it dynamically after.

linq join on guid and string column

I'm new to linq. I need to run a query that joins two columns (AnonymousUser.AnonymousId being uniqueidentifier and comment.UserId being nvarchar(100)), something like below:
using (CommentEntities db = new CommentEntities())
{
// filteredComments is a query that is not run until the next .ToList()
IQueryable<Comment> filteredComments = this.CommentGetList(...);
var query = from comment in filteredComments
// following line is the syntax error, because columns' types don't match
join user in db.AnonymousUsers on comment.UserId equals user.AnonymousId into gj
from userNull in gj.DefaultIfEmpty()
select new CommentWithName
{
Comment = comment,
UserId = comment.UserId,
FirstName = (userNull == null ? "" : userNull.Name),
LastName = "",
Email = (userNull == null ? "" : userNull.Email)
};
return query.ToList();
}
First I was happy writing the query with .ToString() ! As it turns out that entity framework doesn't know how to translate it to sql. The same is true for Guid.Parse(string). Also new Guid(string) cannot be used in linq to entities (only parameterless constructors allowed)!
So after searching, I found out it's not possible doing such thing in EF 4.0! I migrated my code to a stored procedure that I'm not really happy about it.
Is it possible to tell entity framework to use a CAST in SQL?
Is there any solutions to this problem? Is there any way that I can bring the logic in code?
NOTE: I meant to do it in one GO. Otherwise one possible solution is to get Entities from first table, and put the Ids in a list and get entities from second table.
call toList() before applying those methods. Like:
var Product = db.Products.Where(p => p.ProductId == Guid.Parse("B4E913F9-166C-49BA-AADE-6DB889D1756F")).Single();
Would throw a
c# LINQ to Entities does not recognize the method "System.Guid Parse" (System.String)' method, and this method cannot be translated into a store expression
But this works:
var Product = db.Products.ToList().Where(p => p.ProductId == Guid.Parse("B4E913F9-166C-49BA-AADE-6DB889D1756F")).Single()
p.s.: I think you will lose lazyloading but you can do eagerloading with .Include before calling .ToList().
If your list is object list you could convert it to the type which has Guid as identifier, first create new anonymous type and then filter it base on UserId, sure UserId which is of type int, wont include in join:
int output = 0;
var secondList = list.Where(x=>!int.TryParse(x.UserID, out output))
.Select(x=>new {Comment = x, ID = new Guid(x.UserID))
.ToList();
Now you could run your query on db by using secondList.

Why do LINQ to Entities does not recognize certain Methods?

Why cant I do this:
usuariosEntities usersDB = new usuariosEntities();
foreach (DataGridViewRow user in dgvUsuarios.Rows)
{
var rowtoupdate =
usersDB.usuarios.Where(
u => u.codigo_usuario == Convert.ToInt32(user.Cells[0].Value)
).First();
rowtoupdate.password = user.Cells[3].Value.ToString();
}
usersDB.SaveChanges();
And have to do this:
usuariosEntities usersDB = new usuariosEntities();
foreach (DataGridViewRow user in dgvUsuarios.Rows)
{
int usercode = Convert.ToInt32(user.Cells[0].Value);
var rowtoupdate =
usersDB.usuarios.Where(u => u.codigo_usuario == usercode).First();
rowtoupdate.password = user.Cells[3].Value.ToString();
}
usersDB.SaveChanges();
I must admit it is a more readable code but why cant this be done?
The thing about it is that LINQ queries are transformed by the compiler into an expression tree. This expression tree is then converted into T-SQL and passed to the server. LINQ to SQL maps certain methods like String.Contains to the T-SQL equivalents.
In the first example, LINQ apparently does not map Convert.ToInt32 to anything and the exception is thrown. The reason it works in your second example is because the Convert.ToInt32 call is done outside of the query so it isn't part of the expression tree and doesn't need to be converted to T-SQL.
This MSDN page describes how LINQ to SQL translates various data types, operators, and methods into T-SQL. (Although the documentation does suggest Convert.ToInt32 is supported so I'm not sure what else might be going on here.)
Sorry just realized this is ADO.NET Entity Framework, not LINQ to SQL. This page lists the ADO.NET Entity Framework mappings. It is a bit more restrictive, mostly because it needs to work with multiple providers.
Because your LINQ to Ent. doesn't compile the query into MSIL with all the meta data, but simply translates the query into a few extantion methods and is bounded to lambda parsing abuilities of the languge. That means that
this code:
var results = from c in SomeCollection
where c.SomeProperty < someValue * 2
select new {c.SomeProperty, c.OtherProperty};
is the same as this:
var results =
SomeCollection
.Where(c => c.SomeProperty < someValue * 2)
.Select(c => new {c.SomeProperty, c.OtherProperty});

Which ORM Supports this

I have an optional part of query that needs to be executed on a certain condition. Here is the example code:
int cat = 1;
int UserID = 12;
string qry = "select * from articles";
if(cat > 0)
qry += " where categoryID = " + cat;
if(UserID > 0)
qry += " AND userid = " + UserID; //The AND may be a WHERE if first condition is false
As you can see I have an if statement in the query. i am currently using Entity Framework and it does not support this kind of scenario. Is there an ORM out there that support this?
Edit
I tried to dummy down the query. But I have about 20 "IF" statements and the querys are very long.
The ORMs I was looking at were:
NHibernate
LLBLGen
Subsonic
I am open to any ORM. Thanks
As it was already mentioned here, LINQ allows to extend any query by simply adding more criteria to it.
var query =
from x in xs
where x==1
select x;
if (mustAddCriteria1)
query =
from x in query
where ... // criteria 1
select x;
if (mustAddCriteria2)
query =
from x in query
where ... // criteria 2
select x;
And so on. This approach works just perfectly. But likely, you know that compilation of LINQ queries is pretty expensive: e.g. Entity Framework can compile just about 500 relatively simple queries per second (see e.g. ORMBattle.NET).
On the other hand, many ORM tools support compiled queries:
You pass an IQueryable instance to some Compile method, and get a delegate allowing to execute it much faster later, because no recompilation would occur in this case.
But if we'd try to use this approach here, we immediately notice that our query is actually dynamic: IQueryable we execute each time might differ from the previous one. Presence of query parts there is determined by values of external parameters.
So can we execute such queries as compiled without e.g. explicit caching?
DataObjects.Net 4 support so-called "boolean branching" feature. It implies any constant boolean expression is evaluated during query compilation and its actual value is injected into SQL query as true boolean constant (i.e. not as parameter value or as an expression utilizing parameters).
This feature allows to generate different query plans dependently on values of such boolean expressions with ease. E.g. this code:
int all = new Random().Next(2);
var query =
from c in Query<Customer>.All
where all!=0 || c.Id=="ALFKI"
select c;
will be executed using two different SQL queries, and thus - two different query plans:
Query plan based on index seek (quite fast), if all==0
Query plan based on index scan (quite slow), if all!=0
Case when all==null, SQL query:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( CAST( 0 AS bit ) <> 0 ) OR( [a].[CustomerId] = 'ALFKI' ) );
Case when all==null, query plan:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Seek(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]), SEEK:([a].[CustomerId]=N'ALFKI') ORDERED FORWARD)
Second case (when all!=null), SQL query:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( CAST( 1 AS bit ) <> 0 ) OR( [a].[CustomerId] = 'ALFKI' ) );
-- Notice the ^ value is changed!
Second case (when all!=null), query plan:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Scan(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]))
-- There is index scan instead of index seek!
Note that almost any other ORM would compile this to a query utilizing integer parameter:
SELECT
[a].[CustomerId],
111 AS [TypeId] ,
[a].[CompanyName]
FROM
[dbo].[Customers] [a]
WHERE(( #p <> 0 ) OR ( [a].[CustomerId] = 'ALFKI' ) );
-- ^^ parameter is used here
Since SQL Server (as well as most of databases) generates a single version of query plan for a particular query, it has the only option in this case - generate a plan with index scan:
|--Compute Scalar(DEFINE:([Expr1002]=(111)))
|--Clustered Index Scan(OBJECT:([DO40-Tests].[dbo].[Customers].[PK_Customer] AS [a]), WHERE:(CONVERT(bit,[#p],0)<>(0) OR [DO40-Tests].[dbo].[Customers].[CustomerId] as [a].[CustomerId]=N'ALFKI'))
Ok, that was a "quick" explanation of usefulness of this feature. Let's return back to your case now.
Boolean branching allows to implement it in very simple fashion:
var categoryId = 1;
var userId = 1;
var query =
from product in Query<Product>.All
let skipCategoryCriteria = !(categoryId > 0)
let skipUserCriteria = !(userId > 0)
where skipCategoryCriteria ? true : product.Category.Id==categoryId
where skipUserCriteria ? true :
(
from order in Query<Order>.All
from detail in order.OrderDetails
where detail.Product==product
select true
).Any()
select product;
The example differs from yours, but it illustrates the idea. I used different model mainly to be able to test this (my example is based om Northwind model).
This query is:
Not a dynamic query, so you can safely pass it to Query.Execute(...) method to get it executed as compiled query.
Nevertheless each its execution will lead to the same result as if this would be done with "appending" to IQueryable.
this can be done using linq to sql...
IQueryable<Article> query = yourDataContext.Articles;
if (catId > 0)
query = query.Where(x => x.CategoryId == catId);
return query.ToList();
NHibernate supports this using the Criteria API:
ICriteria criteria = session.CreateCriteria<Article>();
if (cat > 0)
criteria.Add(Expression.Eq("categoryID", cat));
You can probably do this with any LINQ provider, but I know the LightSpeed ORM supports it:
var query = UnitOfWork.Articles;
if (cat > 0)
query = query.Where(a => a.CategoryId == cat);
I do this kind of thing in NHibernate all the time.
(I've done similar things in Rails. I'm kind of surprised that there are ORMs that don't support this.)
You can easily build queries in this way using NHibernate's HQL (Hibernate Query Language). It would be an almost identical implementation but I would personally use parameters.
public List<Article> GetCat(int cat)
{
string qry = "select ap from Article a";
if(cat > 0)
qry += " where a.categoryID = :cat";
IQuery query = session.CreateQuery(qry).SetInt32("cat",cat);
return query.List<Article>();
}
This returns a List<> of Article objects ready for use.
No love for LLBLGen? Well it can can do it too.
Using the 'adapter' style:
RelationPredicateBucket filters = new RelationPredicateBucket();
if (cat > 0)
filters.Predicate.Add(Article.Fields.CategoryID == cat);
if (userId > 0)
filters.Predicate.Add(Article.Fields.UserID == userId);
// And so on.
var adapter = new DataAccessAdapter();
var results = new EntityCollection<Article>(new ArticleFactory());
adapter.FetchEntityCollection(results, filters);
I would suspect most ORMs should be able to do this pretty easily.
You can use the Predicate Builder and LINQ to NHibernate to generate dynamic query's like this:
//using Predicate Builder
public List<Location> FindAllMatching(string[] filters)
{
var db = Session.Linq<Location>();
var expr = PredicateBuilder.False<Location>(); //-OR-
foreach (var filter in filters)
{
string temp = filter;
expr = expr.Or(p => p.Name.Contains(temp));
}
return db.Where(expr).ToList();
}
You get the advantage of Type Save Query's and Compiler check.
You can also use the same approach of predicate builder with Linq to Sql and Entity Framework.
EDIT: Added example.
It could be something like get all the locations matching N regions of the world, where the user select the regions he want to see, we don't know how many the user will select, we must build the (OR) expression on the fly, you can do something like:
public ActionResult Action(string[] filters)
{
/*This values are provided by the user, maybe its better to use
an ID instead of the name, but for the example is OK.
filters will be something like : string[] filters = {"America", "Europe", "Africa"};
*/
List<Location> LocationList = FindAllMatchingRegions(filters);
return View(LocationList);
}
public List<Location> FindAllMatchingRegions(string[] filters)
{
var db = Session.Linq<Location>();
var expr = PredicateBuilder.False<Location>(); //-OR-
foreach (var filter in filters)
{
string temp = filter;
expr = expr.Or(p => p.Region.Name == filter);
}
return db.Where(expr).ToList();
}
You can Nest Predicates for a complex scenarios like this:
If you want to do something like
p => p.Price > 99 &&
p.Price < 999 &&
(p.Description.Contains ("foo") || p.Description.Contains ("far"))
you can build:
var inner = PredicateBuilder.False<Product>();
inner = inner.Or (p => p.Description.Contains ("foo"));
inner = inner.Or (p => p.Description.Contains ("far"));
var outer = PredicateBuilder.True<Product>();
outer = outer.And (p => p.Price > 99);
outer = outer.And (p => p.Price < 999);
outer = outer.And (inner);
And use it like :
var pr = db.Products.Where(outer).ToList();
The Predicate Builder Source and examples are available at http://www.albahari.com/nutshell/predicatebuilder.aspx

Categories