I am trying to create the following dynamically, however I am having problems calling the extension method FirstOrDefault:
using(var context = new Entities())
{
var list = context.Engines.Include("Cars").Select(e => e.Cars.FirstOrDefault()).ToList();
}
I have the following
Expression parameter = Expression.Parameter(typeof(Engine), "e");
Expression property = Expression.Property(parameter, "Cars");
parameter = {e}
property = {e.Cars}
Those are good, but I am encountering a problem when I try and call the FirstOrDefault method:
var result = Expression.Call(typeof(Queryable), "FirstOrDefault", new type[] { typeof(Car)}, property);
I would like to get
result = {e.Cars.FirstOrDefault()}
but I am getting an InvalidOperationException
No generic method 'FirstOrDefault' 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.
Any help would be much appreciated.
Are you sure e.Cars is an IQueryable<T>?
If not, you can't pass it to Queryable.FirstOrDefault<T>(IQueryable<T>).
If it's an IEnumerable<T>, change your code to call Enumerable.FirstOrDefault<T>(IEnumerable<T>):
var result =
Expression.Call(
typeof(Enumerable),
"FirstOrDefault",
new Type[] { TypeSystem.GetElementType(property.Type) },
property);
Related
I am trying to return the contents of a Where filtered table of dynamic type based on a dynamic field name from an array of values.
Heres what I have so far:
public JsonResult GetRelationShips(string linkingTable, string idField, int[] ids)
{
var tableType = typeof(context).GetProperty(linkingTable);
var entityTable = tableType.GetValue(db) as IQueryable;
var method = typeof(List<int>).GetMethod("Contains");
var eParam = Expression.Parameter(tableType.PropertyType.GetGenericArguments()[0]);
var call = Expression.Call(Expression.Constant(ids.ToList()), method, Expression.Property(eParam, idField));
var func = typeof(Func<,>);
var genericFunc = func.MakeGenericType(tableType.PropertyType.GetGenericArguments()[0], typeof(bool));
var lambda = Expression.Lambda(genericFunc, call, eParam);
var results = typeof(System.Linq.Enumerable).GetMethods().Where(x => x.Name == "Where").First().Invoke(db, new object[] { lambda });
return Json(results);
}
That last line is giving me an error:
Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Honestly, I cobbled this together this afternoon from snippets from all over the internet. I have no idea what I'm doing here, this is new to me and I'm keen to learn. Just trying to avoid SQL injection, the rest of the project is entirely Linq so I'm soldiering on. I'm learning Generic types too, got my head around that, don't see how I can use them here though.
There any many flaws in this single line of code:
var results = typeof(System.Linq.Enumerable).GetMethods().Where(x => x.Name == "Where").First().Invoke(db, new object[] { lambda });
Trying to call Enumerable.Where instead of Queryable.Where. This would cause retrieving the whole table data and performing the filtering in memory instead of the database side.
Trying to call potentially wrong method. Where has 2 overloads, and it's undefined which one will be returned as first by the reflection.
Trying to invoke generic method definition, causing the exception you are getting. You have to first construct a generic method by using MakeGenericMethod and invoke that.
Trying to invoke static generic extension method via reflection as if it is instance method. Instead, you should pass null as first argument to Invoke and pass new object[] { entityTable, lambda } as second argument.
You can avoid all these traps by simply using the C# dynamic method dispatch:
IQueryable results = Queryable.Where((dynamic)entityTable, (dynamic)lambda);
The whole code can be simplified by using the following Expression.Call overload:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments);
which is very useful for "calling" static generic extension methods:
var query = (IQueryable)db.GetType().GetProperty(linkingTable).GetValue(db);
// e =>
var entity = Expression.Parameter(query.ElementType, "e");
// ids.Contains(e.idField)
// = Enumerable<int>.Contains(ids, e.idField)
var containsCall = Expression.Call(
typeof(Enumerable),
nameof(Enumerable.Contains),
new Type[] { typeof(int) },
Expression.Constant(ids),
Expression.Property(entity, idField)
);
// e => ids.Contains(e.idField)
var predicate = Expression.Lambda(containsCall, entity);
// query = query.Where(predicate);
query = Queryable.Where((dynamic)query, (dynamic)predicate);
You can also avoid the dynamic Where call and use similar Expression.Call based approach to "call" it, combined with IQueryProvider.CreateQuery:
// query.Where(predicate)
// = Queryable.Where<ElementType>(query, predicate)
var whereCall = Expression.Call(
typeof(Queryable),
nameof(Queryable.Where),
new Type[] { query.ElementType },
query.Expression,
predicate
);
// query = query.Where(predicate)
query = query.Provider.CreateQuery(whereCall);
I've provided all that just because you said you are keen to learn. The simplest way of handling such tasks (and not reinvent the wheel) is to use some 3rd party package. For instance, with System.Linq.Dynamic package the whole code would be:
var query = ((IQueryable)db.GetType().GetProperty(linkingTable).GetValue(db))
.Where($"#0.Contains({idField})", ids);
I want create expression with reflection in c#.
I am target script is:
using (var service = new MyProxy<IProductsService>())
{
var response = service.DoAction<Response<List<Product>>>(srv => srv.GetProducts());
}
How to (srv => srv.GetProducts() script is generate with expression tree?
Edit:
I am sorry for the wrong expression.
wherein IProductsService, Response<List<Product>> and GetProducts are actually unknown types. I take generic on runtime.
I wrote the following methods(CreateExpression) to try. But Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter); line gives the following error:
No method 'GetProducts' exists on type
'ServiceFoundation.Response1[System.Collections.Generic.List1[ServiceFoundation.Products.Product]]'.
Next in line for I have not tested it yet.
Method:
private void CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
{
ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter);
Type expressionType = typeof(Expression<>);
Type lambdaType = typeof(LambdaExpression);
Type funcType = typeof(Func<,>);
Type delegateType = funcType.MakeGenericType(interfaceType, methodReturnType);
Type expression = expressionType.MakeGenericType(delegateType);
MethodInfo mI = typeof(Expression).GetMethod("Lambda");
MethodInfo lambda = mI.MakeGenericMethod(delegateType);
var ex = lambda.Invoke(this, new object[] { delegateType, property, parameter });
}
Hopefully I can express.
If you have an
Expression<Action<Response>>
you can call
.Compile()
on it and it returns an
Action<Response>
which you can then invoke normally.
eg
Expression<Action<Response>> exp = resp => Console.WriteLine(resp);
Action<Response> func = exp.Compile();
func(myResponse);
However, if that's all you need to do, you might find it simpler not to use expressions at all;
Action<Response> func = resp => Console.WriteLine(resp);
func(myResponse);
I succeeded after a long struggle. I examined different questions here.
and in the end I solved this way:
private Expression CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
{
ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
Expression callExpression = Expression.Call(parameter, methodName.Name,null, null);
Type expressionType = typeof(Expression<>);
Type lambdaType = typeof(LambdaExpression);
Type funcType = typeof(Func<,>).MakeGenericType(interfaceType, methodReturnType);
Type expressionGenericType = expressionType.MakeGenericType(funcType);
string methodSignature = "System.Linq.Expressions.LambdaExpression Lambda(System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])";
var lambdaMethod = typeof(Expression).GetMethods()
.Single(mi => mi.ToString() == methodSignature);
Expression lambdaExpression = (Expression)lambdaMethod.Invoke(this, new object[] { callExpression, new ParameterExpression[] { parameter } });
return lambdaExpression;
}
Using the Expression Tree API I want to generate code for code that looks like this:
FieldInfo enumFieldInfo = enumFieldInfoSet.SingleOrDefault(fieldInfo => fieldInfo.Name == enumName);
I have written this code but it's not working:
var enumerableType = typeof(Enumerable);
var enumerableGenericType = typeof(IEnumerable<>).MakeGenericType(typeof(FieldInfo));
var predicateGenericType = typeof(Func<>).MakeGenericType(typeof(Func<FieldInfo, bool>));
ParameterExpression fieldInfoSource = Expression.Parameter(enumerableGenericType, "fieldInfoSource");
ParameterExpression predicateSource = Expression.Parameter(predicateGenericType, "funcPredicateOnFieldInfo");
var arrayOfTypes = new Type[] { enumerableGenericType, predicateGenericType };
MethodCallExpression SingleOrDefaultMethodCall = Expression.Call(enumerableType, "SingleOrDefault",arrayOfTypes, fieldInfoSource, predicateSource);
Here is the runtime error: No generic method 'SingleOrDefault' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
I have tried a number of combinations of type coercion but still have not stumbled on the right combination. I know that the SingleOrDefault is an Extension method on the Enumerable class and that it needs two parameters; I've look at the code via the debugger and have written to code inspect its properties at run-time; what am I missing.
The problem is that you are using the Expression.Call overload which takes a type, and for a static method you need an overload with MethodInfo.
void Main()
{
Expression<Func<IEnumerable<FieldInfo>, Func<FieldInfo,bool>, FieldInfo>> singleOrDefaultExpr = (l,p) => l.SingleOrDefault(p);
var callSource = (MethodCallExpression)singleOrDefaultExpr.Body;
var method = callSource.Method;
var collectionParameter = Expression.Parameter(typeof(IEnumerable<FieldInfo>), "enumFieldInfoSet");
var enumNamePredicateParameter = Expression.Parameter(typeof(Func<FieldInfo,bool>), "enumNamePredicate");
var body = Expression.Call(method, collectionParameter, enumNamePredicateParameter);
var lambda = Expression.Lambda<Func<IEnumerable<FieldInfo>, Func<FieldInfo, bool>, FieldInfo>>(body, collectionParameter, enumNamePredicateParameter);
var f = lambda.Compile();
Console.WriteLine(f(typeof(Apple).GetFields(), fi => fi.Name == "color").Name);
}
class Apple
{
public string color;
}
Also, you can use another method to find the required MethodInfo:
var method = typeof(Enumerable)
.GetMethods()
.Single(m => m.Name == "SingleOrDefault" && m.GetParameters().Count() == 2)
.MakeGenericMethod(new[] {typeof(FieldInfo)});
UPDATE:
There is actually a simpler method, and you were on the right track, but your code had en error.
var collectionParameter = Expression.Parameter(typeof(IEnumerable<FieldInfo>), "enumFieldInfoSet");
var enumNamePredicateParameter = Expression.Parameter(typeof(Func<FieldInfo,bool>), "enumNamePredicate");
var body = Expression.Call(typeof(Enumerable), "SingleOrDefault", new[] { typeof(FieldInfo)}, collectionParameter, enumNamePredicateParameter);
var lambda = Expression.Lambda<Func<IEnumerable<FieldInfo>, Func<FieldInfo, bool>, FieldInfo>>(body, collectionParameter, enumNamePredicateParameter);
The problem was that SingleOrDefault has only one generic type parameter: 'FieldInfo' in this case:
SingleOrDefault<FieldInfo>(....
Don't mix it up with the method parameters, there are two of them:
SingleOrDefault<GenericParameter>(
this IEnumerable<GenericParameter> firstMethodParameter,
Func<GenericParameter, bool> secondMethodParameter
)
George did a great job in getting me on the right track and did provide me a partial answer; I re-fashion my code so that is was clear to me. This is an example of code that I wanted to machine generate in testing our Meta-programming runtime. I am providing more code than is necessary for the specific problem but I wanted the others to see the greater context.
// Code to Generate Enum Field Metadata ...
string enumName = Enum.GetName(theEnumType, theOrdinalEnumValue);
Array enumValues = Enum.GetValues(theEnumType);
object enumValue = enumValues.GetValue(theOrdinalEnumValue);
object enumObject = Enum.ToObject(theEnumType, theOrdinalEnumValue);
// Create Local variables of the types targeted for assignment expressions that we will make in the generated code
var enumVariables = Expression.RuntimeVariables(Expression.Variable(typeof(string), "gcEnumName"),
Expression.Variable(typeof(Array), "gcEnumValues"),
Expression.Variable(typeof(object), "gcEnumValue"),
Expression.Variable(theEnumType, "gcEnumObject"),
Expression.Variable(typeof(FieldInfo), "gcFieldInfoOnEnum"));
// Setup the Input Parameters for calls into Enum and Array Types in preperation for Assignments
ParameterExpression theInputOfEnumType = Expression.Variable(typeof(Type), "theInputOfEnumType");
ParameterExpression theEnumFieldNameValue = Expression.Variable(typeof(string), "theEnumFieldNameValue");
ParameterExpression aEnumObjectIndexValue = Expression.Variable(typeof(int), "aEnumObjectIndexValue");
ParameterExpression anArrayOfEnumObjects = Expression.Variable(typeof(Array), "anArrayOfEnumObjects");
ParameterExpression anEnumerableObject = Expression.Variable(typeof(Enumerable), "anEnumerableObject");
ParameterExpression directEnumTypeResolved = Expression.Variable(theEnumType, "directEnumTypeResolved");
ParameterExpression fieldInfoOnEnum = Expression.Variable(typeof(FieldInfo), "fieldInfoOnEnum");
var fieldInfoEnumerableRepresentation = typeof(Enumerable);
In perpetration of making calls to "Expression.Call" we need to get some MethodInfo data
// We need to generate MethodInfo on the Methods we want to call in the generated code. This is metadata
// we need to call the Expression.Call API.
MethodInfo enumGetNameMethodInfo = enumMetadata.GetMethods().FirstOrDefault(info => info.Name == "GetName");
MethodInfo enumGetValuesMethodInfo = enumMetadata.GetMethods().FirstOrDefault(info => info.Name == "GetValues");
MethodInfo enumGetValueMethodInfo = arraryMetadata.GetMethods()
.FirstOrDefault(methodInfo => (methodInfo.Name == "GetValue") && methodInfo.GetParameters().Any(param =>param.ParameterType == typeof(int)));
MethodInfo enumToObjectMethodInfo = enumMetadata.GetMethods()
.FirstOrDefault(info => info.Name == "ToObject");
// We know that there exist a number of polymorphic instances of the "SingleOrDefault" Extension method
// so we use the name of the parameter named "predicate" to locate our method. **FYI Can't use the MethodInfo data in a call to Expression.Call - It's a generic definition**.
MethodInfo enumerableSingleOrDefaultInfo = fieldInfoEnumerableRepresentation.GetMethods()
.FirstOrDefault(methodInfo => (methodInfo.Name == "SingleOrDefault") &&
methodInfo.GetParameters()
.Any(param => param.Name == "predicate"));
Here is the final code that works in making a call to a Generic Method:
// An approach to setup a Generic method call that will be used in an Expression.Assign call
// I decompose this code so for debugging purposes
// I create the "inputOfTSourceType" as a Generic Type because in the Definition of "SingleOrDefault" method there is only one Generic Parameter;
// also, take special note that in the assemblage of Expression.Call methods any of the methods that take a MethodInfo instance you can't pass in
// a "Generic" method definition. If you take a look at the code that is called it will throw and exception if it detects the IsGenericMethodDefinition
// flag is true.
var inputOfTSourceType = typeof(IEnumerable<>).MakeGenericType(typeof(FieldInfo)); // This is the type on the "source" TSource parameter
var predicateOfFuncType = typeof(Func<FieldInfo, bool>); // This is the type on the "predicate" parameter
// Take note there that we only define one(1) type here ("inputParameterType"); this is the type we wish apply to the Generic Type TSource
// declaration: public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
var inputParameterType = new[] {typeof(FieldInfo)}; // This is the type we must match to "source" parameter
// The "SingleOrDefault" method can take two parameters and in this case I need to pass in a lambda expression as the predicate.
// Se we need two parameters.
ParameterExpression fieldInfoSource = Expression.Parameter(inputOfTSourceType, "fieldInfoSource"); // This is: this IEnumerable<TSource>
ParameterExpression predicateSource = Expression.Parameter(predicateOfFuncType, "funcPredicateOnFieldInfo"); // This is: Func<TSource, bool> predicate
MethodCallExpression SingleOrDefaultMethodCall =
Expression.Call(fieldInfoEnumerableRepresentation, // This is the Object Instance for which the
"SingleOrDefault", // The Name of the Generic Method
inputParameterType, // The Generic Type
fieldInfoSource, // this is the "this" source parameter
predicateSource); // this the "predicate" parameter
Expression localEnumNameAssignment =
Expression.Assign(enumVariables.Variables[0], EnumGetNameMethodCall);
Expression localEnumObjectsAssignment =
Expression.Assign(enumVariables.Variables[1], EnumGetValauesMethodCall);
Expression localEnumObjectAssignment =
Expression.Assign(enumVariables.Variables[2], ArrayGetValueMethodCall);
Expression localEnumTypeAssignment =
Expression.Assign(enumVariables.Variables[3], Expression.Convert(EnumToObjectMethodCall, theEnumType));
Expression localFieldInfoAssignment =
Expression.Assign(enumVariables.Variables[4], Expression.Convert(SingleOrDefaultMethodCall, typeof(FieldInfo)));
BlockExpression blockExpression =
Expression.Block(enumVariables,
localEnumNameAssignment,
localEnumObjectsAssignment,
localEnumObjectAssignment,
localEnumTypeAssignment,
localFieldInfoAssignment,
enumTypeToReturn);
Here's the generated code:
$gcEnumName = .Call System.Enum.GetName(
$theInputOfEnumType, $gcEnumName = (System.String)$theEnumFieldNameValue)
$gcEnumValues = .Call System.Enum.GetValues($theInputOfEnumType)
$gcEnumValue = .Call $anArrayOfEnumObjects.GetValue((System.Int32) $theOrdinalEnumValue)
$gcEnumObject = (WorkflowMessagingCommands).Call System.Enum.ToObject(
$theInputOfEnumType,
(System.Object)$theEnumInstance)
// Here is the generated code to call "SignleOrDefault"
$gcFieldInfoOnEnum = .Call System.Linq.Enumerable.SingleOrDefault(
$thisSourceType,
$predciateType)
I have a table that's mapped, but after compile additional columns can be added or removed from the table. I'm trying to come up with a linq query that will take those new columns into account. In this scenario, I want to order by one of those dynamic columns. This is what I have so far.
var queryableData = dc.wf_task_ext_attributes.AsQueryable();
ParameterExpression pe = Expression.Parameter(typeof(DateTime), "ExtValue105");
// The next line is where it fails
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<DateTime, DateTime>>(pe, new ParameterExpression[] { pe }));
IQueryable<string> results = queryableData.Provider.CreateQuery<string>
(orderByCallExpression);
It's failing with the following message:
No generic method 'OrderBy' 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.
What am I doing wrong?
Is queryableData of type IQueryable<DateTime>? Seems not to be since you are calling CreateQuery<string>.
Your call to Expression.Call seems to assume that this is an IQueryable<DateTime>. Make sure that it is.
You can find out how to correctly build a LINQ query by hard-coding the query and then decompiling the resulting assembly.
Your code tries to create something like Queryable.OrderBy(queryableData.Expression, ExtValue105 => ExtValue105). I have no idea why would you expect that to work.
If I understand your question correctly, you need to dynamically create an expression like attribute => attribute.ExtValue105 and then you can use that to call OrderBy().
The code could look something like this (assuming queryableData is IQueryable<Attribute>):
var parameter = Expression.Parameter(typeof(Attribute), "attribute");
var property = Expression.Property(parameter, "ExtValue105");
var lambda = Expression.Lambda(property, parameter);
IQueryable<Attribute> results =
Queryable.OrderBy(queryableData, (dynamic)lambda);
You could use queryableData.Provider.CreateQuery() manually to avoid the dynamic call, but that would be more complicated.
I have a list of PolicyTran objects:
List<PolicyTran> AllTransactions;
I need to run a query filtering by a property, e.g:
var insureds = AllTransactions.Select(x => x.Insured).ToList();
That works fine, but I need to pass the x.Insured property at runtime since that property could take different values.
I tried doing:
ParameterExpression x = Expression.Parameter(typeof (PolicyTran),"x");
MemberExpression body = Expression.Property(x, propertyName);
var lambda = Expression.Lambda(body,x).Compile();
var result = AllTransactions.Select(lambda).ToList();
In this case propertyName contains "Insured" or any other PolicyTran property.
But I get a compilation error saying that "The type arguments cannot be inferred by the ussage..."
I also tried, but no luck:
ParameterExpression x = Expression.Parameter(typeof (PolicyTran),"x");
var result = AllTransactions.Select(Expression.Lambda<Func<PolicyTran, bool>>(x).Compile()).ToList();
Any ideas??
Your first attempt is closer to the solution. You just need to call the generic version of Lambda:
var lambda = Expression.Lambda<Func<PolicyTran, object>>(body, x).Compile();
in order to get a Func<PolicyTran, object> delegate.
Otherwise the labda will return a simple System.Delegate from which the LINQ .Select will be unable to infer the types.