I was looking at a simple rule engine http://netmatze.wordpress.com/2012/01/22/building-a-rule-engine-in-c/ and I'm doing something very similar to this. I have two classes that look like:
class A
{
public List<B> ListB { get; set; }
}
Class B
{
public int ID { get; set; }
}
With my rule set looking like:
List<Rule> rules = new List<Rule>{
new Rule("listB", ExpressionType.Loop, 1, "ID")
};
I'm trying to build the expression to basically look at class A property listB, loop it camparing each item's ID property to see if at least one equals 1. I'm having trouble on how to do this. I currently have something like (I have hard coded values set in this, but it will eventually be changed to be generic as much as possible). This expression does not work, I get compile exceptions:
var parameterExpression = Expression.Parameter(typeof(A));
var listB = MemberExpression.Property(parameterExpression, "ListB");
var leftOperand = MemberExpression.Property(Expression.Parameter(typeof(B)), "ID");
var rightOperand = Expression.Constant(1); //1
var found = Expression.Variable(typeof(bool), "found");
return Expression.Lambda<Func<T, bool>>(
Expression.Block(
listB,
found,
Expression.Loop(
Expression.Block(
Expression.IfThen(
Expression.Equal(
leftOperand,
rightOperand
),//equal
Expression.Assign(
found,
Expression.Constant(true)
)//set to true
)
)//block
)//loop
),
A
).Compile();
I'll end up calling the rule set against my object like so:
Engine ruleEngine = new Engine();
var compiledRules = rules.Select(r => ruleEngine.CompileRule<A>(r)).ToList();
var result = compiledRules.All(rule => rule(objA));
My questions are:
How do I get this function to return true/false if any of the list
items matched the condition.
How do you prevent the Expression.Loop
to stop looping once all list items are compared (and none of them
matched)?
Thanks for any help.
Why use a loop? You wouldn't use a loop if you were coding the check in C#. You'd use Enumerable.Any. So generate the following expression:
A a;
return a.ListB.Any(b => b.ID == 1);
This is translated to:
A a;
return Enumerable.Any(a.ListB, b => b.ID == 1);
This is easily translatable to expression trees.
Following your last comment it sounds like you could use the approach i suggested for another question. Replace this part:
var childProperty = parameter.Type.GetProperty(properties[0]);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(test, typeof(int));
navigationPropertyPredicate = Expression.Equal(left, right);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
with something using your ruleOperator and value
Related
We have a List<SomeType> and want to be able to run different actions on it like selecting elements meeting some criteria. We want these tests to be provided by the user in text, so are looking at C# Expression Evaluator.
It can certainly be used for this purpose but I'm struggling to tell if a csharpeval delegate can be used as a lambda when using LINQ. We want our stored expressions to be applied to each object.
e.g. Just "o.FirstName == "Tom" and not store the whole list expression like list.Where(o => o.FirstName == 'Tom').
Methods to compile an expression once and run against different input data are provided through ScopeCompile but I'm not sure if these are compatible with lambda methods. Can anyone help me out?
I this is what you want:
var expression = new CompiledExpression<bool>("MyProperty > 10");
var func = expression.ScopeCompile<MyClass>();
Example usage:
class MyClass
{
public int MyProperty { get; set; }
}
var list = Enumerable.Range(1, 50).Select(x => new MyClass { MyProperty = x });
var expression = new CompiledExpression<bool>("MyProperty > 10");
var func = expression.ScopeCompile<MyClass>();
var filtered = list.Where(p => func(p));
Console.WriteLine(filtered.Count()); //40
You can create a helper function to create the lambda functions:
Func<T, bool> GetLambda<T>(string inputExp)
{
var expression = new CompiledExpression<bool>(inputExp);
return expression.ScopeCompile<T>();
}
Usage:
var func = GetLambda<People>("Id < 5");
Peoples.Where(p => func(p)).Dump();
I'm building a filtering system for UserProfiles based on known properties but unknown (until runtime) combination of filtering conditions.
In my previous question How do I create a generic Expression that has an expression as a parameter, I've figured out a way to have a FilterDefinition for any value property reachable from User entity via navigation properties (i.e. (User)u=> u.NavigationProperty.AnotherNavigationProperty.SomeValue)
and I have a method that can return a predicate as Expression<Func<User,bool>> for a given property, operation ( > < == etc ) and a value.
Now the time has come to filter them on collection properties as well.
Say for example User has CheckedOutBooks collection (which is a total fiction, but will do)
And I need to create a filter definition for Name property of CheckedOutBooks collection on User object.
What I have:
A collection of Users
User class has a collection of Books
now I would like to create a method
Expression<Func<User,bool>> GetPredicate(Expression<User,TProperty>, Operations operation, TProperty value)
That I can call like GetPredicate(u=>u.Books.Select(b=>b.Name), Operations.Contains, "C# in a nutshell")
and get an expression back similar to
u=>u.Books.Any(b=>b.Name == "C# in a nutshell")
I'm thinking maybe it will be easier to split first parameter in two to achieve this.
Maybe u=>u.Books and b=>b.Name will do better?
EDIT:
what I got so far:
class FilterDefinitionForCollectionPropertyValues<T>:FilterDefinition, IUserFilter
{
public Expression<Func<UserProfile, IEnumerable<T>>> CollectionSelector { get; set; }
public Expression<Func<T, string>> CollectionPropertySelector { get; set; }
public Expression<Func<Profile.UserProfile, bool>> GetFilterPredicateFor(FilterOperations operation, string value)
{
var propertyParameter = CollectionPropertySelector.Parameters[0];
var collectionParameter = CollectionSelector.Parameters[0];
// building predicate to supply to Enumerable.Any() method
var left = CollectionPropertySelector.Body;
var right = Expression.Constant(value);
var innerLambda = Expression.Equal(left, right);
Expression<Func<T, bool>> innerFunction = Expression.Lambda<Func<T, bool>>(innerLambda, propertyParameter);
var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(T));
var outerLambda = Expression.Call(method, Expression.Property(collectionParameter, typeof(UserProfile).GetProperty("StaticSegments")), innerFunction);
throw new NotImplementedException();
}
}
Now this one works awesomely and does exactly what's needed, now the only thing I need to figure out is how to replace typeof(UserProfile).GetProperty("StaticSegments")) somehow to use CollectionPropertySelector that is in current example would be (UserProfile)u=>u.StaticSegments
You're almost done. Now you just need to do a little trick - wrap your CollectionPropertySelector lambda expression in the CollectionSelector lambda expression.
Expression<Func<TParent,bool>> Wrap<TParent,TElement>(Expression<Func<TParent, IEnumerable<TElement>>> collection, Expression<Func<TElement, bool>> isOne, Expression<Func<IEnumerable<TElement>, Func<TElement, bool>, bool>> isAny)
{
var parent = Expression.Parameter(typeof(TParent), "parent");
return
(Expression<Func<TParent, bool>>)Expression.Lambda
(
Expression.Invoke
(
isAny,
Expression.Invoke
(
collection,
parent
),
isOne
),
parent
);
}
You may have to change this a bit to be used for your particular scenario, but the idea should be clear. My test looked basically like this:
var user = new User { Books = new List<string> { "Book 1", "Book 2" }};
var query = Wrap<User, string>(u => u.Books, b => b.Contains("Bookx"), (collection, condition) => collection.Any(condition));
So you specify the collection selector, predicate and predicate operator, and you're done.
I've written it as a generic method for clarity, but it's dynamic, not strongly typed in essence, so it should be pretty easy to change it to non-generic, if you need that.
Ok I've got it solved for myself.
And I published it to gitHub:
https://github.com/Alexander-Taran/Lambda-Magic-Filters
Given the filter definition class (not reafactored to support properties other than string so far, but will do later):
class FilterDefinitionForCollectionPropertyValues<T>:FilterDefinition, IUserFilter
{
//This guy just points to a collection property
public Expression<Func<UserProfile, IEnumerable<T>>> CollectionSelector { get; set; }
// This one points to a property of a member of that collection.
public Expression<Func<T, string>> CollectionPropertySelector { get; set; }
//This one does the heavy work of building a predicate based on a collection,
//it's member property, operation type and a valaue
public System.Linq.Expressions.Expression<Func<Profile.UserProfile, bool>> GetFilterPredicateFor(FilterOperations operation, string value)
{
var getExpressionBody = CollectionPropertySelector.Body as MemberExpression;
if (getExpressionBody == null)
{
throw new Exception("getExpressionBody is not MemberExpression: " + CollectionPropertySelector.Body);
}
var propertyParameter = CollectionPropertySelector.Parameters[0];
var collectionParameter = CollectionSelector.Parameters[0];
var left = CollectionPropertySelector.Body;
var right = Expression.Constant(value);
// this is so far hardcoded, but might be changed later based on operation type
// as well as a "method" below
var innerLambda = Expression.Equal(left, right);
Expression<Func<T, bool>> innerFunction = Expression.Lambda<Func<T, bool>>(innerLambda, propertyParameter);
// this is hadrcoded again, but maybe changed later when other type of operation will be needed
var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(T));
var outerLambda = Expression.Call(method, Expression.Property(collectionParameter, (CollectionSelector.Body as MemberExpression).Member as System.Reflection.PropertyInfo), innerFunction);
var result = Expression.Lambda<Func<UserProfile, bool>>(outerLambda, collectionParameter);
return result;
}
}
I want to accept a string array of where conditions from the client like field == value.
It would be really nice to create a specification object that could accept the string in the constructor and output a lambda expression to represent the Where clause. For example, I could do the following:
var myCondition = new Specification<Product>( myStringArrayOfConditions);
var myProducts = DB.Products.Where( myCondition);
How could you turn "name == Jujyfruits" into DB.Products.Where(p => p.name == "JujyFruits")?
You can use
Reflection to get the Property Product.name from the string name and
the LINQ Expression class to manually create a lambda expression.
Note that the following code example will only work for Equals (==) operations. However, it is easy to generalize to other operations as well (split on whitespace, parse the operator and choose the appropriate Expression instead of Expression.Equal).
var condition = "name == Jujyfruits";
// Parse the condition
var c = condition.Split(new string[] { "==" }, StringSplitOptions.None);
var propertyName = c[0].Trim();
var value = c[1].Trim();
// Create the lambda
var arg = Expression.Parameter(typeof(Product), "p");
var property = typeof(Product).GetProperty(propertyName);
var comparison = Expression.Equal(
Expression.MakeMemberAccess(arg, property),
Expression.Constant(value));
var lambda = Expression.Lambda<Func<Product, bool>>(comparison, arg).Compile();
// Test
var prod1 = new Product() { name = "Test" };
var prod2 = new Product() { name = "Jujyfruits" };
Console.WriteLine(lambda(prod1)); // outputs False
Console.WriteLine(lambda(prod2)); // outputs True
About the constructor thing: Since Func<T, TResult> is sealed, you cannot derive from it. However, you could create an implicit conversion operator that translates Specification<T> into Func<T, bool>.
We've recently discovered the Dynamic LINQ library from the VS2008 sample projects. Works perfectly to turn string based "Where" clauses into expressions.
This link will get you there.
You need to turn your search term into a predicate. Try something like the following:
string searchString = "JujyFruits";
Func<Product, bool> search = new Func<Product,bool>(p => p.name == searchString);
return DB.Products.Where(search);
I have this:
List<string> fields;
fields[0] = "firstFieldName";
fields[1] = "secondFieldName";
...
fields[n] = "nthFieldName";
I want to get this:
var selector = p => new {p.firstField, p.secondField, ..., p.nthFieldName}
// selector is of type Expression<Func<Entity, object>>
GoofBallLogic had this code that was simliar, ending up with p => p.column
// Entity is an object in a diagram from Entity Framework 4
var p = Expression.Parameter(typeof(Entity, "p");
var selector = Expression.Lambda<Func<Entity, string>(
Expression.Property(p, columnToGroupBy), p );
EDIT: What I am trying to accomplish
I have a "generic" Repository:
public class Repository<E, C> : IRepository<E,C>
{
private C _dc {get;set;} // ObjectContext (Entity Framework 4)
private string _entityName {get;set;}
public string entityKeyName {get;private set;}
public List<string> entityKeys {get;private set;}
public Expression<Func<E, object>> entityKey {get;private set;}
private EntityContainer _containerName {get;set;}
public Repository(C myDC)
{ _dc = myDC; // TODO: check for null
// Name of "this" ObjectContext
_containerName = _dc.MetadataWorkspace.GetEntityContainer(
_dc.DefaultContainerName, DataSpace.CSpace);
// Name of "this" Entity
_entityName = _containerName.BaseEntitySets
.Where(p => p.ElementType.Name == typeof (E).Name)
.Select( p => p.Name).FirstOrDefault();
// String list of the keys
entityKeys = _containerName
.BaseEntitySets.First(meta => meta.ElementType.Name ==
typeof(E).Name)
.ElementType.KeyMembers.Select(k => k.Name).ToList();
// Thanks Jon Skeet for this cool comma sep list formula
entityKeyName = string.Join(",", entityKeys.ToArray() );
entityKey = Expression.Lambda<Func<E, object>> ...
What to do to set entityKey as an object that can be used in
an OrderBy statement since Linq to Entities requires
ordering a set before doing a .Skip().Take()
Edit:
Amazingly, Orderby can take this:
p => "field1,field2,field3"
Which allows my code to execute but doesn't actually order the items by the field values. It's a first step in TDD I guess: use a literal.
I found this an interesting problem and took some time to figure it out, and found a relatively easy way to do it.
Anyhow, here's an example on how to do a single field sort (i'll use your first field), if you want to sort on more fields you'll have to create expressions for them too and use .ThenBy(xxx) after the usual OrderBy(xxx).
// Create a parameter which passes the object
ParameterExpression param = Expression.Parameter(typeof(E), "a");
// Create body of lambda expression
Expression body = Expression.PropertyOrField(param, fieldname);
// Create lambda function
Expression<Func<E, string>> exp = Expression.Lambda<Func<E, string>>(body, param);
// Compile it so we can use it
Func<E, string> orderFunc = exp.Compile();
Now you can do an OrderBy(orderFunc) and it'll sort the list by the property named in fieldname. Only downside being it only works for string fields (return value of expression). Could probably work around that too though.
Fixed to work with any IComparable type:
// Create a parameter which passes the field
ParameterExpression param = Expression.Parameter(typeof(E), "a");
// Create body of lambda expression
Expression body = Expression.TypeAs(Expression.PropertyOrField(param, fieldname), typeof(IComparable));
// Create lambda function
Expression<Func<E, IComparable>> exp = Expression.Lambda<Func<E, IComparable>>(body, param);
// Compile it so we can use it
Func<E, IComparable> orderFunc = exp.Compile();
You cannot do this easily because you cannot construct a new expression for a type that doesn’t exist at runtime. (You can have anonymous types in C# because the C# compiler creates the type for you.)
If you want to do it the really hard way, you could generate a dynamic assembly and actually create the type you need. There is a short example here.
I suspect that there is an easier way, though. We would need to know what your goal is (what you need this expression tree for), which you haven’t stated.
From your edited question, it appears that you just want to be able to order by multiple keys. That is easily possible simply by using .OrderBy() followed by .ThenBy(). I’m assuming that you are using an IQueryable<E> here:
IQueryable<E> query = ...;
IOrderedQueryable<E> ordered = null;
foreach (var key in entityKeys)
{
// Code from Doggett to construct the lambda expression for one step
ParameterExpression param = Expression.Parameter(typeof(E), "a");
var body = Expression.TypeAs(
Expression.PropertyOrField(param, key),
typeof(IComparable));
var exp = Expression.Lambda<Func<E, IComparable>>(body, param);
if (ordered == null)
ordered = query.OrderBy(exp);
else
ordered = ordered.ThenBy(exp);
}
var finalQuery = (ordered ?? query).Skip(n).Take(m);
I've been looking on google but not finding anything that does the trick for me.
as you know SQL has a "where x in (1,2,3)" clause which allows you to check against multiple values.
I'm using linq but I can't seem to find a piece of syntax that does the same as the above statement.
I have a collection of category id's (List) against which I would like to check
I found something that uses the .contains method but it doesn't even build.
You have to use the Contains method on your id list:
var query = from t in db.Table
where idList.Contains(t.Id)
select t;
The syntax is below:
IEnumerable<int> categoryIds = yourListOfIds;
var categories = _dataContext.Categories.Where(c => categoryIds.Contains(c.CategoryId));
The key thing to note is that you do the contains on your list of ids - not on the object you would apply the in to if you were writing sql.
Here's an article illustrating the approach. You should indeed use the Contains method over your collection which will be translated into IN clause.
Here is my realization of WhereIn() Method, to filter IQueryable collection by a set of selected entities:
public static IQueryable<T> WhereIn<T,TProp>(this IQueryable<T> source, Expression<Func<T,TProp>> memberExpr, IEnumerable<TProp> values) where T : class
{
Expression predicate = null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
bool IsFirst = true;
MemberExpression me = (MemberExpression) memberExpr.Body;
foreach (TProp val in values)
{
ConstantExpression ce = Expression.Constant(val);
Expression comparison = Expression.Equal(me, ce);
if (IsFirst)
{
predicate = comparison;
IsFirst = false;
}
else
{
predicate = Expression.Or(predicate, comparison);
}
}
return predicate != null
? source.Where(Expression.Lambda<Func<T, bool>>(predicate, param)).AsQueryable<T>()
: source;
}
And calling of this method looks like:
IQueryable<Product> q = context.Products.ToList();
var SelectedProducts = new List<Product>
{
new Product{Id=23},
new Product{Id=56}
};
...
// Collecting set of product id's
var selectedProductsIds = SelectedProducts.Select(p => p.Id).ToList();
// Filtering products
q = q.WhereIn(c => c.Product.Id, selectedProductsIds);