I'm using Entity Framework 6 and Waseem Sabjee's solution for appending expressions at runtime which works really well.
However, what I can't work out is how to separate parts of the expression with what in SQL would be parentheses.
For example:
SELECT * FROM SOMETABLE WHERE Field1 = 'somevalue' AND (FIELD2 = 1 OR FIELD2 = 2 OR FIELD2 = 3)
instead of
SELECT * FROM SOMETABLE WHERE Field1 = 'somevalue' AND FIELD2 = 1 OR FIELD2 = 2 OR FIELD2 = 3
which will output different results and would be wrong in the second
example for what I actually want.
(although in SQL I would do the "OR" parts with an "IN" clause)
Normally I'd just put parentheses around the relevant part of the filter but in my case at runtime I don't know how many OR statements I need as I pass in a List of values, the number of which is only known at runtime i.e:
public Search(IList<int> vals, string filter){
Expression<Func<Event, bool>> filter = x => x.Field1 == filter;
bool first = true;
for(int i in vals){
if (first){
filter = filter.And(x -> x.Field2 == i);
first = false;
}
else{
filter = filter.Or(x -> x.Field2 == i);
}
}
}
Does anyone know how this can be achieved with expressions?
You should create or statements before and statement:
public Search(IList<int> vals, string filter){
Expression<Func<Event, bool>> andFilter = x => x.Field1 == filter;
var firstVal = vals.First();
Expression<Func<Event, bool>> orFilter = x=>x.Field2 == firstVal;
foreach(int i in vals.Skip(1)){
orFilter = orFilter.Or(x -> x.Field2 == i);
}
andFilter = andFilter.And(orFilter);
}
OK so I had to think a bit laterally to solve this as I could not find a way to do this with an expression builder (there may still be a way!)
Instead, I turned to Roslyn that I found referenced here.
With Rosyln you can essentially compile strings into C# code and return output to be further used at runtime.
So instead of appending expressions I now create the expression as a piece of code in a string, compile it with Rosyln, and return the new expression I used to filter my data.
This is how it works:
ScriptEngine scriptEngine = new ScriptEngine();
string exp = "public System.Linq.Expressions.Expression<System.Func<my.namespace.object,bool>> CreateFilter(){System.Linq.Expressions.Expression<System.Func<my.namespace.object,bool>> filter = e => e.Field1.Contains(\"a\");return filter;}CreateFilter();";
This first part sets up the new method as a string.
Roslyn.Scripting.Session s = scriptEngine.CreateSession();
s.AddReference(typeof(System.Linq.Expressions.Expression).Assembly);
s.AddReference(typeof(my.namespace.object).Assembly);
You need to add references to any non-system assemblies your code requires just like any other project.
Expression<Func<Event, bool>> filter = (Expression<Func<Event, bool>>)s.Execute(exp);
Finally, you just execute the code which returns your new expression.
With this technique you can easily add any parentheses you like, where ever you like, and build up the expression using loops and other conditional statements.
I found Roslyn on NuGet.
Sorry for my late reply (power issues this side).
Here is my solution (code only)
// at this point query will be: SELECT * FROM SOMTABLE WHERE Field1 = 'Cat'
Expression<Func<Event, bool>> filterExpression = x => x.Field1 == filter;
if (vals.Count > 0)
{
// at this point query will be: SELECT * FROM SOMTABLE WHERE Field1 = 'Cat' AND Field2 = 1
filterExpression = filterExpression.And(x => x.Field2 == vals.First());
}
if (vals.Count > 1)
{
// at this point query will be: SELECT * FROM SOMTABLE WHERE Field1 = 'Cat' AND (Field2 = 1 OR Field2 = 3 OR Field2 = 4)
filterExpression = filterExpression.Or(x => vals.Skip(1).Contains(x.Field2));
}
I've posted a link to a vs solution here: http://www.waseem-sabjee.com/code/LinqExpressionFilterIssue.zip
Related
I hava table with multiple vehicle columns
My corresponding SQL Query is
sQuery = "Select * from Vehicle where " + variant + "='Y'";
How can I write the same query in LINQ?
You can build lambda expression dynamically by using System.Linq.Expression namespace and pass it to Where method.
For example:
public IQueryable<Vehicle> GetAccounts(string variant)
{
// Build equivalent lambda to 'param => param.{variant} == "Y"'
// This is lambda parameter
var param = Expression.Parameter(typeof(Vehicle));
// This is lambda body (comparison)
var body = Expression.Equal(
// Access property {variant} of param
Expression.Property(param, typeof(Vehicle).GetProperty(variant)),
// Constant value "Y"
Expression.Constant("Y")
);
// Build lambda using param and body defined above
var lambda = Expression.Lambda<Func<Vehicle, bool>>(body, param);
// Use created lambda in query
return VehicleDatatable.Where(lambda);
}
Another idea is to convert query a bit.
You can realize same work using following query:
sQuery = "Select * from Vehicle where (Tracks+Cars+Utility) LIKE '" + value + "'";
Where value is 'Y__' or 'Y' or '__Y' depending on which vehicle type you want to query. It's definitely not most effective, but that is pretty easy to convert to linq.
Build your query:
var query = Vehiclelist;
if (column == "Trucks")
{
query = query.Where(q => q.Trucks=="Y");
}
else if (column == "Cars")
{
query = query.Where(q => q.Cars=="Y");
}
else if (column == "Utility")
{
query = query.Where(q => q.Utility=="Y");
}
This approach is more maintainable, more testable. You have strong-typed expressions and transparent filters.
using lambda expression :
VehicleDatatable.AsEnumerable().Where(q=>q.Trucks=="Y" || q.Cars=="Y" || q.Utility=="Y");
Another way
(from d in VehicleDatatable.AsEnumerable() where string.compare(d["Trucks"],"Y")==0 select d)
Try this:
Vehiclelist.Where(q => q.Trucks=="Y" || q.Cars=="Y" || q.Utility=="Y");
This may also be another approach:
PropertyInfo pi = typeof(Vehicles).GetProperty(vehVariant);
var services = context.Vehicles.ToList();
services = services.Where(item => pi.GetValue(item).ToString().Trim() == "Y").ToList();
Happy Coding.
My users should be able to configure a filter to get a result from the database.
I decided to use an expression tree, to be flexible with the query. But I have problems with the expression tree for filtering by ReferenceCode.
Entities:
PumpStartUp
Duration (TimeSpan)
DutDbId (Guid/FK)
TimeStamp (DateTime)
DUT (DeviceUnderTest)
DeviceUnderTest
AdminId (string/unique)
DbId (Guid/PK)
ReferenceCode (string)
StartUps (List)
Here is a part of the filter in equivalent linq. But linq I can't use, because I don't know how many ReferenceCodes will be defined by the user.:
//-------------------reference code filter---------------------------
var query = context.PumpStartUps.Where((p => p.DUT.ReferenceCode == "HKR566" ||
p.DUT.ReferenceCode == "HSH967" ||
.
.
.));
startUps = query.ToList();
For filtering by DeviceUnderTest part of the filter, my solution is:
// --------------------duts filter-----------------------------------
Expression dutsExpression = null;
Expression psuExpression = Expression.Parameter(typeof(PumpStartUp), "psu");
Expression psuDutIdExpression = Expression.Property(psuExpression, "DutDbId");
foreach (var dut in filter.Duts)
{
DeviceUnderTest deviceUnderTest = context.DevicesUnderTest.Where(d => d.AdminId == dut.Id).Single();
Expression dutIdExpression = Expression.Constant(deviceUnderTest.DbId);
Expression dutExpression = Expression.Equal(pumpStartUpDutIdExpression, dutIdExpression);
if (dutsExpression == null)
{
dutsExpression = dutExpression;
}
else
{
dutsExpression = Expression.Or(dutsExpression, dutExpression);
}
}
How can I filter by ReferenceCode in that manner:
Use this:
var dutExpression = Expression.Property(psuExpression, "DUT");
var referenceCodeExp = = Expression.Property(dutExpression, "ReferenceCode ");
var constExpr = Expression.Constant("HKR566");
var eqExp = Expression.Equal(referenceCodeExp , constExpr);
dutsExpression = Expression.Or(dutsExpression, eqExp);
If you have a limited amount of codes, you can always say
var query = context.PumpStartUps.Where(p => codes.Contains(p.DUT.ReferenceCode))
This works up until about 2000 parameters. If you need more, then you should probably send the codes somehow to a temp table (or rather a function returning a table, since ef does not support temp tables), and join on that since constructing an expression with more than 2000 ors is not gonna perform well.
I am trying to build a method in my asp.net WebAPI to grab data based on the arguments passed on the method. The method is used to perform a search on restaurant data. I have a variable called 'type' that determines the type of data search performed. The second variable 'keyword' is the keyword searched by the user. The WHERE condition in my LINQ query depends on the type and needs to be dynamic, so I have used a separate variable outside the LINQ query to define the condition. I have tried assigning this variable to my WHERE statement on the LINQ query but it doesn't seem to work. Can someone help with it please? I have been stuck on this for a few days now
public IQueryable<RestaurantView> GetRestaurantsForSearch(string keyword, int type, string location)
{
//
var condition = "";
if(type == 1)
{
condition = "x.RestaurantName.Contains(keyword)";
} else if(type == 2){
condition = "x.Cuisine.Equals(keyword)";
}
else {
condition = "x.Rating.Equals(keyword)";
}
var query = from x in db.Restaurants
join y in db.Cuisine on x.RestaurantCuisine equals y.CuisineID
where condition
select new RestaurantView
{
RestaurantID = x.RestaurantID,
RestaurantName = x.RestaurantName,
RestaurantCuisine = y.CuisineName,
RestaurantDecription = x.RestaurantDecription
};
return query;
}
Try this:
Predicate<Restaurant> pred;
if (type == 1) pred = x => x.RestaurantName.Contains(keyword);
else if (type == 2) pred = x => x.Cuisine.Equals(keyword);
else pred = x => x.Rating.Equals(keyword);
var query = from x in db.Restaurants
join y in db.Cuisine on x.RestaurantCuisine equals y.CuisineID
where pred(x)
select new RestaurantView
{
RestaurantID = x.RestaurantID,
RestaurantName = x.RestaurantName,
RestaurantCuisine = y.CuisineName,
RestaurantDecription = x.RestaurantDecription
};
return query;
You need to look a dynamic linq library i think then you can execute string statements inside your linq
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
or you can execute direct query
http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executequery.aspx
If you are ok with dropping your comprehensive LINQ query in favour of the extension method syntax, it's pretty simple (I'm on a netbook without VS, so I apologize that this is untested but should give you the idea):
var query = db.Restaurants
.Include("Cuisine")
if(type == 1)
{
query= query.Where(x => x.RestaurantName.Contains(keyword));
}
else if(type == 2)
{
query = query.Where(x => x.Cuisine == keyword);
}
else {
query = query.Where(x => x.Rating == keyword);
}
This builds out your expression tree differently based on your logic checks, which will result in a different SQL query being generated based on the value of type.
I notice that in your join, Cuisine appears to be an Entity, but in your logic checks, you attempt to filter by comparing Cuisine to a string so I think there is some disconnect.
var query = from x in db.Restaurants
join y in db.Cuisine on x.RestaurantCuisine equals y.CuisineID
where condition
select new RestaurantView
{
RestaurantID = x.RestaurantID,
RestaurantName = x.RestaurantName,
RestaurantCuisine = y.CuisineName,
RestaurantDecription = x.RestaurantDecription
};
return query;
}
how to get the return query value in client side to assign for grid view binding
So, I'm getting the error that's in the title. I'll jump straight into the relevant code. First, the manual creation of the where statement (which does work) and then the dynamic creation which does not work. Both create the same queryExpression.
First, the queryExpression that is generated with both methods:
{Table(Products).Select
(s => new SalesData() {
ProductID = s.ProductID,
StoreName = s.StoreName,
Count = s.Count
}).Where(s => (s.Count > 500))}
Now, the manual method that does work:
IQueryable<SalesData> data = ( from s in sales.Products
select new SalesData
{
ProductID = s.ProductID,
StoreName = s.StoreName,
Count = s.Count
} ).AsQueryable<SalesData>();
data = data.Where(s => s.Count > 500);
this.dataGridView1.DataSource = null;
this.dataGridView1.DataSource = (from d in data
select new
{
d.ProductID,
d.StoreName,
d.Count
} );
This works as expected; my DataGridView is populated with the data.
Now, to create the where clause dynamically:
IQueryable<SalesData> data = ( from s in sales.Products
select new SalesData
{
ProductID = s.ProductID,
StoreName = s.StoreName,
Count = s.Count
} ).AsQueryable<SalesData>();
if (this.filter.Predicate != null)
{
Expression<Func<SalesData, bool>> lambda =
Expression.Lambda<Func<SalesData, bool>>(this.filter.Predicate,
new ParameterExpression[] { this.filter.PE });
data = data.Where(lambda);
}
this.dataGridView1.DataSource = null;
this.dataGridView1.DataSource = (from d in data <---- fails here
select new
{
d.ProductID,
d.StoreName,
d.Count
} );
The only thing that is different is that in the second snippet I'm creating the lambda expression dynamically. I've noted where it fails in the second snippet.
Using the debugger I can see the queryExpression for data and it is the same with both methods, so I don't think my problem is with my actual expression creation. If that code is needed I can post it here.
My question is, what am I doing wrong and how do I fix it?
Edit:
Here is the function that gives filter.Predicate it's value:
public static Expression GetPredicate(List<FilterItem> itemList, ParameterExpression pe)
{
List<Expression> expressions = new List<Expression>();
List<string> combiners = new List<string>();
foreach (FilterItem item in itemList)
{
Expression left = Expression.PropertyOrField(pe, item.Field);
Expression right = Expression.Constant(Convert.ToInt32(item.Value), typeof(int));
expressions.Add(Expression.GreaterThan(left, right));
combiners.Add(item.Combiner);
}
int expressionCount = expressions.Count();
Expression predicateBody = expressions[0];
if (expressionCount > 1)
{
for (int x = 1; x <= expressionCount; x++)
{
switch (combiners[x - 1])
{
case "AND":
predicateBody = Expression.And(predicateBody, expressions[x]);
break;
case "OR":
predicateBody = Expression.Or(predicateBody, expressions[x]);
break;
default:
break;
}
}
}
return predicateBody;
}
Inside filter I have:
this.Predicate = BuildPredicate.GetPredicate(this.filterList, this.PE);
this.PE is:
public ParameterExpression PE
{
get
{
return Expression.Parameter(typeof(SalesData), "s");
}
}
From the father of C#, Anders Hejlsberg:
Parameters are referenced in expressions through their object identity, not by comparing their names. In fact, from an expression tree's point of view, the name of a parameter is purely informational. The reason for this design is the same reason that types are referenced through their System.Type objects and not their names--expression trees are fully bound and are not in the business of implementing name lookup rules (which may differ from language to language).
From MSDN Forums
The short and long of it is that filter.Predicate has a value of {(s.Count > 500)} but this doesn't mean that s has meaning here by sharing a name with the initial LINQ expression.
I was able to solve my problem thanks to the dialog on this page and using Predicate Builder.
A better method for what I'm trying to do can be found at Scott Gu's site. This is better for my purposes because I need to determine the field and the value dynamically, plus allow for grouping (easier with this method).
This is the pseudo-SQL I want to generate:
SELECT * FROM Table WHERE Column1 = #Value1 OR Column2 = #Value2
The problem is, sometimes the second one should not be included. I was hoping to chain together .Where() clauses like so:
var query = context.TableName;
query = query.Where(t => t.Column1 == value1);
if (NeedsToBeIncluded(value2))
query = query.Where(t => t.Column2 == value2);
Unfortunately, this doesn't work. .Where() will emit an AND if you chain them together by default. Is there a way to get it to emit an OR?
I'm looking for something along the lines of:
var query = context.TableName;
query = query.Where(t => t.Column1 == value1);
if (NeedsToBeIncluded(value2))
query = query.OrWhere(t => t.Column2 == value2);
UPDATE
Ok, so my example listed above is too simple. It was merely supposed to be an example that outlines the problem space. Out "in the wild" so to speak, Column1 and Column2 could actually be "CarType" and "OwnerName", maybe there's more, maybe there's less. I just used a simple example to outline the problem because I'm looking to solve a range of domain problems with this chaining-.Where()s together.
One way is to use LINQKit's PredicateBuilder.
Another way is to use a list:
var values = new List<string> { value1 };
if (NeedsToBeIncluded(value2)) values.Add(value2);
query = context.TableName.Where(t => values.Contains(t));
PB is more flexible, but the list will solve the problem in your question. Note that you need EF 4 for Contains.
I gave an example how to dynamically build a condition yesterday - see here. For your case it would be something like that.
var parameter = Expression.Parameter(typeof(TableName), "t");
Expression condition = Expression.Equal(
Expression.Property(parameter, "Column1"),
Expression.Constant(value1)));
if (NeedsToBeIncluded(value2))
{
condition = Expression.OrElse(
condition,
Expression.Equal(
Expression.Property(parameter, "Column2"),
Expression.Constant(value2)));
}
var expression = Expression.Lambda<Func<TableName, Boolean>>(condition, parameter);
var query = context.TableName.Where(expression);