Parsing expression with unknown number of parameters in DynamicExpreso - c#

Due to performance reasons GitHub page suggests "If you need to run the same expression multiple times with different parameters I suggest to parse it one time and then invoke the parsed expression multiple times."
I have a class like this:
public class Conditional : TemplateElement
{
public string Condition { get => _condition; set => _condition = value; }
public Lambda Parsed { get => ...; private set => ... }
public Conditional(string condition)
{
Condition = condition;
...
ParseExpression();
}
private void ParseExpression()
{
var target = new Interpreter();
Lambda = target.Parse(Condition, ???);
}
}
The 'Condition' string can be in form:
item["CreatedDate"] <= DateTime.Today.AddDays(-2)
Now, at the moment of instantiation of Conditional class I don't know what the 'item' contains, I want it to be parsed so I can use it later. Lambda should resolve to the Condition to boolean.
I'm not exactly sure how to achieve this, documentation doesn't help me much. Should I define 'item' as specific type in Parameters array?

Related

Get the name of the passed method without using nameof

It is easy to declare a method, which takes a method name as a string:
public void DoSomethingWithMethodName(string methodName)
{
// Do something with the method name here.
}
and the call it as:
DoSomethingWithMethodName(nameof(SomeClass.SomeMethod));
I want to get rid of nameof and call some other method as:
DoSomethingWithMethod(SomeClass.SomeMethod);
and then be able to get the name of the method the same as in the example above. It "feels" possible to do that using some Expression and / or Func sorcery. The question is what signature this DoSomethingWithMethod should have and what it should actually do!
====================================
The question seems to cause a lot of confusion and the answers assume what I did not ask. Here is a hint at what I am aiming but cant' get right. This is for some different problem (for which I have a solution). I can declare:
private async Task CheckDictionary(Expression<Func<LookupDictionary>> property, int? expectedIndex = null)
{
await RunTest(async wb =>
{
var isFirst = true;
foreach (var variable in property.Compile().Invoke())
{
// Pass the override value for the first index.
await CheckSetLookupIndex(wb, GetPathOfProperty(property), variable, isFirst ? expectedIndex : null);
isFirst = false;
}
});
}
where GetPathOfProperty comes from:
https://www.automatetheplanet.com/get-property-names-using-lambda-expressions/
and Fully-qualified property name
and then use:
[Fact]
public async Task CommercialExcelRaterService_ShouldNotThrowOnNumberOfStories() =>
await CheckDictionary(() => EqNumberOfStories, 2);
where EqNumberOfStories is:
public static LookupDictionary EqNumberOfStories { get; } = new LookupDictionary(new Dictionary<int, string>
{
{ 1, "" },
{ 2, "1 to 8" },
{ 3, "9 to 20" },
{ 4, "Over 20" }
});
As you can see, I am passing a property and then "unwinding" it to get to the source. I'd like to do the same but in a simpler setup as described above.
You can use [CallerMemberName] to get the name of the calling method.
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
System.Diagnostics.Trace.WriteLine("message: " + message);
System.Diagnostics.Trace.WriteLine("member name: " + memberName);
System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}
In example above example memberName param will be assigned with value DoProcessing.
Sample output
message: Something happened.
member name: DoProcessing
source file path:
C:\Users\user\AppData\Local\Temp\LINQPad5_osjizlla\query_gzfqkl.cs
source line number: 37
https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx
Basically what you have to do is declare the parameter as a Func that matches the method signature you want to accept and then wrap that in an Expression so that the compiler will give you an expression tree rather than an actual delegate. Then you can walk through the expression tree to find a MethodCallExpression from which you can get the method name. (BTW, the sample code in the link you provided will work with method calls too, just like you want, in addition to properties)
what signature this DoSomethingWithMethod should have
It depends on the signature of the method you would like to take as a parameter.
If some method looks like:
public MyReturnType SomeMethod(MyParameterType parameter) {}
Then the DoSomethingWithMethod signature would look like:
public void DoSomethingWithMethod(Expression<Func<MyParameterType,MyReturnType>> methodExpression) {}
You can also make it generic incase you want to accept methods with slightly different signatures (however if you want to accept methods with different numbers of parameters you will have to use overloading, also the C# compiler probably won't resolve the generic type parameters automatically in this situation, and you'll have to specify them explicitly)
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression) {}
and what it should actually do
I suppose this question is actually, how do I get the method name as a string from the expression tree?
There are a couple of different ways to do this and it depends on how robust you want your code to be. Consider that the above method signature allows for passing a delegate that is significantly more complicated than just a single method call. For example:
DoSomethingWithMethod(t => t.SomeMethod().SomeOtherMethod(5) + AnotherThing(t));
If you search the expression tree generated from the above you will find quite a few method calls not just one. If you just want to enforce that the passed parameter is a single method call, it's probably easier to just try to cast the expression Body property to a MethodCallExpression
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression)
{
if (methodExpression.Body is MethodCallExpression methodCall)
{
var methodName = methodCall.Method.Name;
//...
}
}
The other option is to use the visitor pattern, this is helpful especially if you have a more complex scenario, such as you would like to retrieve a list of all method names when there are multiple for example, or a support a mixture of property or method calls, etc.
For this option, create a class that inherits ExpressionVisitor and overrides the appropriate methods in the base class and store the results somewhere. Here's an example:
class MyVisitor : ExpressionVisitor
{
public List<string> Names { get; } = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.MemberType == MemberTypes.Method)
{
Names.Add(node.Member.Name);
}
return base.VisitMember(node);
}
}
you can call it like this:
var visitor = new MyVisitor();
visitor.Visit(methodExpression.Body);
var methodName = visitor.Names[0];
//...
Finally, to call it, you won't be able to use the shortened "method group" mode of calling DoSomethingWithMethod since C# compiler is not capable of automatically converting a method group to an expression tree (it can however automatically convert it to a regular delegate, which is the notation you are used to).
So you can't do:
DoSomethingWithMethod(SomeMethod);
instead it will have to look like a lambda expression:
DoSomethingWithMethod(t => SomeMethod(t));
or if no parameters:
DoSomethingWithMethod(() => SomeMethod());

Dynamically build lambda expression from a collection of objects?

I have a list of sorts stored in this format:
public class ReportSort
{
public ListSortDirection SortDirection { get; set; }
public string Member { get; set; }
}
And I need to turn it into a lambda expression of type Action<DataSourceSortDescriptorFactory<TModel>>
So assuming I have the following collection of Report Sorts as:
new ReportSort(ListSortDirection.Ascending, "LastName"),
new ReportSort(ListSortDirection.Ascending, "FirstName"),
I would need to transform it into such a statement to be used like so:
.Sort(sort => {
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
})
And the sort method signature is:
public virtual TDataSourceBuilder Sort(Action<DataSourceSortDescriptorFactory<TModel>> configurator)
So I have some method right now:
public static Action<DataSourceSortDescriptorFactory<TModel>> ToGridSortsFromReportSorts<TModel>(List<ReportSort> sorts) where TModel : class
{
Action<DataSourceSortDescriptorFactory<TModel>> expression;
//stuff I don't know how to do
return expression;
}
...and I have no idea what to do here.
EDIT: Answer is:
var expression = new Action<DataSourceSortDescriptorFactory<TModel>>(x =>
{
foreach (var sort in sorts)
{
if (sort.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
{
x.Add(sort.Member).Ascending();
}
else
{
x.Add(sort.Member).Descending();
}
}
});
I was thinking at first I had to dynamically build a lambda expression from scratch using the Expression class. Luckily that wasn't the case.
...and I have no idea what to do here.
Well, reason it out.
What have you got in hand? A List<ReportSort> called sorts.
What do you need? An Action<Whatever>.
You've already taken the first step: you've make a method that takes the thing you have and returns the thing you need. Great first step.
Action<DataSourceSortDescriptorFactory<TModel>> expression;
//stuff I don't know how to do
return expression;
And you've called out what you don't know how to do -- yet. This is a good technique.
Start by filling in something that compiles but doesn't work properly.
Action<DataSourceSortDescriptorFactory<TModel>> expression =
sort => {
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
};
return expression;
Excellent. Now you have a compiling program which means you can run your tests and verify that if this case is expected, the test passes, and if anything else is expected, the test fails.
Now think, what have I got in hand? I've got a list of stuff, and I'm doing an Action. That means that a side effect is happening, probably involving every item on the list. So there's probably a foreach in there somewhere:
Action<DataSourceSortDescriptorFactory<TModel>> expression =
sort => {
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
foreach(var sort in sorts) {
// Do something
}
};
return expression;
Compile it. It fails. Ah, we have confused the sort we are adding to with the new sort we are adding. Fix the problem.
Action<DataSourceSortDescriptorFactory<TModel>> expression =
existingSort => {
existingSort.Add("LastName").Ascending();
existingSort.Add("FirstName").Ascending();
foreach(var newSort in sorts) {
// Do something
}
};
return expression;
Great, now again we are in a position to compile and run tests.
The pattern here should be clear. Keep it compiling, keep running tests, gradually make your program more and more correct, reason about the operations you can perform on the values that you have in hand.
Can you finish it off?
You could use the following lambda expression that you could assign to the Action<T> delegate. In that lambda expression, capture the List<T> variable and loop over it:
public static Action<DataSourceSortDescriptorFactory<TModel>> ToGridSortsFromReportSorts<TModel>(List<ReportSort> sorts) where TModel : class
{
Action<DataSourceSortDescriptorFactory<TModel>> expression =
result =>
{
foreach (var sort in sorts)
{
if (sort.SortDirection == ListSortDirection.Ascending)
result.Add(sort.Member).Ascending();
else // or whatever other methods you want to handle here
result.Add(sort.Member).Descending();
}
};
return expression;
}

Untyped lambda calculus in C#

I've been trying to implement raw lambda calculus on C# but I am having some troubles implementing it, as in the end, I am always asked for objects.
I would like something that would allow me, for instance, to define a few basic logical combinators, such as
I = Lambda x. x
M = Lambda x. x(x)
but C# seems to run on the assumption that it will get an object in the end. I've tried to define them in various ways, such as
using lambda = Func<Object, Object>;
using lambda = Func<Func<Object, Object>, Func<Object, Object>>;
using lambda = Func<Func, Func>;
and so on, but either those do not obey the syntax or are incompatible with
lambda I = x => x;
lambda M = x => x(x);
I tried using the delegate
public delegate object lambda(Object o);
static object i(object o)
{
return o;
}
static object m(object o)
{
return ((lambda)o)(o);
}
But in the end, any actual use of those functions will still require an argument at the end of the line, and a dummy argument like
m(m(i('')));
will simply lead to a cast error during execution.
Is there a way to implement typeless lambda calculus natively, or do I have to go back to string processing?
For an example of execution of the program, it would look something like this. For the following functions :
lambda i(lambda x)
{
print("i");
return x;
}
lambda m(lambda x)
{
print("m");
return x(x);
}
The execution of (m(m))(i) should be something like m(m) is evaluated, returning m(m) after printing "m", which gives us back the original (m(m))(i), which will then print an infinite amount of "m" (this is the simplest infinite loop with logical combinators, although this will involve some trampolining later on to avoid blowing the stack).
Rather than encoding a lambda expression as Func<,> (which is probably not a good match for untyped lambda calculus), you should encode it as an abstract syntax tree. Something like this:
public class LambdaAbstraction: ILambdaExpression {
public LambdaVariable Variable { get; set; }
public ILambdaExpression Body { get; set; }
}
public class LambdaApplication: ILambdaExpression {
public ILambdaExpression Function { get; set; }
public ILambdaExpression Argument { get; set; }
}
public class LambdaVariable: ILambdaExpression {
public string Name { get; set; }
}
For example, M would be
ILambdaExpression M = new LambdaAbstraction {
Variable = new LambdaVariable { Name = "x" },
Body = new LambdaApplication {
Function = new LambdaVariable { Name = "x" },
Argument = new LambdaVariable { Name = "x" }
}
}
Then it's quite straightforward to implement recursive methods for alpha renaming and beta reductions on this data structure.

How to dynamically assign lambda<expression<delegate>> to expression<delegate>

I am trying to make dynamic expression and assign lambda to it. As a result, I got exception:
System.ArgumentException: Expression of type 'Test.ItsTrue' cannot be used for assignment to type 'System.Linq.Expressions.Expression`1[Test.ItsTrue]'
What is wrong?
public delegate bool ItsTrue();
public class Sample
{
public Expression<ItsTrue> ItsTrue { get; set; }
}
[TestClass]
public class MyTest
{
[TestMethod]
public void TestPropertySetWithExpressionOfDelegate()
{
Expression<ItsTrue> itsTrue = () => true;
// this works at compile time
new Sample().ItsTrue = itsTrue;
// this does not work ad runtime
var new_ = Expression.New(typeof (Sample));
var result = Expression.Assign(
Expression.Property(new_, typeof (Sample).GetProperties()[0]),
itsTrue);
}
}
The second argument of Expression.Assign is the expression representing the value to assign. So currently you're effectively trying to assign an ItsTrue to the property. You need to wrap it so that it's an expression returning the value itsTrue... either via Expression.Quote or Expression.Constant. For example:
var result = Expression.Assign(
Expression.Property(new_, typeof (Sample).GetProperties()[0]),
Expression.Constant(itsTrue, typeof(Expression<ItsTrue>)));
Alternatively, you might want Expression.Quote - it really depends on what you're trying to achieve.

How can I get property name strings used in a Func of T

I have a scenario where I have to get an array of strings that represent each of the property names used within a Func parameter. Here is an example implementation:
public class CustomClass<TSource>
{
public string[] GetPropertiesUsed
{
get
{
// do magical parsing based upon parameter passed into CustomMethod
}
}
public void CustomMethod(Func<TSource, object> method)
{
// do stuff
}
}
Here would be an example usage:
var customClass = new CustomClass<Person>();
customClass.CustomMethod(src => "(" + src.AreaCode + ") " + src.Phone);
...
var propertiesUsed = customClass.GetPropertiesUsed;
// propertiesUsed should contain ["AreaCode", "Phone"]
The part I'm stuck on in the above is the "do magical parsing based upon parameter passed into CustomMethod."
You should use Expression<Func<>> class instead. The expression contains actual tree, and can easily be complied to get a delegate (which is a func). What You are really trying to do is to look at the body of the expression and reason about it. Expression class provides You with all the neccessary infrastructure.
You'll need to change your CustomMethod to take an Expression<Func<TSource, object>>, and probably subclass the ExpressionVisitor, overriding VisitMember:
public void CustomMethod(Expression<Func<TSource, object>> method)
{
PropertyFinder lister = new PropertyFinder();
properties = lister.Parse((Expression) expr);
}
// this will be what you want to return from GetPropertiesUsed
List<string> properties;
public class PropertyFinder : ExpressionVisitor
{
public List<string> Parse(Expression expression)
{
properties.Clear();
Visit(expression);
return properties;
}
List<string> properties = new List<string>();
protected override Expression VisitMember(MemberExpression m)
{
// look at m to see what the property name is and add it to properties
... code here ...
// then return the result of ExpressionVisitor.VisitMember
return base.VisitMember(m);
}
}
This should get you started in the right direction. Let me know if you need some help figuring out the "... code here ..." part.
Useful links:
Expression Trees
How to Modify Expression Trees
ExpressionVisitor
VisitMember
MemberExpression

Categories

Resources