Okay first to explain the rules:
I need a function that constructs a delegate matching any delegate type that encapsulates a body of which invokes a delegate of type (Object) (Object[] args) with 'args' containing all of the arguments passed to the original delegate during invocation.
My work so far:
delegate void TestDelegate(int x, int y);
delegate object TestDelegate2(object[] args);
static void Main(string[] sargs)
{
TestDelegate d = (TestDelegate)CreateAnonymousFromType(typeof(TestDelegate));
object ret = d.DynamicInvoke(2, 6);
if (ret != null) { Console.WriteLine(ret); }
Console.ReadKey();
}
static void SpecialInvoke(int x, int y)
{
Console.WriteLine("x: {0}\r\ny: {1}", x, y);
}
static Delegate CreateAnonymousFromType(Type type)
{
MethodInfo method = type.GetMethod("Invoke");
TestDelegate2 _delegate = new TestDelegate2(
delegate(object[] args)
{
Console.WriteLine("x: {0}\r\ny: {1}", args[0], args[1]);
return "This is the return";
});
var typeargs = CreateArgs(method.GetParameters());
var argindex = -1;
var tmp = Expression.Parameter(typeof(Object), "tmp");
var index = Expression.Parameter(typeof(int), "index");
var length = Expression.Constant(typeargs.Length);
var _break = Expression.Label("breakto");
var delegateargs = Expression.Parameter(typeof(object[]), "args");
return Expression.Lambda(
type,
Expression.Block(
new[] { tmp, index, delegateargs },
Expression.Assign(index, Expression.Constant(0)),
Expression.Assign(delegateargs, Expression.NewArrayBounds(typeof(Object), length)),
Expression.Loop(
Expression.IfThenElse(Expression.LessThan(index, length),
Expression.Block(
Expression.Assign(tmp, Expression.Convert(typeargs[++argindex], typeof(Object))),
Expression.Assign(Expression.ArrayAccess(delegateargs, index), tmp),
Expression.PostIncrementAssign(index)
),
Expression.Break(_break)
),
_break
),
Expression.Call(_delegate.Method, delegateargs)
),
typeargs
).Compile();
}
static ParameterExpression[] CreateArgs(ParameterInfo[] _params)
{
ParameterExpression[] ret = new ParameterExpression[_params.Length];
for (int i = 0; i < ret.Length; i++)
ret[i] = Expression.Parameter(_params[i].ParameterType, _params[i].Name);
return ret;
}
Now this SORTA works... I only get the value of typeargs[0] passed to the delegate "TestDelegate2" for both parameters x and y, the 'args' parameter at runtime is object[] { 2, 2 } and I can't for the life of me find a way to increment "argindex" inside the scope of the argument iteration... parameter count at compile time is indefinate.
Anybody know how I can solve this?
I've tried just copying the argument array using Expression.NewArrayInit(typeof(Object), typeargs) but that fails saying it can't use Int32 to initialize an array of Object
I've also tried this:
var arguments = Expression.Constant(typeargs);
And accessing the value of "arguments" at "index", however this produces the strings "x" and "y" .. apparently the names of the arguments and not their values.
This is honestly my first major attempt at using expression trees so any help.. no matter how little. Would be appreciated.
Thank you.
I think you were on the right track with Expression.NewArrayInit. You can fix the "An expression of type 'System.Int32' cannot be used to initialize an array of type 'System.Object'" error by using Expression.Convert to insert a conversion for each parameter:
var typeargs = CreateArgs(method.GetParameters());
return Expression.Lambda(
type,
Expression.Call(_delegate.Method, Expression.NewArrayInit(typeof(object),
typeargs.Select(arg => Expression.Convert(arg, typeof(object)))
)),
typeargs
).Compile();
Related
I have a generic func that takes a string of fields and selects them from an object using linq. However the output is not type of object but a subset of. So to convert this new List<subsetOfOriginal> to a DataTable, I cannot use list.GetType() because it will get me Original.
I have converted
Func<T, T> CreateSelectStatement<T>(string fields)
to
Func<T, S> CreateSelectStatement<T, S>(string fields)
because I don't want TResult to be of type T. Not sure if this is the correct way. This is the code I have at the moment and how it is invoked.
Func<T, S> CreateSelectStatement<T, S>(string fields)
{
var xParameter = Expression.Parameter(typeof(T), "o");
var xNew = Expression.New(typeof(T));
var bindings = fields.Split(',').Select(o => o.Trim())
.Select(o => {
var mi = typeof(T).GetProperty(o);
var xOriginal = Expression.Property(xParameter, mi);
return Expression.Bind(mi, xOriginal);
}
);
var xInit = Expression.MemberInit(xNew, bindings);
var lambda = Expression.Lambda<Func<T, S>>(xInit, xParameter);
return lambda.Compile();
}
and then to invoke it I'm using
CreateSelectStatement<SP_ProjectAnalysis_NP, dynamic>(columns)
Expected outcome should be the fields that are loading correctly but the type of the object is as if I used p => new { }. Currently I am getting object as the return type and trying to perform the following gets me 0 properties:
private static DataTable ToDataTable<T>(IList<T> list, string tableName = "table")
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
I'm creating an expression tree builder to return custom anonymous types. I tried it first with discrete types and it works ok, but using TypeBuilder to build types at runtime and pass that type to the expression tree fail with this error
'Argument expression is not valid'
here is the code I use:
this method I use to create the anonymous type
private Type CreateAnonymousType(Dictionary<string, Type> properties)
{
AssemblyName dynamicAssemblyName = new AssemblyName("MyAssembly");
AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("MyAssembly");
TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("ReturnType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
foreach (var p in properties)
{
dynamicAnonymousType.DefineField(p.Key, p.Value, FieldAttributes.Public);
}
return dynamicAnonymousType.CreateType();
}
and here is how I create the expression tree
var cars = new List<Car>();
for (int i = 0; i < 10; i++)
{
cars.Add(new Car { Id = i, Name = "Car " + i, Age = 2010 + i });
}
IQueryable<Car> allCars = cars.AsQueryable();
var properties = new Dictionary<string, Type>
{
{ "Id", typeof(int) },
{ "Name", typeof(string) }
};
ParameterExpression x = Expression.Parameter(typeof(Car), "x");
var listMembers = properties.Select(p => Expression.Property(x, p.Key));
var returnType = CreateAnonymousType(properties);
object destObject = Activator.CreateInstance(returnType);
var listBind = listMembers.Select(p => Expression.Bind(returnType.GetField(p.Member.Name), p));
var result = Expression.New(returnType);
var initExp = Expression.MemberInit(result, listBind.ToArray());
var call = Expression.Call(typeof(Queryable), "Select",
new Type[] {
typeof(Car),
returnType
}
, Expression.Constant(allCars)
, Expression.Lambda(initExp, x));
var qResult = allCars.Provider.CreateQuery<IdName>(call);
foreach (var car in qResult)
{
Console.WriteLine(car.Id + " - " + car.Name);
}
the error happened while CreateQuery method executes
This is because call returns dynamically created ReturnType not IdName thus the exception. Additionally you cannot put such dynamic types like ReturnType as generic type parameters because compiler knows nothing about them so you should use dynamic instead so the type will be resolved at runtime:
var qResult = allCars.Provider.CreateQuery<dynamic>(call);
I'm building a LINQ-based query generator.
One of the features is being able to specify an arbitrary server-side projection as part of the query definition. For example:
class CustomerSearch : SearchDefinition<Customer>
{
protected override Expression<Func<Customer, object>> GetProjection()
{
return x => new
{
Name = x.Name,
Agent = x.Agent.Code
Sales = x.Orders.Sum(o => o.Amount)
};
}
}
Since the user must then be able to sort on the projection properties (as opposed to Customer properties), I recreate the expression as a Func<Customer,anonymous type> instead of Func<Customer, object>:
//This is a method on SearchDefinition
IQueryable Transform(IQueryable source)
{
var projection = GetProjection();
var properProjection = Expression.Lambda(projection.Body,
projection.Parameters.Single());
In order to return the projected query, I'd love to be able to do this (which, in fact, works in an almost identical proof of concept):
return Queryable.Select((IQueryable<TRoot>)source, (dynamic)properProjection);
TRoot is the type parameter in SearchDefinition. This results in the following exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for
'System.Linq.Queryable.Select<Customer,object>(System.Linq.IQueryable<Customer>,
System.Linq.Expressions.Expression<System.Func<Customer,object>>)'
has some invalid arguments
at CallSite.Target(Closure , CallSite , Type , IQueryable`1 , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet]
(CallSite site, T0 arg0, T1 arg1, T2 arg2)
at SearchDefinition`1.Transform(IQueryable source) in ...
If you look closely, it's inferring the generic parameters incorrectly: Customer,object instead of Customer,anonymous type, which is the actual type of the properProjection expression (double-checked)
My workaround is using reflection. But with generic arguments, it's a real mess:
var genericSelectMethod = typeof(Queryable).GetMethods().Single(
x => x.Name == "Select" &&
x.GetParameters()[1].ParameterType.GetGenericArguments()[0]
.GetGenericArguments().Length == 2);
var selectMethod = genericSelectMethod.MakeGenericMethod(source.ElementType,
projectionBody.Type);
return (IQueryable)selectMethod.Invoke(null, new object[]{ source, projection });
Does anyone know of a better way?
Update: the reason why dynamic fails is that anonymous types are defined as internal. That's why it worked using a proof-of-concept project, where everything was in the same assembly.
I'm cool with that. I'd still like to find a cleaner way to find the right Queryable.Select overload.
The fix is so simple it hurts:
[assembly: InternalsVisibleTo("My.Search.Lib.Assembly")]
Here's my test as requested. This on a Northwind database and this works fine for me.
static void Main(string[] args)
{
var dc = new NorthwindDataContext();
var source = dc.Categories;
Expression<Func<Category, object>> expr =
c => new
{
c.CategoryID,
c.CategoryName,
};
var oldParameter = expr.Parameters.Single();
var parameter = Expression.Parameter(oldParameter.Type, oldParameter.Name);
var body = expr.Body;
body = RebindParameter(body, oldParameter, parameter);
Console.WriteLine("Parameter Type: {0}", parameter.Type);
Console.WriteLine("Body Type: {0}", body.Type);
var newExpr = Expression.Lambda(body, parameter);
Console.WriteLine("Old Expression Type: {0}", expr.Type);
Console.WriteLine("New Expression Type: {0}", newExpr.Type);
var query = Queryable.Select(source, (dynamic)newExpr);
Console.WriteLine(query);
foreach (var item in query)
{
Console.WriteLine(item);
Console.WriteLine("\t{0}", item.CategoryID.GetType());
Console.WriteLine("\t{0}", item.CategoryName.GetType());
}
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
Console.WriteLine();
}
static Expression RebindParameter(Expression expr, ParameterExpression oldParam, ParameterExpression newParam)
{
switch (expr.NodeType)
{
case ExpressionType.Parameter:
var parameterExpression = expr as ParameterExpression;
return (parameterExpression.Name == oldParam.Name)
? newParam
: parameterExpression;
case ExpressionType.MemberAccess:
var memberExpression = expr as MemberExpression;
return memberExpression.Update(
RebindParameter(memberExpression.Expression, oldParam, newParam));
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
var binaryExpression = expr as BinaryExpression;
return binaryExpression.Update(
RebindParameter(binaryExpression.Left, oldParam, newParam),
binaryExpression.Conversion,
RebindParameter(binaryExpression.Right, oldParam, newParam));
case ExpressionType.New:
var newExpression = expr as NewExpression;
return newExpression.Update(
newExpression.Arguments
.Select(arg => RebindParameter(arg, oldParam, newParam)));
case ExpressionType.Call:
var methodCallExpression = expr as MethodCallExpression;
return methodCallExpression.Update(
RebindParameter(methodCallExpression.Object, oldParam, newParam),
methodCallExpression.Arguments
.Select(arg => RebindParameter(arg, oldParam, newParam)));
default:
return expr;
}
}
Also, dynamic method resolution doesn't really do much for you in this case as there are only two very distinct overloads of Select(). Ultimately you just need to remember that you won't have any static type checking on your results since you don't have any static type information. With that said, this will also work for you (using the above code example):
var query = Queryable.Select(source, expr).Cast<dynamic>();
Console.WriteLine(query);
foreach (var item in query)
{
Console.WriteLine(item);
Console.WriteLine("\t{0}", item.CategoryID.GetType());
Console.WriteLine("\t{0}", item.CategoryName.GetType());
}
Ok, I'll admit that I don't entirely "get" lambda expressions and LINQ expression trees yet; a lot of what I'm doing is cutting and pasting and seeing what works. I've looked over lots of documentation, but I still haven't found the my "aha" moment yet.
With that being said...
I'm attempting to dynamically add a GroupBy expression to my Linq expression. I followed the question here:
Need help creating Linq.Expression to Enumerable.GroupBy
and tried to implement what I saw there.
First off, I've got entity classes for my database, and a table calledObjCurLocViewNormalized
I've got an method that does the initial call,
public IQueryable<ObjCurLocViewNormalized> getLocations()
{
IQueryable<ObjCurLocViewNormalized> res = (from loc in tms.ObjCurLocViewNormalized
select loc);
return res;
}
so I can call:
IQueryable<MetAmericanLinqDataModel.ObjCurLocViewNormalized> locations = american.getLocations();
No problem so far.
Now, I want to group by an arbitrary column, with a call like this:
var grouped = locations.addGroupBy(childLocationFieldName);
Right now, I have a method :
static public System.Linq.IQueryable<System.Linq.IGrouping<string, TResult>> addGroupBy<TResult>(this IQueryable<TResult> query, string columnName)
{
var providerType = query.Provider.GetType();
// Find the specific type parameter (the T in IQueryable<T>)
var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
var tableType = iqueryableT.GetGenericArguments()[0];
var tableName = tableType.Name;
var data = Expression.Parameter(iqueryableT, "query");
var arg = Expression.Parameter(tableType, tableName);
var nameProperty = Expression.PropertyOrField(arg, columnName);
var lambda = Expression.Lambda<Func<TResult, string>>(nameProperty, arg);
var expression = Expression.Call(typeof(Enumerable),
"GroupBy",
new Type[] { tableType, typeof(string) },
data,
lambda);
var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
var result = query.GroupBy(predicate).AsQueryable();
return result;
}
All this compiles ok, but when I run it, I get the error:
System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Linq.IGrouping`2[System.String,MetAmericanLinqDataModel.ObjCurLocViewNormalized]]' cannot be used for return type 'System.String'
and the error comes from this line:
var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg);
I'm copying and adapting this code from successful work I did in dynamically added Where clauses to an expression. So I'm sort of stabbing in the dark here.
If anyone out there can help to shed some light on this, Obviously posting complete working code and doing all my thinking for me would be great :), but if you could just lay out just why this is wrong, or how to wrap my head around these concepts, that would be great. If you can point to documentation that can really help be bridge the gap between the basics of lambda expressions, and building dynamic expression trees, that would be great. There's obviously big holes in my knowledge, but I think this information could be useful to others.
thanks everyone for your time, and of course if I find the answer elsewhere, I'll post it here.
Thanks again.
Don
The solution should be pretty simple:
public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(
IQueryable<T> source, string column)
{
PropertyInfo columnProperty = typeof(T).GetProperty(column);
var sourceParm = Expression.Parameter(typeof(T), "x");
var propertyReference = Expression.Property(sourceParm, columnProperty);
var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);
return source.GroupBy(groupBySelector);
}
Assuming a sample class like this:
public class TestClass
{
public string TestProperty { get; set; }
}
You invoke it like this:
var list = new List<TestClass>();
var queryable = list.AsQueryable();
DynamicGroupBy<TestClass, string>(queryable, "TestProperty");
All that you need to do to make it work is the following:
static public IQueryable<IGrouping<TValue, TResult>> addGroupBy<TValue, TResult>(
this IQueryable<TResult> query, string columnName)
{
var providerType = query.Provider.GetType();
// Find the specific type parameter (the T in IQueryable<T>)
const object EmptyfilterCriteria = null;
var iqueryableT = providerType
.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), EmptyfilterCriteria)
.FirstOrDefault();
Type tableType = iqueryableT.GetGenericArguments()[0];
string tableName = tableType.Name;
ParameterExpression data = Expression.Parameter(iqueryableT, "query");
ParameterExpression arg = Expression.Parameter(tableType, tableName);
MemberExpression nameProperty = Expression.PropertyOrField(arg, columnName);
Expression<Func<TResult, TValue>> lambda = Expression.Lambda<Func<TResult, TValue>>(nameProperty, arg);
//here you already have delegate in the form of "TResult => TResult.columnName"
return query.GroupBy(lambda);
/*var expression = Expression.Call(typeof(Enumerable),
"GroupBy",
new Type[] { tableType, typeof(string) },
data,
lambda);
var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
var result = query.GroupBy(predicate).AsQueryable();
return result;*/
}
And you will call you expression in the following manner:
var grouped = locations.addGroupBy<string, ObjCurLocViewNormalized>(childLocationFieldName);
First generic parameter "string" us used for saying explicilty what type of elements you a grouping on. For example you can group by "int" field and method call will be like following:
var grouped = locations.addGroupBy<int, ObjCurLocViewNormalized>(someFieldNameWithTheTypeOfInt);
Edit
Just to finish this solution your way:
//return query.GroupBy(lambda);
MethodCallExpression expression = Expression.Call(typeof (Enumerable),
"GroupBy",
new[] { typeof(TResult), typeof(TValue) },
data,
lambda);
var result = Expression.Lambda(expression, data).Compile().DynamicInvoke(query);
return ((IEnumerable<IGrouping<TValue, TResult>>)result).AsQueryable();
Before someone shouts out the answer, please read the question through.
What is the purpose of the method in .NET 4.0's ExpressionVisitor:
public static ReadOnlyCollection<T> Visit<T>(ReadOnlyCollection<T> nodes, Func<T, T> elementVisitor)
My first guess as to the purpose of this method was that it would visit each node in each tree specified by the nodes parameter and rewrite the tree using the result of the elementVisitor function.
This does not appear to be the case. Actually this method appears to do a little more than nothing, unless I'm missing something here, which I strongly suspect I am...
I tried to use this method in my code and when things didn't work out as expected, I reflectored the method and found:
public static ReadOnlyCollection<T> Visit<T>(ReadOnlyCollection<T> nodes, Func<T, T> elementVisitor)
{
T[] list = null;
int index = 0;
int count = nodes.Count;
while (index < count)
{
T objA = elementVisitor(nodes[index]);
if (list != null)
{
list[index] = objA;
}
else if (!object.ReferenceEquals(objA, nodes[index]))
{
list = new T[count];
for (int i = 0; i < index; i++)
{
list[i] = nodes[i];
}
list[index] = objA;
}
index++;
}
if (list == null)
{
return nodes;
}
return new TrueReadOnlyCollection<T>(list);
}
So where would someone actually go about using this method? What am I missing here?
Thanks.
It looks to me like a convenience method to apply an aribitrary transform function to an expression tree, and return the resulting transformed tree, or the original tree if there is no change.
I can't see how this is any different of a pattern that a standard expression visitor, other than except for using a visitor type, it uses a function.
As for usage:
Expression<Func<int, int, int>> addLambdaExpression= (a, b) => a + b;
// Change add to subtract
Func<Expression, Expression> changeToSubtract = e =>
{
if (e is BinaryExpression)
{
return Expression.Subtract((e as BinaryExpression).Left,
(e as BinaryExpression).Right);
}
else
{
return e;
}
};
var nodes = new Expression[] { addLambdaExpression.Body }.ToList().AsReadOnly();
var subtractExpression = ExpressionVisitor.Visit(nodes, changeToSubtract);
You don't explain how you expected it to behave and why therefore you think it does little more than nothing.