Calling parameterised constructor using compiled expression - c#

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();

Related

Get MethodInfo of a function under a variable in C#

I have a specific object in C#, call it MyCustomObject. MyCustomObject is of type MyNamespace.CustomObject, and every object of that type contains a method MyCustomMethod. I am trying to get the MethodInfo (System.Reflection.MethodInfo) of MyCustomObject.MyCustomMethod so I can use it to create an expression tree later. However, if I just use typeof(MyCustomObject).GetMethod("MyMethodInfo"), it returns a general method for all objects of type MyNamespace.CustomObject. How can I get the MethodInfo of just MyCustomObject.MyCustomMethod?
When creating your expression tree (per this comment), you presumably want to use the Call factory method.
In your case, you're trying to create an expression tree representing an instance method call, not a static method call; the difference between them is that an instance method call uses an instance, while a static method call does not.
To create such an expression tree, you'll need some kind of expression tree that represents the instance; it might be a property or field, or the result of another method call. But if you want to apply it to an existing instance, you could pass your instance into the Constant factory method.
You could pass this node into one of the Call overloads which represent an instance method call, such as this one, for an instance method call with no arguments.
Something like this:
// using System.Linq.Expressions.Expression
CustomObject MyCustomObject = /* initialized somehow */
var methodInfo = typeof(CustomObject).GetMethod("MyCustomMethod");
var expr = Lambda(
Call(
Constant(MyCustomObject),
methodInfo
),
new ParameterExpression[] { } // if the LambdaExpression has parameters, add them here
);
Addendum
When using the compiler to generate a similar expression tree:
CustomObject MyCustomObject = /* initialized somehow */
Expression<Action> expr = () => MyCustomObject.MyCustomMethod();
MyCustomObject isn't represented with a ConstantExpression, but rather with a MemberAccessExpression. The C# compiler rewrites closed-over variables (in this case, MyCustomObject within the lambda expression) as a property access on a compiler-generated object. Instead of the call to Constant, the corresponding factory methods to represent MyCustomObject would look something like this:
// using System.Linq.Expressions.Expression
PropertyOrField(
Constant(<<closure_object>>),
"MyCustomObject"
)
We can't write something like this in code, because our code doesn't have access to the <<closure_object>> instance.

Reflection .NET Core Create Lambda Expression

I'm trying to create a lambda expression (using Reflection) which looks like this
IServiceProvider provider => provider.GetService<TDbContext>()
Or, to be more specific, as GetService is an extension method :
provider => ServiceProviderServiceExtensions.GetService<TDbContext>(provider)
This is my code:
var methodInfo = typeof(ServiceProviderServiceExtensions).
GetTypeInfo().
GetMethod("GetService").
MakeGenericMethod(typeof(TDbContext));
var lambdaExpression = Expression.Lambda(
Expression.Call(methodInfo, Expression.Parameter(typeof(IServiceProvider), "provider")),
Expression.Parameter(typeof(IServiceProvider), "provider")
);
var compiledLambdaExpression = lambdaExpression.Compile();
I'm getting this error
An exception of type 'System.InvalidOperationException' occurred in
System.Linq.Expressions.dll but was not handled in user code
Additional information: variable 'provider' of type
'System.IServiceProvider' referenced from scope '', but it is not
defined
You've created two different parameters with the same name. You should call Expression.Parameter just once and save the result and then use it:
var methodInfo = typeof(ServiceProviderServiceExtensions).
GetTypeInfo().
GetMethod("GetService").
MakeGenericMethod(typeof(TDbContext));
var providerParam = Expression.Parameter(typeof(IServiceProvider), "provider");
var lambdaExpression = Expression.Lambda(
Expression.Call( methodInfo, providerParam ),
providerParam
);
var compiledLambdaExpression = lambdaExpression.Compile();

How to convert PropertyInfo to property expression and use it to invoke generic method?

How to convert PropertyInfo to property expression which can be used to invoke StructuralTypeConfiguration<TStructuralType>.Ignore<TProperty>(Expression<Func<TStructuralType, TProperty>> propertyExpression) method?
I tried to use Expression.Property() to construct expression but I am getting following error when I use this expression as propertyExpression parameter:
The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly.
This error probably refers to TProperty type parameter which I don't know how to specify having only PropertyInfo.
I am doing this in relation to: Use Entity Framework's StructuralTypeConfiguration.Ignore() to Ignore all properties but specified set.
UPDATE
Code which is not working:
var propertyInfo = typeof(Foo).GetProperties()[0];
var expression = Expression.Default(typeof(Foo));
var expressionProperty = Expression.Property(expression, propertyInfo);
Ignore(expressionProperty);
var entityType = propertyInfo.DeclaringType;
var parameter = Expression.Parameter(entityType, "entity");
var property = Expression.Property(parameter, propertyInfo);
var funcType = typeof(Func<,>).MakeGenericType(entityType, propertyInfo.PropertyType);
var lambda = Expression.Lambda(funcType, property, parameter);
structureConfiguration.GetType()
.GetMethod("Ignore")
.MakeGenericMethod(propertyInfo.PropertyType)
.Invoke(structureConfiguration, new[]{lambda});
Property expressions require the property access to be on a specific object. There's a few options you can take here. First, if this is being done within one of your entity objects, you can simple use a ConstantExpression to build the property expression:
// Already have PropertyInfo in propInfo
Expression.Property(Expression.Constant(this, this.GetType()), propInfo)
However, since you need a Expression<Func<TStructuralType, TProperty>>, then it seems like you're going to have to build it using a ParameterExpression:
ParameterExpression pe = Parameter.Expression(typeof(MyEntity), "eParam");
Expression propExp = Expression.Property(pe, propInfo);
HOWEVER, here's the kicker... This is just a MemberExpression. To convert to the expression you need, you need to use Expression.Lambda to get a Func<> expression of the type you need. The problem? You don't know the type of the property to define the generic parameters of the lambda expression!
Expression<Func<MyEntity, ????>> eFunc = Expression.Lambda<Func<MyEntity, ????>>(propExp, pe);
This is the crux of the problem of doing it this way. That's not to say it can't be done... It's just that using this method IN THIS WAY isn't going to work. You'll have to use a bit runtime and static typing trickery (as well as judicious use of Actions instead of Funcs) to get this to work correctly.
TProperty exists only in the c# source code text. The compiler always resolves it to a concrete type. If you have a method
void Test<T>(T arg)
{
}
and call it like this
Test("hello");
Test(3);
The compiler generates code for two methods!
void Test(string arg)
{
}
void Test(int arg)
{
}
This means that you have to supply concrete types for your generic parameters if you want to have an invokable method.
This code will get you an Expression<Func<>> of the desired type. Note that there is an Expression.Lambda(...) override that doesn't need you to specify the type of the Func returned.
var t = typeof(Foo);
var pi = t.GetProperty(...);
var prm = Expression.Parameter(t, t.Name);
var prx = Expression.Property(prm, pi);
var lambda = Expression.Lambda(prx, prm);
Note that in many cases you don't have to bother with creating the Expression<Func<>> this way, assuming structureConfiguration below is a StructureConfiguration<Foo>, type inference will allow you to write something like this:
structureConfiguration.Ignore(f => f.Bar);

Expressions call constructor with a parameter and set its value

i am trying to call an parameterized constructor from an expression instead of using the default ctor. this is the code that gets the constructor parameter(s):
ConstructorInfo ci = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.HasThis, new[] { typeof(bool) }, new ParameterModifier[] { });
ParameterInfo[] paramsInfo = ci.GetParameters();
//create a single param of type object[]
ParameterExpression param = Expression.Parameter(typeof(bool), "il");
Expression[] argsExp = new Expression[paramsInfo.Length];
//pick each arg from the params array
//and create a typed expression of them
for (int i = 0; i < paramsInfo.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;
Expression paramAccessorExp = param;
//Expression.ArrayIndex(param, index);
Expression paramCastExp =
Expression.Convert(paramAccessorExp, paramType);
argsExp[i] = param;
}
NewExpression ci2 = Expression.New(ci, argsExp);
But if i try to compile the lambda expression i am getting the following error:
variable 'il' of type 'System.Boolean' referenced from scope '', but it is not defined"
What am i missing? Any help and/ or hint is appreciated.
You define a parameter called li in the 4th line of your code. In order to use this in a lambda expression, you need to have a scope in which this parameter is defined. You have two choices:
Create a BlockExpression that contains param as a local variable. Then use this expression as the body of your lambda expression.
Use param as a parameter in your LambdaExpression.
If you use option 1, you'll also have to initialize the variable. Otherwise you'll get a different kind of error message.
EDIT
There are two problems with the additional code you posted:
You need to use the same parameter object throughout your expression tree. Having the same name and type does not make two Parameter objects equal. I would simply move everything up to and including creating the lambda to the ConvertThis method so you can reuse the param variable. You can then just compile the return value of ConvertThis to get your delegate.
When creating the BlockExpression, you need to pass param in as a local variable. You do this by adding an argument, new ParameterExpression[] { param } to the method.

How do I get a value of a reference type in an Expression?

I have this method:
public void DoSomething<T>(Expression<Func<T, object>> method)
{
}
If this method is called like this:
DoSomething(c => c.SomeMethod(new TestObject()));
... how do I get the value of the parameter that was passed into SomeMethod()?
If the parameter is a value type, this works:
var methodCall = (MethodCallExpression)method.Body;
var parameterValue = ((ConstantExpression)methodCall.Arguments[0]).Value;
However, when I pass in a reference type, methodCall.Arguments[0] is a MemberExpression, and I can't seem to figure out how to write code to get the value out of it.
Here is the answer (inspired by Akash's answer):
LambdaExpression lambda = Expression.Lambda(methodCall.Arguments[0]);
var compiledExpression = lambda.Compile();
return compiledExpression.DynamicInvoke();
You will have to evaluate member expression manually, MemberExpression contains "Expression" which is the container object of Member specified, to do this you can crete Lamda of your arguement and compile and execute it.
LamdaExpression l = Expression.Lambda(methodCall.Arguments[0]);
var c = l.Compile();
var v = c.Invoke();
So whatever you pass, you will get it in "v" variable.
This isn't really a matter of value type or reference type - it's a matter of a constant expression or not-constant expression. I'm sure your existing code would fail if you called
DoSomething(c => c.SomeMethod(DateTime.Now));
as well.
Basically, the argument to the method is just an expression. It's not a value. You could potentially compile that expression and then execute it to get the value at that point in time, but it's important to understand that an expression isn't a value in itself. In the case of a constant expression it's easy, but taking the DateTime.Now example, by definition the evaluated value of the expression changes over time :)
What are you trying to do with the argument? What's the bigger picture here?
Firstly, what Jon said.
There are no values to get ahold of, it's all just expressions. What might be of interest is the NewExpression, which has a Constructor property; this property contains the reflected constructor that would be called if you compiled expression and ran the resulting delegate. You could manually invoke that constructor and get an instance of what the user was intending on instantiating.

Categories