Can't use dynamic delegate parameter in lambda of expression tree - c#

I am trying to apply a simple "Where" clause on a dynamically selected table. However, the table field the clause will be applied to is also dynamic and I can't figure out how to make that part work. Getting the dynamic table works correctly.
using (var context = new DBEntities())
{
var type = context.GetType();
var tableProperty = type.GetProperty("tableName");
var tableGet = tableProperty.GetMethod;
var tableContent = tableGet.Invoke(context, null);
var tableQuery = (IQueryable)tableContent;
var tableType = tableQuery.ElementType;
var pe = Expression.Parameter(tableType, "tableType");
var left = Expression.PropertyOrField(pe, "fieldName");
var right = Expression.Constant("fieldValue");
var predicateBody = Expression.Equal(left, right);
var whereCallExpression = Expression.Call(typeof(Queryable), "Where", new[] { tableType },
tableQuery.Expression, Expression.Lambda<Func<tableType, bool>>(predicateBody, pe));
IQueryable<string> results = tableQuery.Provider.CreateQuery<string>(whereCallExpression);
}
This block of code will not compile because of Expression.Lambda<Func<tableType, bool>>(predicateBody, pe). If I hard-code types for the Expression-related code, this sample runs and returns the expected results.

The code does not compile because tableType, a variable of type System.Type, cannot be used as a type parameter of a generic function.
However, you should be able to make this compile and run by replacing a call to generic Lambda<Func<...>> with a call to non-generic Lambda:
var whereCallExpression = Expression.Call(typeof(Queryable), "Where", new[] { tableType },
tableQuery.Expression, Expression.Lambda(predicateBody, pe));

Related

Dynamically ordering by string

I've created an extension on IQueryable as I would like to order by nullable datetimes first then order by the datetime itself using just the string of the property i.e "activeTo". I've created the code below:
public static IQueryable<T> Sort<T>(this IQueryable<T> source, string sortBy)
{
//create the expression tree that represents the generic parameter to the predicate
var param = Expression.Parameter(typeof(T), "p");
//create an expression tree that represents the expression p=>p.SortField.HasValue
var prop = Expression.Property(param, sortBy);
var target = Expression.Constant(null, prop.Type);
var bin = Expression.Equal(prop, Expression.Convert(target, prop.Type));
var exp = Expression.Lambda(bin, param);
string method = "OrderBy";
Type[] types = new Type[] { source.ElementType, exp.Body.Type };
var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp);
//now do the ThenBy bit,sending in the above expression to the Expression.Call
exp = Expression.Lambda(prop, param);
types = new Type[] { source.ElementType, exp.Body.Type };
method = "ThenBy";
var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp);
return source.Provider.CreateQuery<T>(ThenByCallExpression);
}
This extension is called by:
query.Sort("activeTo");
Which then gives the below the response:
{
"title": "test 5",
"activeFrom": "2019-06-08T21:26:50.2833333",
"activeTo": "2019-06-08T21:26:50.2833333",
},
{
"title": "test 2",
"activeFrom": "2019-06-08T21:28:45.65",
"activeTo": null,
}
I'd expect the record with activeTo as null to be first however, this isn't the case.
Does anyone know what I'm doing wrong?
From the comments the goal seems to be to dynamically generate an expression which sorts null values to the front.
The current code produces the following expression OrderBy(p => p.activeTo == null).ThenBy(p => p.activeTo == null). This has two flaws:
It sorts null values to the front as the bools sort order is false, true (as their ordinal values are 0 and 1, respectively). Therefore a comparison to null first collects the false cases, and then the truecases.
The ThenBy repeats the OrderBy, but was actually intended to emit ThenBy(p => p.ActiveTo).
The first can be solved by either using Expression.NotEqual instead of Expression.Equal for p => p != p.activeTo or by using OrderByDescending instead of OrderBy.
In total the code should be:
public static IQueryable<T> Sort<T>(IQueryable<T> source, string sortBy)
{
//create the expression tree that represents the generic parameter to the predicate
var param = Expression.Parameter(typeof(T), "p");
//create an expression tree that represents the expression p=>p.SortField.HasValue
var prop = Expression.Property(param, sortBy);
var target = Expression.Constant(null, prop.Type);
// NotEqual, to sort nulls before not-nulls
var bin = Expression.NotEqual(prop, Expression.Convert(target, prop.Type));
var exp = Expression.Lambda(bin, param);
// OrderBy with the null comparison expression
string method = nameof(Queryable.OrderBy);
Type[] types = new Type[] { source.ElementType, exp.Body.Type };
var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp);
// ThenBy with the property expression
exp = Expression.Lambda(prop, param);
types = new Type[] { source.ElementType, exp.Body.Type };
method = nameof(Queryable.ThenBy);
var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp);
return source.Provider.CreateQuery<T>(ThenByCallExpression);
}
This yields the following expression:
OrderBy(p => p.activeTo != null).ThenBy(p => p.activeTo).
Remarks: It should be noted that usually OrderBy(p => p.activeTo) would already sort null values to the front, as this is the default sort order for strings, nullables, and so on. However, this behavior could be overwritten depending by the specific type and depend on the query source. Therefore, I left it like the OP has.

How to create an anonymous object as a LINQ Expression

I would like to create the following lambda expression using an Expression Tree in C#:
var result = dataList.GroupBy(x => new { x.Prop1, x.Prop2 })
How do I make the anonymous type with two properties as a LINQ Expression (lambdaExp)?
This is what I got so far:
IQueryable<GraphData> queryableData = graphDataList.AsQueryable();
ParameterExpression pe = Expression.Parameter(typeof(GraphData), "x");
Expression prop1 = Expression.PropertyOrField(pe, "Prop1");
Expression prop2 = Expression.PropertyOrField(pe, "Prop2");
var lambdaExp = Expression.Lambda<Func<GraphData, object>>( new { prop1, prop2 } , pe); //doesn't compile
MethodCallExpression groupByCallExpression = Expression.Call(
typeof(Queryable),
"GroupBy",
new Type[] { typeof(GraphData), typeof(object) },
queryableData.Expression,
lambdaExp);
IQueryable<GraphData> result = queryableData.Provider.CreateQuery<GraphData>(groupByCallExpression);
When you're writing linq queries and anonymous objects, the compiler is hiding a lot of the magic that it's doing for you. Specifically with anonymous objects, it is also creating new types for you. Unless that type exists somewhere, you will need to create the type manually and use that type in its place.
You can cheat a bit and have the compiler create an object with that type and save a reference to that type. With that, you can generate the necessary expressions to instantiate that object.
One thing to note is that the anonymous objects created for you will have constructors with the parameters in definition order so you just need to invoke that constructor.
var keyType = new { Prop1=default(string), Prop2=default(string) }.GetType();
var ctor = keyType.GetConstructor(new Type[] { typeof(string), typeof(string) });
var param = Expression.Parameter(typeof(GraphData), "x");
var keySelector = Expression.Lambda(
Expression.New(ctor,
Expression.PropertyOrField(param, "Prop1"), // corresponds to Prop1
Expression.PropertyOrField(param, "Prop2") // corresponds to Prop2
),
param
); // returns non-generic LambdaExpression
Do keep in mind that we are dealing with types not known at compile time, so your key selector expression will not have a compile time type.

Dynamically building an expression tree

I'm following this excellent example: Convert Linq to Sql Expression to Expression Tree
In my case I'm trying to build an expression tree where the type to be filtered is only known at run time, and is expressed as a string. In the above example the type Region is already known and typed directly:
ParameterExpression pe = Expression.Parameter(typeof(Region), "region");
In my application I've been able to rewrite this as:
ParameterExpression pe = Expression.Parameter(Type.GetType("mystring"), "listData");
My stumbling block is this from the example:
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { query.ElementType },
query.Expression,
Expression.Lambda<Func<Region, bool>>(e3, new ParameterExpression[] { pe }));
var results = query.Provider.CreateQuery<Region>(whereCallExpression);
In these two lines Region is directly typed. Is there a way of dynamically using a string "Region" to achieve the same thing?
Sure, but you'll have to understand the implications. The type name Region is the compile time type. With it, you can generate a strongly typed query. However, since you don't have the type at compile time, you can still generate a query, but it won't be strongly typed.
You can generate an lambda expression of unknown compile time type by using the non-generic overloads. Likewise with the CreateQuery() method.
Here's two versions of the same query which checks if some property value matches a given value. One is generic and the other is not.
The generic version implicitly takes the type from the type of the query.
public IQueryable<TSource> PropertyEqualsValue<TSource>(IQueryable<TSource> query,
string propertyName, object value)
{
var param = Expression.Parameter(typeof(TSource));
var body = Expression.Equal(
Expression.Property(param, propertyName),
Expression.Constant(value)
);
var expr = Expression.Call(
typeof(Queryable),
"Where",
new[] { typeof(TSource) },
query.Expression,
Expression.Lambda<Func<TSource, bool>>(body, param)
);
return query.Provider.CreateQuery<TSource>(expr);
}
var query = PropertyEqualsValue(SomeTable, "SomeColumn", "SomeValue");
While the other the type is taken from the provided typeName. Note that when the query is created, we cannot provide the type, since we don't know what the type is at compile time.
public IQueryable PropertyEqualsValue(IQueryable query,
Type type, string propertyName, object value)
{
var param = Expression.Parameter(type);
var body = Expression.Equal(
Expression.Property(param, propertyName),
Expression.Constant(value)
);
var expr = Expression.Call(
typeof(Queryable),
"Where",
new[] { type },
query.Expression,
Expression.Lambda(body, param)
);
return query.Provider.CreateQuery(expr);
}
var type = Type.GetType("Some.Type.Name");
var query = PropertyEqualsValue(SomeTable, type, "SomeColumn", "SomeValue");

How to make query without having generic type?

I'll start with piece of code:
var objectType = typeof(Department); // Department is entity class from linqdatacontext
using (var dataContext = new DataModel.ModelDataContext())
{
var entity = Expression.Parameter(objectType, "model");
var keyValue = Expression.Property(entity, "Id");
var pkValue = Expression.Constant(reader.Value);
var cond = Expression.Equal(keyValue, pkValue);
var table = dataContext.GetTable(objectType);
... // and here i don't how to proceed
}
I am not even sure if i am building that expression correctly. However simply put, i need to call dynamically SingleOrDefault() on that table to find entity by primary key. Every example i had found is using generic variant of GetTable<>(), but i cannot use that obviously. I am probably overlooking something...
Whenever I build expression trees, I like to start off with an example of what I'm building:
() => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == 1);
From that, we can easily dissect the target expression. You are partway there; you just need to include a call to the GetTable method in the expression tree and then build an outer lambda expression to call the whole thing:
using(var dataContext = new DataModel.ModelDataContext())
{
var getTableCall = Expression.Call(
Expression.Constant(dataContext),
"GetTable",
new[] { entityType });
var entity = Expression.Parameter(entityType, "entity");
var idCheck = Expression.Equal(
Expression.Property(entity, "Id"),
Expression.Constant(reader.Value));
var idCheckLambda = Expression.Lambda(idCheck, entity);
var singleOrDefaultCall = Expression.Call(
typeof(Queryable),
"SingleOrDefault",
new[] { entityType },
getTableCall,
Expression.Quote(idCheckLambda));
var singleOrDefaultLambda = Expression.Lambda<Func<object>>(
Expression.Convert(singleOrDefaultCall, typeof(object)));
var singleOrDefaultFunction = singleOrDefaultLambda.Compile();
return singleOrDefaultFunction();
}
We have to convert the SingleOrDefault call to have a return type of object so it can serve as the body of the Func<object> function.
(Untested)
Edit: Parameterizing the data context and value
Now we are building this function:
(dataContext, value) => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == value);
You would change the constants to parameters and add those parameters to the function you compile:
var dataContextParameter = Expression.Parameter(typeof(ModelDataContext), "dataContext");
var valueParameter = Expression.Parameter(typeof(object), "value");
var getTableCall = Expression.Call(
dataContextParameter,
"GetTable",
new[] { entityType });
var entity = Expression.Parameter(entityType, "entity");
var idCheck = Expression.Equal(
Expression.Property(entity, "Id"),
valueParameter);
var idCheckLambda = Expression.Lambda(idCheck, entity);
var singleOrDefaultCall = Expression.Call(
typeof(Queryable),
"SingleOrDefault",
new[] { entityType },
getTableCall,
Expression.Quote(idCheckLambda));
var singleOrDefaultLambda =
Expression.Lambda<Func<ModelDataContext, object, object>>(
Expression.Convert(singleOrDefaultCall, typeof(object)),
dataContextParameter,
valueParameter);
var singleOrDefaultFunction = singleOrDefaultLambda.Compile();
// Usage
using(var dataContext = new DataModel.ModelDataContext())
{
return singleOrDefaultFunction(dataContext, reader.Value);
}
If you are using .NET 4, you could try casting your returned objects as dynamic, so you could then query like this.
using (var dataContext = new DataModel.ModelDataContext())
{
var entity = Expression.Parameter(objectType, "model");
var keyValue = Expression.Property(entity, "Id");
var pkValue = Expression.Constant(reader.Value);
var cond = Expression.Equal(keyValue, pkValue);
var table = dataContext.GetTable(objectType);
var result = table.Where(ent => ((dynamic)ent).SomeField == "SomeValue");
}
I'm still not entirely sure as to the whole of your problem (and I suspect the answer about dynamic is going to solve part of what will come up too). Still, just to answer:
Every example i had found is using generic variant of GetTable<>(), but i cannot use that obviously
For any T, Table<T> implements (among other interfaces) ITable<T> and ITable. The former is generically typed, the latter not.
The form GetTable<T>() returns such a Table<T>. However, the form GetTable(Type t) returns an ITable. Since ITable inherits from IQueryable you can query it. If you need to do something with that query that would normally require knowledge of the type (such as comparing on a given property) then dynamic as per the previous answer given by Steve Danner allows that to happen.
I'd do it using Reflection and the LINQ dynamic query library, personally.
You can get the list of all the keys for a table with dataContext.Mapping.GetMetaType(objectType).IdentityMembers, then access the data with something along the lines of dataContext.GetTable(objectType).Where(key.Name + "==#0", id).
Obviously, I left out a few steps in there - if you have multiple keys, you'll need to build a fuller predicate with a loop over .IdentityMembers, and if you always just have the one key, you can use .First() on it. I haven't tested it either, but it should be pretty close. It'd probably be 6-7 lines of code total - I can write it up (and test) if you need it.
Edit: The LINQ Dynamic Query Library can be downloaded from Microsoft at http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx - just include DynamicLINQ.cs in your project and you're good.

Dynamic "WHERE IN" on IQueryable (linq to SQL)

I have a LINQ to SQL query returning rows from a table into an IQueryable object.
IQueryable<MyClass> items = from table in DBContext.MyTable
select new MyClass
{
ID = table.ID,
Col1 = table.Col1,
Col2 = table.Col2
}
I then want to perform a SQL "WHERE ... IN ...." query on the results. This works fine using the following. (return results with id's ID1 ID2 or ID3)
sQuery = "ID1,ID2,ID3";
string[] aSearch = sQuery.Split(',');
items = items.Where(i => aSearch.Contains(i.ID));
What I would like to be able to do, is perform the same operation, but not have to specify the i.ID part. So if I have the string of the field name I want to apply the "WHERE IN" clause to, how can I use this in the .Contains() method?
There's a couple of ways to do this. One way is to use Dynamic Linq. Another way is to use Predicate Builder.
Your have to build an expression tree. It will end up looking like this (partial code, will not compile). This one does both contains and equals. Used in this project: http://weblogs.asp.net/rajbk/archive/2010/04/15/asp-net-mvc-paging-sorting-filtering-a-list-using-modelmetadata.aspx
var param = Expression.Parameter(filterType, propertyName);
var left = Expression.Property(param, filterType.GetProperty(propertyName));
var right = Expression.Constant(propertyValue, modelMetaData.ModelType);
LambdaExpression predicate = null;
if (searchFilterAttribute.FilterType == FilterType.Contains)
{
var methodContains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var filterContains = Expression.Call(left, methodContains, right);
predicate = Expression.Lambda(filterContains, param);
}
else
{
var expr = Expression.Equal(left, right);
predicate = Expression.Lambda(expr, param);
}
var expression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryable.ElementType },
queryable.Expression,
predicate);
queryable = queryable.Provider.CreateQuery<T>(expression);
I may rewrite this into a reusable extension method (it is too specific to that project at the moment) and blog about it.

Categories

Resources