I am trying to create a where clause for my view using LINQ.
I was able to create single column where clause and I would like now to create multiple column where clauses..
I have seen code to implement in .Net 4 and above, but since I have to use .Net 3.5, I need a quick work around for this. so what I am trying to do is....
Expression leftexp = {tag=>((tag.id=2)||(tag.id=3))}
Expression rightexp = {tag=>((tag.uid="MU")||(tag.uid="ST"))}
from these two expressions i would like to create
BinaryExpression be = {tag=>((tag.id=2)||(tag.id=3))} &&
{tag=>((tag.uid="MU")||(tag.uid="ST"))}
something like this which i could pass to my where clause in LINQ.
I tried to use Expression.And(leftexp,rightexp)
but got the error..
The binary operator And is not defined for the types
'System.Func2[WebApplication1.View_MyView,System.Boolean]' and
'System.Func2[WebApplication1.View_MyView,System.Boolean]'.
Expression is new for me and might have looked at too much of code so a bit confused to how to go about doing this... would really appreciate if you could point me in the right direction.
Rewriting expressions has been made easy with the addition of ExpressionVisitor to BCL. With some helpers the task gets almost trivial.
Here's a visitor class I use to apply a delegate to the tree nodes:
internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {
private readonly Func<Expression , Expression> m_Visitor;
private readonly bool m_Recursive;
public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
}
private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
m_Visitor = visitor;
m_Recursive = recursive;
}
public override Expression Visit ( Expression node ) {
if ( m_Recursive ) {
return base.Visit ( m_Visitor ( node ) );
}
else {
var visited = m_Visitor ( node );
if ( visited == node ) return base.Visit ( visited );
return visited;
}
}
}
And here are the helper methods to simplify the rewriting:
public static class SystemLinqExpressionsExpressionExtensions {
public static Expression Visit ( this Expression self , Func<Expression , Expression> visitor , bool recursive = false ) {
return ExpressionDelegateVisitor.Visit ( self , visitor , recursive );
}
public static Expression Replace ( this Expression self , Expression source , Expression target ) {
return self.Visit ( x => x == source ? target : x );
}
public static Expression<Func<T , bool>> CombineAnd<T> ( this Expression<Func<T , bool>> self , Expression<Func<T , bool>> other ) {
var parameter = Expression.Parameter ( typeof ( T ) , "a" );
return Expression.Lambda<Func<T , bool>> (
Expression.AndAlso (
self.Body.Replace ( self.Parameters[0] , parameter ) ,
other.Body.Replace ( other.Parameters[0] , parameter )
) ,
parameter
);
}
}
Which allows to combine the expressions like this:
static void Main () {
Expression<Func<int , bool>> leftExp = a => a > 3;
Expression<Func<int , bool>> rightExp = a => a < 7;
var andExp = leftExp.CombineAnd ( rightExp );
}
UPDATE:
In case ExpressionVisitor's not available, its source had been published a while ago here. Our library used that implementation until we've migrated to .NET 4.
You cannot do that without rewriting both complete expression trees into a complete new one.
Reason: the parameter-expression objects must be the same for the whole expression tree. If you combine the two, you have two parameter-expression objects for the same parameter, which will not work.
It shows with the following code:
Expression<Func<Tab, bool>> leftexp = tag => ((tag.id == 2) || (tag.id == 3));
Expression<Func<Tab, bool>> rightexp = tag => ((tag.uid == "MU") || (tag.uid == "ST"));
Expression binaryexp = Expression.AndAlso(leftexp.Body, rightexp.Body);
ParameterExpression[] parameters = new ParameterExpression[1] {
Expression.Parameter(typeof(Tab), leftexp.Parameters.First().Name)
};
Expression<Func<Tab, bool>> lambdaExp = Expression.Lambda<Func<Tab, bool>>(binaryexp, parameters);
var lambda = lambdaExp.Compile();
This fails on the lambdaExp.Compile() call, which gives the following exception:
Lambda Parameter not in scope
This is caused by the fact that basically I'm re-using the leftexp and rightexp expression, but they have different parameter-expressions, both which are not given by me to the Expression.Lambda<Func<Tab>>(...) call. Deep down into the leftexp and rightexp there are parameter-expression objects which must match the one given to the Expression.Lambda<Func<Tab>>(...) call.
To solve this you have recreate the complete expression using a new (single) parameter-expression for parameter tag.
See here for more information about the problem.
Related
I have a list of conditions composed of two Funcs:
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
For each condition, expression would look like this:
Expression<Func<TNumbering, TConfiguration, bool>> (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
I need to chain the list of these expressions with OrElse.
I tried doing something like:
BinaryExpression expression = null;
foreach (var criteria in SelectionCriteria)
{
Expression<Func<TNumbering, TConfiguration, bool>> exp = (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n);
expression = expression == null ? exp : Expression.OrElse(expression, exp);
}
if (expression == null) return Result.Failure("Expression not defined"));
var lambda = Expression.Lambda<Func<TConfiguration, bool>>(expression);
numberingsToRemove = numberings.Where(_ => configurations.All(lambda));
However, compiler doesn't like it, says there is no implicit conversion between Expression.Lambda<Func<TConfiguration, bool>> and Binary expression.
If I use
expression = expression == null ? Expression.OrElse(exp, exp) : Expression.OrElse(expression, exp);
I get
The binary operator OrElse is not defined for the types 'System.Func<TNumbering,TConfiguration,System.Boolean> and 'System.Func<TNumbering,TConfiguration,System.Boolean>.
I am new to building expressions, can somebody point me in the right direction how to do this?
Your Expression<Func<TNumbering, TConfiguration, bool>> is a generic type whose open generic type is Expression<TDelegate>, where TDelegate is some delegate type; in this case Func<TNumbering, TConfiguration, bool>.
Expression<TDelegate> inherits from LambdaExpression, which represents a C# (or VB.NET) lambda expression.
Just like you couldn't write the following code:
var result =
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
(n1, c1) => criteria.ConfigurationField(c1) != criteria.NumberingField(n1);
trying to combine two LambdaExpressions with OrElse would throw an exception at runtime.
Your code isn't even compiling, because expression is typed as BinaryExpression, meaning an expression corresponding to this:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
into which you're trying to put a full Expression<TDelegate>, which includes (for example) the parameter list.
Every LambdaExpression has a Body property, which extracts from an expression corresponding to this:
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
the body of the LambdaExpression, or an expression corresponding to this:
criteria.ConfigurationField(c) != criteria.NumberingField(n)
which, in theory, you could then combine into a BinaryExpression corresponding to this:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
But this wouldn't work either, because each iteration introduces multiple new parameters, all of which you'd have to pass in to the final lambda.
It's possible to work around this problem, but I would suggest first and foremost you map each element in SelectionCriteria to an expression corresponding to the criteria evaluation, using the factory methods at System.Linq.Expressions.Expression. You could then combine those expressions into a BinaryExpression which you could then wrap up in a LambdaExpression or even an Expression.
It might look something like this (making some assumptions):
class Criteria<TConfiguration, TNumbering> {
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
}
// using static System.Linq.Expressions.Expression;
var SelectionCritera = new List<Criteria>();
/*
* populate list here
*/
var configParam = Parameter(typeof(TConfiguration));
var numberingParam = Parameter(typeof(TNumbering));
var expressions =
SelectionCriteria.Select(criteria => {
var criteriaExpr = Constant(criteria);
return NotEqual( // !=
Invoke( // ( ... )
PropertyOrField( // .ConfigurationField
criteriaExpr, // criteria
"ConfigurationField"
),
configParam // c
),
Invoke( // ( ... )
PropertyOrField( // .NumberingField
criteriaExpr, // criteria
"NumberingField"
),
numberingParam // n
)
);
})
.ToList();
if (!expressions.Any) { return Result.Failure("Expression not defined")); }
// Combine all the subexpressions using ||
var body = expressions.Aggregate((prev, next) => OrElse(prev, next));
// Create a LambdaExpression
var lmbd = Lambda<Func<TConfiguration, TNumbering, bool>>(body, configParam, numberingParam);
// Create a .NET method from the LambdaExpression
var mthd = lmbd.Compile();
// Apply the method to each config/numbering pair
var result = (
from config in configs
from numbering in numbering
select (config, numbering)
).All(x => mthd(config, numbering));
I am able to deconstruct a container via an extension method:
var limits = new[] { MyEnum.A , MyEnum.B , MyEnum.C }
.ToDictionary( x => x ,
x => ( SupportedLimit: GetLimit( x ) , RequiredLimit: 0 ) );
public static class Extensions
{
public static void Deconstruct<TKey, TVal>(
this KeyValuePair<TKey , TVal> tuple ,
out TKey key , out TVal value )
{
key = tuple.Key;
value = tuple.Value;
}
public static void Deconstruct<TKey, TVal1, TVal2>(
this KeyValuePair<TKey , (TVal1,TVal2)> tuple ,
out TKey key , out TVal1 v1 , out TVal2 v2 )
{
key = tuple.Key;
(v1 , v2) = tuple.Value;
}
}
// works
foreach( var ( enumVal , supportedLimit , requiredLimit ) in limits )
Debugger.Break();
How do I deconstruct a container/dictionary containing a System.ValueTuple in LINQ?
// won't work, .Where() expects Func<T,bool> not Func<T1,T2,T3,bool>
var failedLimits = limits.Where( ( _ , sup , req ) => sup < req );
I just wanted to know how (and if) it is possible to deconstruct the ValueTuple in (any) LINQ method. I guess I have to add an extension method for every Linq-method (List-like) + overloads for dictionaries + each amount of values in the ValueTuple.
How would it look like for the Where() in the example?
public static class LinqExtensions
{
public static IEnumerable<KeyValuePair<TKey,(T1,T2)>> Where<TKey,T1,T2>(
this IEnumerable<KeyValuePair<TKey,(T1,T2)>> source ,
Func<TKey,T1,T2, Boolean> predicate )
=> source.Where( predicate );
}
Overloads for List-like types + every amount of ValueTuple-parameter
Since you're dealing with a Dictionary the values you iterate over are KeyValuePairs. You need to deal with the Value part of the KeyValuePair and then just use the named property of your value tuple.
var failedLimits = limits.Where(kvp => kvp.Value.SupportedLimit < req);
I've created a generic to target tables in my database. I've also added a generic .where() with an expression containing only 1 parameter in the lambda expression. Is there a way that I can add more than 1 parameter in the same expression? Please ignore the fact that the below method returns 1 item, I would like to make it return a IList.
Here's my method:
public virtual T GetById( Int32 id, String someStringColumn )
{
ParameterExpression itemParameter = Expression.Parameter( typeof( T ), "item" );
var whereExpression = Expression.Lambda<Func<T, Boolean>>
(
Expression.Equal( Expression.Property( itemParameter, "Id" ), Expression.Constant( id ) ),
new[] { itemParameter }
);
return context.Set<T>().Where( whereExpression ).FirstOrDefault();
}
My actual intentions for this method is to later perform a Contains() on "string" properties of the target table "T". I would like to do something like below and append to the above Expression a check if the String property contains a value in the "someStringColumn". Just an insight, the "someStringColumn" will be a general search string on my page past by Ajax on every back-end call.
var properties = item.GetType().GetProperties().Where( p => p.PropertyType == typeof( string ) ).ToArray();
for ( Int32 i = 0; i < properties.Length; i++ )
{
}
I'm trying to achieve something like this in a non-generic method:
public override List<TableInDatabase> List( PagingModel pm, CustomSearchModel csm )
{
String[] qs = ( pm.Query ?? "" ).Split( ' ' );
return context.TableInDatabase
.Where( t => ( qs.Any( q => q != "" ) ? qs.Contains( t.ColumnName) : true ) )
.OrderBy( String.Format( "{0} {1}", pm.SortBy, pm.Sort ) )
.Skip( pm.Skip )
.Take( pm.Take )
.ToList();
}
If I understand correctly, you are seeking for something like this:
var item = Expression.Parameter(typeof(T), "item");
Expression body = Expression.Equal(Expression.Property(item, "Id"), Expression.Constant(id));
if (!string.IsNullOrEmpty(someStringColumn))
{
var properties = typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)).ToList();
if (properties.Any())
body = Expression.AndAlso(body,
properties.Select(p => (Expression)Expression.Call(
Expression.Property(item, p), "Contains", Type.EmptyTypes, Expression.Constant(someStringColumn))
).Aggregate(Expression.OrElse));
}
var whereExpression = Expression.Lambda<Func<T, bool>>(body, item);
i.e. build Contains expression for each string property, combine them using Or and finally combine the result with the first condition using And.
I want to create lambda something like this
user => user.Address == address
but is not compiled one, I want to return LambdaExpression
If the lambda take constant like this
user => user.Age == 50
Then I can use this method
public static LambdaExpression PropertyEqual(Type tEntityType, string propertyName, object value)
{
// entity => entity.PropName == const
var itemParameter = Expression.Parameter(tEntityType, "entity");
return Expression.Lambda
(
Expression.Equal
(
Expression.Property
(
itemParameter,
propertyName
),
Expression.Constant(value) // Tried to replace this with Expression.Parameter or Expression.Variable but no luck
),
new[] { itemParameter }
);
}
How to make this method accept variable address come from the scope just outside from the lambda expression?
var addressPropertyName = "Address";
var address = new Address() {...};
var q = Repo.GetQuery().Where(PropertyEqual(typeof(User), addressPropertyName, address))
Edit: clarify my question: How build the right Expression to generate the first lambda?
Update: This is not possible because EF does not support non-scalar variable
I change the lambda to user => user.AddressId == addressId as suggested here. It just the matter how to get AddressId FK PropertyInfo from a known navigation property Address.
You can't dynamically generate a closure on a variable (you can't extend the lifetime of a variable outside its context) because this is a trick of the compiler (that rewrites your code to do it).
If you don't want a closure but you want an additional parameter then you can add an additional parameter to the expression.
You could
Expression<Func<string>> myExpr = () => address;
now you have an expression that closes around your address. Now you only have to combine the two expressions.
You'll have to change the method to:
public static LambdaExpression PropertyEqual<T>(Type tEntityType, string propertyName, Expression<Func<T>> getValue)
{
// entity => entity.PropName == const
var itemParameter = Expression.Parameter(tEntityType, "entity");
return Expression.Lambda
(
Expression.Equal
(
Expression.Property
(
itemParameter,
propertyName
),
Expression.Invoke(getValue) // You could directly use getValue.Body instead of Expression.Invoke(getValue)
),
new[] { itemParameter }
);
}
EDIT: Let's try this again. This time I've used the AdventureWorks sample database so you can all play along. This will rule out anything crazy I've done in my own database. Here's a new example demonstrating what works and what I would expect to work (but doesn't). Can anyone explain why it doesn't work or suggest a different way of achieving my goal (refactoring out the common expression so it can be reused elsewhere)?
using (AdventureWorksDataContext db = new AdventureWorksDataContext())
{
// For simplicity's sake we'll just grab the first result.
// The result should have the name of the SubCategory and an array of Products with ListPrice greater than zero.
var result = db.ProductSubcategories.Select(subCategory => new
{
Name = subCategory.Name,
ProductArray = subCategory.Products.Where(product => product.ListPrice > 0).ToArray()
}).First();
Console.WriteLine("There are {0} products in SubCategory {1} with ListPrice > 0.", result.ProductArray.Length, result.Name);
// Output should say: There are 3 products in SubCategory Bib-Shorts with ListPrice > 0.
// This won't work. I want to pull the expression out so that I can reuse it in several other places.
Expression<Func<Product, bool>> expression = product => product.ListPrice > 0;
result = db.ProductSubcategories.Select(subCategory => new
{
Name = subCategory.Name,
ProductArray = subCategory.Products.Where(expression).ToArray() // This won't compile because Products is an EntitySet<Product> and that doesn't have an overload of Where that accepts an Expression.
}).First();
Console.WriteLine("There are {0} products in SubCategory {1} with ListPrice > 0.", result.ProductArray.Length, result.Name);
}
</Edit>
The following LINQ to SQL works fine:
var result = from subAccount in db.SubAccounts
select new ServiceTicket
{
MaintenancePlans = subAccount.Maintenances.Where(plan => plan.CancelDate == null && plan.UpgradeDate == null).Select(plan => plan.ToString()).ToArray()
// Set other properties...
};
However, I want to break out the predicate passed to the Where since it's used throughout the code. But if I try and pass a defined predicate into the Where it fails, such as:
Func<DatabaseAccess.Maintenance, bool> activePlanPredicate = plan => plan.CancelDate == null && plan.UpgradeDate == null;
var result = from subAccount in db.SubAccounts
select new ServiceTicket
{
MaintenancePlans = subAccount.Maintenances.Where(activePlanPredicate).Select(plan => plan.ToString()).ToArray()
// Set other properties...
};
This makes no sense to me. Can anyone explain what's going on? Maintenances is of type EntitySet<DatabaseAccess.Maintenance>. The error I get is:
System.NotSupportedException:
Unsupported overload used for query
operator 'Where'..
EDIT: For those interested, here's what Reflector has for the first (working) example with Optimization set to .NET 2.0:
using (BugsDatabaseDataContext db = new BugsDatabaseDataContext())
{
ParameterExpression CS$0$0001;
ParameterExpression CS$0$0006;
ParameterExpression CS$0$0010;
return db.SubAccounts.Select<SubAccount, ServiceTicket>(Expression.Lambda<Func<SubAccount, ServiceTicket>>(
Expression.MemberInit(
Expression.New(
(ConstructorInfo) methodof(ServiceTicket..ctor),
new Expression[0]),
new MemberBinding[]
{
Expression.Bind(
(MethodInfo) methodof(ServiceTicket.set_MaintenancePlans),
Expression.Call(
null,
(MethodInfo) methodof(Enumerable.ToArray),
new Expression[]
{
Expression.Call(
null,
(MethodInfo) methodof(Enumerable.Select),
new Expression[]
{
Expression.Call(
null,
(MethodInfo) methodof(Enumerable.Where),
new Expression[]
{
Expression.Property(CS$0$0001 = Expression.Parameter(typeof(SubAccount), "subAccount"), (MethodInfo) methodof(SubAccount.get_Maintenances)),
Expression.Lambda<Func<Maintenance, bool>>(
Expression.AndAlso(
Expression.Equal(
Expression.Property(CS$0$0006 = Expression.Parameter(typeof(Maintenance), "plan"), (MethodInfo) methodof(Maintenance.get_CancelDate)),
Expression.Convert(Expression.Constant(null, typeof(DateTime?)), typeof(DateTime?)), false, (MethodInfo) methodof(DateTime.op_Equality)
),
Expression.Equal(
Expression.Property(CS$0$0006, (MethodInfo) methodof(Maintenance.get_UpgradeDate)),
Expression.Convert(Expression.Constant(null, typeof(DateTime?)), typeof(DateTime?)), false, (MethodInfo) methodof(DateTime.op_Equality)
)
),
new ParameterExpression[] { CS$0$0006 }
)
}
),
Expression.Lambda<Func<Maintenance, string>>(
Expression.Call(
CS$0$0010 = Expression.Parameter(typeof(Maintenance), "plan"),
(MethodInfo) methodof(object.ToString),
new Expression[0]
),
new ParameterExpression[] { CS$0$0010 }
)
}
)
}
)
)
}
),
new ParameterExpression[] { CS$0$0001 }
)
).ToList<ServiceTicket>();
}
EDIT: The Reflector output for the second example (using a predicate) is mostly similar. The biggest difference being that, in the call to Enumerable.Where, rather than passing an Expression.Lambda it passes Expression.Constant(activePlanPredicate).
I don't fully understand the guts of Linq to Entities, but there is an Open Source (usable in proprietary software) toolkit specifically designed to help solve this problem, called LinqKit, linked off this O'Reilly-related article:
http://www.albahari.com/nutshell/predicatebuilder.aspx
Since I don't fully understand the guts, I'll just quote them:
Entity Framework's query processing pipeline cannot handle invocation expressions, which is why you need to call AsExpandable on the first object in the query. By calling AsExpandable, you activate LINQKit's expression visitor class which substitutes invocation expressions with simpler constructs that Entity Framework can understand.
Here is a direct link to LinqKit.
And here is the type of code that this project enables:
using LinqKit;
// ...
Expression<Func<Product, bool>> expression = product => product.ListPrice > 0;
var result = db.ProductSubcategories
.AsExpandable() // This is the magic that makes it all work
.Select(
subCategory => new
{
Name = subCategory.Name,
ProductArray = subCategory.Products
// Products isn't IQueryable, so we must call expression.Compile
.Where(expression.Compile())
})
.First();
Console.WriteLine("There are {0} products in SubCategory {1} with ListPrice > 0."
, result.ProductArray.Count()
, result.Name
);
The result is:
There are 3 products in SubCategory Bib-Shorts with ListPrice > 0.
Yay, no exception, and we can extract the predicate!
I'd refactor the original like this
private bool IsYourPredicateSatisfied(Maintenance plan)
{
return plan.CancelDate == null && plan.UpgradeDate == null;
}
Then your Where clause is Where(m => IsYourPredicateSatisfied(m))
Try this:
Expression<Func<DatabaseAccess.Maintenance, bool>> activePlanPredicate = plan => plan.CancelDate == null && plan.UpgradeDate == null;
var result = from subAccount in db.SubAccounts
select new ServiceTicket
{
MaintenancePlans = subAccount.Maintenances.Where(activePlanPredicate).Select(plan => plan.ToString()).ToArray()
// Set other properties...
};
I don't have VisualStudio in front of me, so that may require some tweaking. The issue you're running into is that you want to access the IQueryable extension of Where, but just having a Func<T,bool> gives you the IEnumerable extension.