Is there any way to use the LINQ dynamic query library (System.Linq.Dynamic) to evaluate a condition based on the properties of an ExpandoObject? The following code throws an exception on the "var e..." line, saying "No property or field 'Weight' exists in type ExpandoObject":-
const string TestCondition = "MyStateBag.Foo >= 50 && MyStateBag.Bar >= 100";
dynamic myStateBag = new ExpandoObject();
myStateBag.Foo = 70;
myStateBag.Bar = 100;
var p = Expression.Parameter(typeof(ExpandoObject), "MyStateBag");
var e = DynamicExpression.ParseLambda(new[] { p }, null, TestCondition);
var result = e.Compile().DynamicInvoke(myStateBag);
Assert.IsTrue(result);
The alternative would be to implement the "statebag" as a dictionary, but this will result in a slightly more verbose condition string, e.g. MyStateBag["Foo"] >= 50 && MyStateBag["Bar"] >= 100. As this is going to be used as the basis of a user scripting environment, I would prefer the simpler ExpandoObject syntax if it's possible to achieve.
Not directly. The dynamic LINQ library boils down to an expression-tree, and expression trees do not directly support dynamic. Most likely, the dynamic query library is using Expression.PropertyOrField to handle .Foo etc, and that will not work with dynamic.
You could perhaps write a custom expression parser that replaces this with lots of lookup code if it finds the parameter is a dictionary; not fun, though.
Related
I'm trying to accomplish what I feel like should be a straightforward task, but have found it much more complicated than I expected.
Essentially, given:
public class MyObject
{
public int A;
public float B;
public string C;
}
List<MyObject> objectList;
I would like to be able to read in strings something like:
"A < 1"
"B > 0.5"
"C = \"text\""
and for each of those get a List of items in objectList satisfying the requirement.
I've been working with LINQ queries like:
objectList.Where(obj => obj.A < 1)
so far, but am unable to figure out how to create queries like that with the field name.
Is there something straightforward that I am missing? Or is my whole approach here flawed?
I thinks you can use Expression tree, which you may be create own lambda expression and pass the it Where function.
For example please check these link:
Dynamically generate LINQ queries
Or you can use System.Linq.Dynamic namespace.:
https://docs.telerik.com/data-access/developers-guide/linq-support/data-access-feature-ref-linq-support-dynamic-linq
You can apply AND operator (&&):
objectList.Where(obj => obj.A < 1 && obj.B > 0.5 && obj.C == "text").ToList();
More to read Filtering in LINQ
EDIT:
If you want to query based on string you can use System.Linq.Dynamic
objectList.Where("string predicate ..");
I have this
List<Expression> levl1expressions;
Collection contains binary expressions like Expression.NotEqual, Expression.Equal
etc
I have another collection which is And and Or Conditions
List<Expression> levl2expressions;
I would like to execute these two expression collections
levl1expressions[0]+levl2expressions[0]+levl1expressions[1]+levl2expressions[1]....
Is this possible?
eg:
object.Name = "something" && object.Category != "myCategory"(//I transformed the string to expressions)
levl1expressions[0] = Expression.Equal(
Expression.Property(Expression.Parameter(typeof(MyObject), "m")),
Expression.Constant("something")
levl1expressions[1] = Expression.NotEqual(....)
levl2expressions[0]= Expression.And(/*Would like to join levl1expressions*/)
If I understand your problem correctly, you should not use lists at all. Instead you can create a single expression which will look like that:
var finalExpression = Expression.And(Expression.Equal(...), Expression.NotEqual(...));
If you would like to combine more logical operators then you can use a result of Expression.And as the right operand:
var finalExpression = Expression.And(Expression.Equal(...), Expression.And(Expression.NotEqual(...), Expression.Equal(...)));
To invoke the expression you need to compile first:
var action = Expression.Lambda<Action<bool>>(finalExpression).Compile();
Here Action<bool> is specifying what kind of function you create. Action<bool> basically means a function which is returning a boolean and has no parameters. Once you have that you can simply call it:
var result = action();
Bear in mind that expression compilation process is very expensive. Cache the result if you can.
I am trying to use Expression Trees because based on description, that seems to be the most correct (performant, configurable) approach.
I expect to be able to craft a statement that gets the first item from the existingItems collection that matches the propertyNameToCompareOn value of the incomingItem.
I have a method with the following signature and simulated code body...
DetectDifferences<T>(List<T> incomingItems, List<T> existingItems)
{
var propertyNameToCompareOn = GetThisValueFromAConfigFile(T.FullName());
//does this belong outside of the loop?
var leftParam = Expression.Parameter(typeof(T), "left");
var leftProperty = Expression.Property(leftParam, identField);
var rightParam = Expression.Parameter(typeof(T), "right");
var rightProperty = Expression.Property(rightParam, identField);
//this throws the error
var condition = Expression.Lambda<Func<T, bool>>(Expression.Equal(leftProperty, rightProperty));
foreach (var incomingItem in incomingItems) //could be a parallel or something else.
{
// also, where am I supposed to provide incomingItem to this statement?
var existingItem = existingItems.FirstOrDefault(expression/condition/idk);
// the statement for Foo would be something like
var existingFoos = exsistingItems.FirstOrDefault(f => f.Bar.Equals(incomingItem.Bar);
//if item does not exist, consider it new for persistence
//if item does exist, compare a configured list of the remaining properties between the
// objects. If they are all the same, report no changes. If any
// important property is different, capture the differences for
// persistence. (This is where precalculating hashes seems like the
// wrong approach due to expense.)
}
}
At the marked line above, I get an "Incorrect number of parameters supplied for lambda declaration" InvalidOperationException. At this point I am just hacking crap together from the web and I really dont know what this wants. There are a bunch of overloads that VS can full my screen with, and none of the examples make sense from the articles on MSDN/SO.
PS - I dont really want an IComparer or similar implementation if it can be helped. I can do that with reflection. I do need to make this as rapid as possible, but allow it to be called for multiple types, hence the choice of expression trees.
When working with expression trees, it's important to first understand, in real code, what you want to do.
I always begin by first writing out (in static code) what the resulting expression looks like with real C# lambda syntax.
Based on your description, your stated goal is that you should be able to (dynamically) look up some property of the type T that gives some sort of quick comparison. How would you write this if both T and TProperty were both known at compile time?
I suspect it would look something like this:
Func<Foo, Foo, bool> comparer = (Foo first, Foo second) =>
first.FooProperty == second.FooProperty;
Right away we can see that your Expression is wrong. You don't need one input T, you need two!
It should also be obvious why you're getting the InvalidOperationException as well. You never supplied any parameters to your lambda expression, only the body. Above, 'first' and 'second' are the parameters provided to the lambda. You'll need to provide them to the Expression.Lambda()call as well.
var condition = Expression.Lambda<Func<T,T, bool>>(
Expression.Equal(leftProperty, rightProperty),
leftParam,
rightParam);
This simply uses the Expression.Lambda(Expression, ParameterExpression[]) overload for Expression.Lambda. Each ParameterExpression is the parameter that is used in the body. That's it. Don't forget to .Compile() your expression into a delegate if you want to actually invoke it.
Of course this doesn't mean that your technique will be necessarily fast. If you're using fancy expression trees to compare two lists with a naive O(n^2) approach, it won't matter.
Here's a method to make a property access expression;
public static Expression<Func<T, object>> MakeLambda<T>(string propertyName)
{
var param = Expression.Parameter(typeof(T));
var propertyInfo = typeof(T).GetProperty(propertyName);
var expr = Expression.MakeMemberAccess(param, propertyInfo);
var lambda = Expression.Lambda<Func<T, object>>(expr, param);
return lambda;
}
which you can use like this;
var accessor = MakeLambda<Foo>("Name").Compile();
accessor(myFooInstance); // returns name
Making your missing line
var existingItem = existingItems.FirstOrDefault(e => accessor(e) == accessor(incomingItem));
Be aware the == only works well for value types like ints; careful of comparing objects.
Here's proof the lambda approach is much faster;
static void Main(string[] args)
{
var l1 = new List<Foo> { };
for(var i = 0; i < 10000000; i++)
{
l1.Add(new Foo { Name = "x" + i.ToString() });
}
var propertyName = nameof(Foo.Name);
var lambda = MakeLambda<Foo>(propertyName);
var f = lambda.Compile();
var propertyInfo = typeof(Foo).GetProperty(nameof(Foo.Name));
var sw1 = Stopwatch.StartNew();
foreach (var item in l1)
{
var value = f(item);
}
sw1.Stop();
var sw2 = Stopwatch.StartNew();
foreach (var item in l1)
{
var value = propertyInfo.GetValue(item);
}
sw2.Stop();
Console.WriteLine($"{sw1.ElapsedMilliseconds} vs {sw2.ElapsedMilliseconds}");
}
As someone's also pointed out, though, the double-loop in the OP is O(N^2) and that should probably be the next consideration if efficiency is the driver here.
I almost got what I need but only one place where I stucked. I need to build fileCount = c.CPNDocs.Count() Dynamically in Lambda Expression. Code is below with comments what I am using to build Dynamic Lambda Expression.
var dColDefaultList = new List<String>() { "Download", "I_ID", "C_TYP", "C_LST_ACT" }; // <------- Columns I need in Lambdas Expression
ParameterExpression cParam = Expression.Parameter(typeof(CPNDBase), "c");
NewExpression newExp = Expression.New(typeof(DTDataModel));
List<MemberBinding> bindings = new List<MemberBinding>();
foreach (String sCol in dColDefaultList)
{
if (!String.Equals(sCol, "Download")) {
bindings.Add(GetMemberBinding(sCol, cParam, sCol));
}
else
{
bindings.Add(GetMemberBinding("fileCount", cParam, "CPNDocs.Count()")); // <-------need count of rows return from CPNDocs(Different Table) is a Object I recieved from Entity Relatioship
}
}
MemberInitExpression memberInitExpression = System.Linq.Expressions.Expression.MemberInit(newExp, bindings);
Expression<Func<CPNDBase, DTDataModel>> selector = (Expression<Func<CPNDBase, DTDataModel>>)BinaryExpression.Lambda(memberInitExpression, cParam);
// selector will be selector = {c => new DTDataModel() {fileCount = c.CPNDocs, I_ID = c.I_ID, C_TYP = c.C_TYP, C_LST_ACT = c.C_LST_ACT }}
// but I Need selector = {c => new DTDataModel() {fileCount = c.CPNDocs.Count(), I_ID = c.I_ID, C_TYP = c.C_TYP, C_LST_ACT = c.C_LST_ACT }}
// Question is How can I make fileCount = c.CPNDocs.Count() ?
var resultLm = finalFilteredCPNData.AsQueryable<CPNDBase>().Select(selector);
Above method is defined here :
static MemberBinding GetMemberBinding(string property, ParameterExpression param, string column)
{
MemberInfo memberInfo = typeof(DTDataModel).GetMember(property)[0];
MemberExpression memberExpression = LambdaExpression.PropertyOrField(param, column);
return System.Linq.Expressions.Expression.Bind(memberInfo, memberExpression);
}
Does anybody know how can I do this?
The Count() is not a property. It is an extension method implemented in a static class. This extension method is implemented at several places. Correct place depends on what are your classes inheriting from. To find the correct place you use the "go to definition" feature of Visual Studio.
e.g. for IQueryable.Count() the extension methods are implemented by System.Linq.Queryable static class as can be seen here → http://referencesource.microsoft.com/#System.Core/System/Linq/IQueryable.cs
So in order to encode the expression you need to encode a call to the extension method.
Much simpler way to generate expression trees from strings was shown quite early in a prototype published by Microsoft. Introductory article is available e.g. in Dynamic Expressions and Queries in LINQ
We use modified version of the original source of the automatic "string to linq" engine with success and it simplifies development a lot. By inspecting the source code of the System.Linq.Dynamic you can find exact way how to encode the expression. Link to the original source code available through NuGet is mentioned e.g. in Stack Overflow article Dynamic LINQ - Is There A .NET 4 Version?
Say I have a LINQ-to-XML query that generates an anonymous type like this:
var aQuery =
(from a in document.Root.Elements("items")
select new {
id = a.Attribute("id").Value,
type = a.Attribute("type").Value,
modified = a.Attribute("modified").Value
});
if there a way to store that query expression in a variable or constant and then execute at runtime? The basic idea is that I have a bunch of these expressions and it would be handy if they could all be defined in one place and then invoked dynamically thru a single method where I just need to pass in the XML document and which expression to use. Thanks.
You could define them as methods quite easily, though you'd forfit the right to use anonymous types.
public static IQueryable<Item> GetItemsFromXml(XDocument document)
{
return (from a in document.Root.Elements("items")
select new Item
{
Id = a.Attribute("id").Value,
Type = a.Attribute("type").Value,
Modified = a.Attribute("modified").Value
});
}
Having said that, patterns like the repository pattern are used to wrap the whole process of accessing data.