There are methods in MongoDB C# driver (or any other libraries) that expect an expression only as a parameter like:
query.SortByDescending(x => x.Name) // sort by name for example
What I would like to do is to receive the field "Name" as a string and be able to sort by it at runtime:
var sortBy = "Name";
query.SortByDescending(x => x."SortyBy") // something like this
i.e how to create an expression to be x => x.(value of sortBy)?
This question basically has the same answer as your question yesterday.
The reason the answer is essentially the same is simply that FieldDefinition<T> is implicitly castable from string, so you can pass a string anywhere you see a FieldDefinition<T> argument.
Therefore, we can use the .Sort method (since that takes a SortDefinition<T>) and use Builders<T>.Sort.Descending which takes a FieldDefinition<TElement> and returns the SortDefinition<T> that we need:
query = query.Sort(Builders<Items>.Sort.Descending(sortBy));
I'm assuming your document type is Items here as it was in your question yesterday.
Note that the textual name you use here has to match the name in the database, which isn't necessarily the same as your document's property name. If you want to make it more robust, then you'll want to use expression trees to produce an expression for the property in question.
An alternative solution would be to build the Expression<Func<T, TElement>> that SortByDescending requires:
private static Expression<Func<TDocument, object>> GetPropertyExpression<TDocument>(string propertyName)
{
var parameter = Expression.Parameter(typeof(TDocument));
var property = Expression.Property(parameter, propertyName);
var castResult = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<TDocument, object>>(castResult, parameter);
}
query = query.SortByDescending(GetPropertyExpression<Items>(sortBy));
As far as Mongo is concerned, this is no different to having written query = query.SortByDescending(i => i.Name);
Related
Hi, I'm trying to build an Expression to get a generic entity by its primary key and getting a parameterized sql query.
Currently I can get the correct WHERE query, but it isn't parameterized.
public async Task<TDbo> Get(TKey key, Expression<Func<TEntity, TKey>> keySelector)
{
var propertyRef = keySelector.Body;
var parameter = keySelector.Parameters[0];
var constantRef = Expression.Constant(key);
var equals = Expression.Equal(propertyRef, constantRef);
var comparer = Expression.Lambda<Func<TEntity, bool>>(equals, parameter);
return await _context.Set<TDbo>().SingleOrDefaultAsync(comparer);
}
This results in the following query:
SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = 1\r\nLIMIT 2,
instead of the wanted:
SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = #__s_0\r\nLIMIT 2
It's because of Expression.Constant(key). Value constant expressions are not parameterized by the query translator. What you need is an expression referring to a property or field of another expression (which could be constant). That's basically what C# compiler emits for closures.
One way is to actually use the C# compiler to create lambda expression with closure and take the body:
Expression<Func<TKey>> keyValue = () => key;
var variableRef = key.Body;
(the variableRef is a replacement of yours constantRef)
Another way is to use anonymous, tuple or specific class type to create explicit closure instance and bind the corresponding property or field. For instance, with anonymous type:
var variableRef = Expression.Property(Expression.Constant(new { key }), "key");
or with System.Tuple:
var variableRef = Expression.Property(Expression.Constant(Tuple.Create(key)), "Item1");
The actual method doesn't really matter (I personally prefer the first variant with lambda) - all they will cause creating parameter by EF Core query translator.
I would like to create the following expression dynamically:
e.Collection.Select(inner => inner.Property)
I created this code to do it, however I have an issue when I execute the expression call, someone knows what I'm doing wrong?
private static Expression InnerSelect<TInnerModel>(IQueryable source, ParameterExpression externalParameter, string complexProperty)
{
// Creates the expression to the external property. // this generates: "e.Collection".
var externalPropertyExpression = Expression.Property(externalParameter, complexProperty);
// Creates the expression to the internal property. // this generates: "inner => inner.Property"
var innerParameter = Expression.Parameter(typeof(TInnerModel), "inner");
var innerPropertyExpression = Expression.Property(innerParameter, "Property");
var innerLambda = Expression.Lambda(innerPropertyExpression, innerParameter);
return Expression.Call(typeof(Queryable), "Select", new [] { typeof(TInnerModel) }, externalPropertyExpression, innerLambda);
}
Error:
No generic method 'Select' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
So, first off, the primary problem is very simple. As the error message says, you haven't passed enough type arguments to Select. But when you fix that, you'll still have a problem, and that problem will be much harder for you to see and understand.
Let's dig into that.
You wish to represent this as an expression tree:
e.Collection.Select(inner => inner.Property)
Let's begin by rewriting it in its non-extension-method form.
Queryable.Select<A, B>(e.Collection, inner => inner.Property)
Where A is the collection member type and B is the type of Property.
Now, suppose you had this expression in your program. What would it actually do at runtime? It would construct an expression tree for the lambda and pass it to Queryable.Select. That is, it would do something like:
var innerParameter = parameterFactory(whatever);
var lambdaBody = bodyFactory(whatever);
var lambda = makeALambda(lambdaBody, innerParameter);
Queryable.Select<TInnerModel>(e.Collection, lambda);
Right? You with me so far?
Now, suppose we wish to translate this program fragment to an expression tree that could itself be the body of a lambda. That would be:
var theMethodInfoForSelect = whatever;
var receiverE = valueFactory(whatever);
var thePropertyInfoForCollection = whatever;
var theFirstArgument = propertyFactory(receiverE, thePropertyInfoForCollection);
...
Again, with me so far? Now, the crucial question: what is the second argument? It is NOT the value of lambda, which is what you are passing. Remember, what we are doing here is constructing an expression tree which represents the code that the compiler is generating for that thing, and the expression tree that is in lambda is not that. You're mixing levels!
Let me put it this way: the compiler is expecting "add one and three". You are passing 4. Those are very different things! One of them is a description of how to obtain a number and the other one is a number. You are passing an expression tree. What the compiler is expecting is a description of how to obtain an expression tree.
So: do you have to now write code that generates expression trees for all of lambda's construction code? Thank goodness no. We provided you a handy way to turn an expression tree into a description of how to produce an expression tree, which is the Quote operation. You need to use it.
So, what is the right sequence of events that you need to do to build your expression tree? Let's walk through it:
First, you'll need a ParameterExpression of the type of e, which you already have in hand. Let's suppose that is:
ParameterExpression eParam = Expression.Parameter(typeof(E), "e");
Next, you will need a method info for the Select method. Let's suppose you can correctly get that.
MethodInfo selectMethod = whatever;
That method takes two arguments, so let's make an array of argument expressions:
Expression[] arguments = new Expression[2];
You'll need a property info for your Collection property. I assume you can get that:
MethodInfo collectionGetter = whatever;
Now we can build the property expression:
arguments[0] = Expression.Property(eParam, collectionGetter);
Super. Next we need to start building that lambda. We need a parameter info for inner:
ParameterExpression innerParam = Expression.Parameter(typeof(Whatever), "inner");
We'll need a property info for Property, which I assume you can get:
MethodInfo propertyGetter = whatever;
Now we can build the body of the lambda:
MemberExpression body = Expression.Property(innerParam, propertyGetter);
The lambda takes an array of parameters:
ParameterExpression[] innerParams = { innerParam };
Build the lambda from the body and the parameters:
var lambda = Expression.Lambda<Func<X, int>>(body, innerParams);
Now the step you missed. The second argument is the quoted lambda, not the lambda:
arguments[1] = Expression.Quote(lambda);
Now we can build the call to Select:
MethodCallExpression callSelect = Expression.Call(null, selectMethod, arguments);
And we're done.
Give someone an expression tree and you give them an expression tree for a day; teach them how to find expression trees themselves and they can do it for a lifetime. How did I do that so fast?
Since I wrote the expression tree code generator, I had some immediate familiarity with the problem that you were likely to have. But that was ten years ago, and I did not do the above entirely from memory. What I did was I wrote this program:
using System;
using System.Linq.Expressions;
public interface IQ<T> {}
public class E
{
public IQ<X> C { get; set; }
}
public class X
{
public int P { get; set; }
}
public class Program
{
public static IQ<R> S<T, R>(IQ<T> q, Expression<Func<T, R>> f) { return null; }
public static void Main()
{
Expression<Func<E, IQ<int>>> f = e => S<X, int>(e.C, c => c.P);
}
}
Now I wished to know what code was generated by the compiler for the body of the outer lambda, so I went to https://sharplab.io/, pasted in the code, and then clicked on Results --> Decompile C#, which will compile the code to IL and then decompile it back to human-readable C#.
This is the best way I know of to quickly understand what the C# compiler is doing when it builds an expression tree, regardless of whether you know the compiler source code backwards and forwards. It's a very handy tool.
I am not very familiar with lambda expressions. So I have the following expression:
EnabledPropertySelector = l => l.FranchiseInfo.ExternalSystemType == ExternalSystemTypes.Master
And two properties:
public string VisibilityPropertyName { get; set; }
public object VisibilityPropertyValue { get; set; }
I want to extract some data from the expression so in the end I can get the values of the two properties:
VisibilityPropertyName == 'FranchiseInfo.ExternalSystemType';
VisibilityPropertyValue == ExternalSystemTypes.Master;
VisibilityPropertyName is always a string. This is the name of the property.
VisibilityPropertyValue can be of any type.
EDIT:
I have a lot of properties. Some of them are dependent on other properties. For every single property I have to manually write the name and the value of the parent property:
{ VisibilityPropertyName = 'FranchiseInfo.ExternalSystemType', VisibilityPropertyValue = ExternalSystemTypes.Master, EnabledPropertySelector = l => l.FranchiseInfo.ExternalSystemType == ExternalSystemTypes.Master}
Instead of writing all this I want to write only the expression and populate the properties from it.
This is the declaration of the expresion:
Expression<Func<TEntity, bool?>> EnabledPropertySelector
First off all, you need an Expression. What's the type of EnabledPropertySelector? It'll need to be something like Expression<Func<T, bool>> where T is whatever the type of "l" in your example is.
If you already have an Expression then you can use the Expression API to extract whatever you need:-
var body = EnabledPropertySelector.Body as BinaryExpression;
var left = body.Left as PropertyExpression;
var outerMemberName = left.Member.Name;
var innerMemberName = (left.Expression as PropertyExpression).Member.Name
VisibilityPropertyName = innerMemberName + "." + outerMemberName;
var right = body.Right as PropertyExpression;
var rightValueDelegate = Expression.Lambda<Func<object>>(right).Compile();
VisibilityPropertyValue = rightValueDelegate();
etc.
I really recommend doing some reading to properly grok the expression API before diving in though; there are a lot of corner cases depending on how flexible you need to be. E.g. is the expression always of the form parameter.Property.Property == constant? It gets really complicated really quickly, so you'll want a solid understanding of the fundamentals before trying to handle any real-world cases.
There's a reasonable introduction to expression trees on MSDN, but some focused googling might get you a better understanding quicker.
You can use Funciton and Action class, I'm not very sure of what you want be able to do, but I can give an tip.
Functions returns a value:
Function<InputType1,InputType2,ResultType> functionVariableName;
Usage:
functionVariableName = (param1, param2) => {
//...process both params
return result;
};
Actions, do not return values:
Action<InputType1,InputType2> actionVariableName;
Usage:
actionVariableName= (param1, param2) => {
//...process both params
};
If the lambda expression is simple (one line, with out if expression) you can make the lambda with out {}:
functionVariableName = (param1, param2) => //process values and result;
Hope this helps...
if you want to create an IEnumerable where the two properties are equal:
var results = EnabledPropertySelector.Where(l => l.FranchiseInfo.ExternalSystemType ==
ExternalSystemTypes.Master.ToString());
I am trying to create a dynamic lambda expression (parsed from text), that does more than one assignment. Creating the individual assignments was fairly easy, however I am stuck on how to combine them. So what I'm trying to achieve is the same as:
Action<Entity> action = (entity) =>
{
entity.Property1 = "1";
entity.Property2 = "2";
};
Is there a way to combine more than one Expression.Assign expressions into one (since Expression.Lambda only takes one Expression as input)?
--Edit:
So for clarification the code I wrote was just pseudo-code, so the typo (now corrected) doesn't change anything. The code I have right now is something like :
Expression parameter = Expression.Parameter(typeof(Entity), "param");
Expression firstProperty = Expression.Property(parameter, "Property1");
Expression first = Expression.Assign(firstProperty, "1");
Expression secondProperty = Expression.Property(parameter, "Property2");
Expression second = Expression.Assign(secondProperty, "2");
Expression final = [INSERT MAGIC HERE]
Action<Entity> action = Expression.Lambda<Action<Entity>>(final, entity).Complie();
Note that the property name actually come from text input, and the number of assigment expressions isn't limited to 2, it's just for demonstration purposes. My question is, is there an expression the lets me combine the assigment expressions or do I have to create different lambdas for each one?
Try this magic:
Expression.Block(first, second);
I want to provide a set of filters for a user to pick from, and each filter will correspond to an Expression<Func<X, bool>>. So, I might want to take a dynamic list of available items ('Joe', 'Steve', 'Pete', etc), and create a collection of "hard-coded" filters based on those names, and let the user select which filter(s) he wants to use. My problem is that even when I try to "hard-code" my expression based on a string value from the dynamic list, the expression is still storing the value as, what looks to me, a property hanging off of an anonymous type (and I don't know how to serialize the anon. type). Sorry if this is confusing, I'm not quite sure how to articulate this.
Here's my sample code:
public class Foo
{
public string Name { get; set; }
}
static void Main(string[] args)
{
Foo[] source = new Foo[]
{
new Foo() { Name = "Steven" } ,
new Foo() { Name = "John" } ,
new Foo() { Name = "Pete" },
};
List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>();
foreach (Foo f in source)
{
Expression<Func<Foo, bool>> exp = x => x.Name == f.Name;
filterLst.Add(exp);
}
}
}
My problem is that when I look at when I look at the body of my expression, it reads as follows:
(x.Name = value(ConsoleApplication1.Program+<>c__DisplayClass3).value)
When what I really want is for the first one to read like this:
(x.Name = "Steven")
(if I change my code to this, instead, that's exactly what I get:
Expression<Func<Foo, bool>> exp = x => x.Name == "Steven";
)
I've tried forcing my value to a local string value before sticking it into the Expression, but it doesn't seem to help:
List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>();
foreach (Foo f in source)
{
string value = f.Name;
Expression<Func<Foo, bool>> exp = x => x.Name == value;
filterLst.Add(exp);
}
I don't understand why (or really even how) it's still looking at some anonymous type even once I'm using a local variable that is declared to a string. Is there a way to make this work as I want it to?
The anon-type is actually the compiler-generated type it is using to perform capture of the variables. With delegates you can hack around this by implementing the capture by hand, but not with lambda expressions compiled to expression-trees.
Two choices:
build the expression tree explicitely on code via Expression.Constant etc
learn how to handle the anon types
The latter isn't too bad actually; they are just MemberExpression typically, although I have some code kicking around that covers this in full detail. I can also provide examples of buildin the expression tree, but I'm not at a PC at the moment and it doesn't lend itself well to iPod typing...
From a brief read of the question I'd look at the first option more than the second.
Oh, and watch out; the first foreach code in the question looks susceptible to the notorious l-value capture issue ;)
Edit: I found a PC ;p
var param = Expression.Parameter(typeof(Foo), "x");
var body = Expression.Equal(
Expression.PropertyOrField(param, "Name"),
Expression.Constant(f.Name, typeof(string)));
var exp = Expression.Lambda<Func<Foo, bool>>(body, param);
filterLst.Add(exp);
Marc Gravell's answer is correct, and here's how you'd implement his first choice:
var filters =
from f in source
let param = Expression.Parameter(typeof(Foo),"x")
select Expression.Lambda<Func<Foo, bool>>(
Expression.Equal(
Expression.Property(param, "Name"),
Expression.Constant(f.Name)), param);
But this typically isn't necessary. Your second example of the for loop should work with all major LINQ providers. Is there a reason you need the expression to use constants?