This works.
context => context.GetType().Name + '1'
Expression type: System.Linq.Expressions.Expression1`1[System.Func`2[Namespace.Context,System.String]]
Body: (context.GetType().Name + Convert(1, Object))
IL:
.Lambda #Lambda1<System.Func`2[Namespace.Context,System.String]>(Namespace.Context $context)
{
(.Call $context.GetType()).Name + (System.Object)'1'
}
This does not work.
context => context.Previous.Output["rate"].ToString()
Expression type: System.Linq.Expressions.Expression1`1[System.Func`2[Namespace.Context,System.String]]
Body: context.Previous.Output.get_Item("rate").ToString()
IL:
.Lambda #Lambda1<System.Func`2[Namespace.Context,System.String]>(Namespace.Context $context)
{
.Call (.Call (($context.Previous).Output).get_Item("rate")).ToString()
}
The error is:
variable 'context' of type 'Namespace.Context' referenced from scope '', but it is not defined
Where is the difference for expression parser? In both cases context is defined as Context parameter inside expression. I have no ideas why the second is not compiling.
I'm using Serialize.Linq library to serialize expression. Maybe there is some bug inside and there are some expression transformations?
Related
I am trying to pass a dynamic object and an Action into a function. (below is a simple test) But, I am getting the following compile time error:
Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.
One or the other is ok... but not both... any help?
void Test()
{
dynamic obj = new System.Dynamic.ExpandoObject();
obj.A = 1;
obj.B = 2;
Calc(obj, (result) =>
{
Console.Write("Result: " + result);
});
}
void Calc(dynamic obj, Action<int> onComplete)
{
onComplete((int)obj.A + (int)obj.B);
}
Sure - do exactly as the compiler says - cast the lambda expression to a concrete type:
Calc(obj, (Action<int>)(result => Console.Write("Result: " + result)));
The reason you have to do this is that a lambda expression doesn't have a type - the compiler has to know what delegate (or expression tree) type you're trying to convert it to. It can't do that if the method you'll be calling won't be chosen until execution time, which is the case when another argument is dynamic.
I know the compiler can't convert an async lambda expression to an expression tree, but is it possible to generate the expression tree manually ?
var expr = Expression.Lambda<Func<Task>>(
// how do I use 'await' in the body here?
);
var func = expr.Compile();
I can't find any method related to async or await in the Expression class, but perhaps there's another way?
await involves significant compiler re-writing; the generated IL is quite dissimilar to the original C#, with variable hoisting (onto a class) and branching, tasks, continuations, etc. It certainly isn't something that can be represented in a simple lambda, although in recent versions of .NET with richer Expression support (Expression.Block etc), technically I suppose it is probably possible to mimic most of the things the compiler does for await - but you'd probably go quite loopy trying to do it by hand.
No, AFAIK, no facility to automate this translation exists in the Expression API, and frankly I wouldn't ever expect there to be.
The same probably could be say of ILGenerator; frankly, AFAIK the only "easy" way (and I use the word "easy" quite incorrectly) to use await in meta-programming would be to generate C# and run it through roslyn or CSharpCodeProvider.
Late to the party, but I just published a lib which does accomplish this.
https://github.com/avonwyss/bsn.AsyncLambdaExpression
It leverages the fact that nested Lambdas in an Expression Tree can access the outer lambda variables (which are captured as closure, just like nested lambdas behave in C#). Thus the code creates a main lambda containing all the necessary variables and returning a Task, and a state machine in a nested lambda which is callable as continuation on awaiter.OnComplete(). Since the state, current awaiter etc. is stored in the closure, the state machine keeps its state even when the inner lambda stops execution.
The API to use it consists of two extension methods, one for awaiting and one for converting the Expression Tree with await to a lambda implementing the state machine and returning the Task.
Here's a code sample:
// Build a normal expression tree with "await" calls
var paraInput = Expression.Parameter(typeof(string), "input");
var exprTree = Expression.Lambda<Func<Task<string>, string>>(
Expression.Block(
Expression.Call(typeof(Task), nameof(Task.Delay), null, Expression.Constant(1000)).Await(false),
paraInput.Await(false)),
paraInput);
// Create compilable state machine async expression tree (result must be Task<?> or Task)
var asyncExprTree = exprTree.Async<Func<Task<string>, Task<string>>>();
var asyncCompiled = asyncExprTree.Compile();
// Invoke delegate as usual
var result = await asyncCompiled(Task.FromResult("test")).ConfigureAwait(false);
The DebugView of the original lambda Expression Tree is like this:
.Lambda #Lambda1<System.Func`2[System.Threading.Tasks.Task`1[System.String],System.String]>(System.Threading.Tasks.Task`1[System.String] $input)
{
.Block() {
.Call bsn.AsyncLambdaExpression.AsyncExpressionExtensions.AwaitVoid(.Call (.Call System.Threading.Tasks.Task.Delay(1000)).ConfigureAwait(False));
.Call bsn.AsyncLambdaExpression.AsyncExpressionExtensions.AwaitResult(.Call $input.ConfigureAwait(False))
}
}
The async conversion creates the following Expression Tree:
.Lambda #Lambda1<System.Func`2[System.Threading.Tasks.Task`1[System.String],System.Threading.Tasks.Task`1[System.String]]>(System.Threading.Tasks.Task`1[System.String] $input)
{
.Block(
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter $awaiter,
System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[System.String] $awaiter,
System.Int32 $state,
System.Int32 $resumeState,
System.Threading.Tasks.TaskCompletionSource`1[System.String] $taskCompletionSource,
System.Action $continuation,
System.Exception $exception) {
$taskCompletionSource = .New System.Threading.Tasks.TaskCompletionSource`1[System.String](.Constant<System.Threading.Tasks.TaskCreationOptions>(RunContinuationsAsynchronously));
$continuation = .Lambda #Lambda2<System.Action>;
.Invoke $continuation();
$taskCompletionSource.Task
}
}
.Lambda #Lambda2<System.Action>() {
.Try {
.Loop {
.Switch ($state) {
.Case (0):
.Block() {
$state = 1;
.If (
!($awaiter = .Call (.Call (.Call System.Threading.Tasks.Task.Delay(50)).ConfigureAwait(False)).GetAwaiter()).IsCompleted
) {
.Block() {
.Call $awaiter.OnCompleted($continuation);
.Break :break { }
}
} .Else {
.Default(System.Void)
}
}
.Case (1):
.Block() {
$state = 2;
.Call $awaiter.GetResult();
.If (
!($awaiter = .Call (.Call $input.ConfigureAwait(False)).GetAwaiter()).IsCompleted
) {
.Block() {
.Call $awaiter.OnCompleted($continuation);
.Break :break { }
}
} .Else {
.Default(System.Void)
}
}
.Case (2):
.Block(System.String $result:2) {
$result:2 = .Call $awaiter.GetResult();
.Call $taskCompletionSource.SetResult($result:2);
.Break :break { }
}
.Default:
.Throw .New System.InvalidOperationException()
}
}
.LabelTarget :break:
} .Catch (System.Exception $ex) {
.Call $taskCompletionSource.SetException($ex)
}
}
This is a simple example; but the async conversion can not only handle await, but also the control flow expressions (loop, conditional, label/goto, try..catch..finally) in order to offer support for a wide variety of Expression Trees.
I'm trying to create a compiled expression delegate to call a constructor taking a single parameter, I'm receiving the following exception:
Additional information: variable 'value' of type 'MyType' referenced from scope '', but it is not defined
The code is as follows:
var constructorInfo = instanceType.GetConstructors().Skip(1).First();
ParameterExpression param = Expression.Parameter(genericArgument, "value");
Delegate constructorDelegate = Expression.Lambda(Expression.New(constructorInfo, new Expression[] { param })).Compile();
I believe I'm receving the exception because the parameter 'value' is not scoped inside an Expression.Block.
How do I scope the parameter & constructor expressions inside an Expression.Block?
In order to declare the parameter value, you need to also specify it when creating the Lambda expression (see this overload of the Expression.Lambda method). Up to now, you only create a parameterized lambda expression, but do not declare the parameters that are used in the expression. Changing your code should solve the issue:
var lambdaExpr = Expression.Lambda(Expression.New(constructorInfo,
new Expression[] { param }),
param);
Delegate constructorDelegate = lambdaExpr.Compile();
I'm attempting to write an expression tree which can allow dynamic use of a StartsWith() method on non-string valued columns using Entity Framework.
E.g. IntegerValuedColumn.StartsWith(5) would return 500, 5000, 555, 5123, etc
I am trying to write an expression tree based on this answer:
How do I query an integer column for "starts with" in Entity Framework?
Here is what I have so far:
MethodInfo stringConvert = typeof(SqlFunctions).GetMethod("StringConvert", new[] { typeof(double?) });
Expression castExpression = Expression.Convert(propertyExpression, typeof(double?));
Expression convertExpression = Expression.Call(null, stringConvert, castExpression);
MethodInfo trimStart = typeof(string).GetMethod("TrimStart");
Expression nullExpression = Expression.Constant(null, typeof(char[]));
Expression trimExpression = Expression.Call(convertExpression, trimStart, nullExpression);
MethodInfo startsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
Expression methodExpression = Expression.Call(trimExpression, startsWith, constantExpression);
return methodExpression;
When I compile and run this expression, I get the following exception:
The method 'System.String TrimStart(Char[])' is only supported in LINQ to Entities when there are no trim characters specified as arguments.
In the original example, the expression is:
SqlFunctions.StringConvert((double)x.AccountNumber)
.TrimStart().StartsWith(searchTerm)
But what I get comes out as:
StringConvert(Convert(x.AccountNumber)).TrimStart(null).StartsWith(searchTerm)
I have removed the two lines dealing with TrimStart (nullExpression and trimExpression) and verified that the statement runs (excluding the thought that the error is being caused by the different language use). My theory based on the exception message is that the TrimStart() method wants to be called with zero parameters, but when I try that the Expression builder tells me that the incorrect number of parameters have been passed in so I figure I'm just missing something.
How would I go about calling the TrimStart method like TrimStart() instead of TrimStart(null) or TrimStart(new char[0]) using expression trees?
I have the following method:
public IEnumerable<OrderLines> RetrieveOrderLines(Expression<Func<OrderLines,
bool>> expression)
{
return _context.OrderLines.Where(expression);
}
This method is used from a test project (C#):
var testLine = repo.RetrieveOrderLines(x => x.I_ID == 1
&& x.T_ID == 2).Single();
And our production project which is VB.Net also accesses this method like this:
Dim line As OrderLines = repo.RetrieveConsumptionLines(Function(x) x.I_ID = InvId
And x.T_ID = tId).Single()
The C# test is working fine, I am not getting any error or warning, but the line in VB is highlighted and re-sharper is giving me the following warning:
runtime errors might occur when converting lambda expression to parameter
type system.linq.expressions.expression(Of System.Func(Of OrderLines, Boolean))
any idea why and is there are solution or a better way of doing this? I am new to Lambda expressions so any advice is appreciated.
Thanks.