Error when making generic expression dynamically - c#

Based on Generic expression abstraction issue I want to make a more generic method to use "UpdateGraph" using Entity Framework 6.
Currently, I can call SetOwnedCollectionMapping(t => t.ClassProperty), but I want to make a more reusable method by using the class instance and any class property.
Here is an example that I use :
private static void AddOrUpdate<T>(TDataEntity class1, T NavigationProperty) where T : class
{
PropertyInfo pinfo = typeof(TDataEntity).GetProperty("PropertyName");
object value = pinfo.GetValue(class1, null);
ParameterExpression pe = Expression.Parameter(value.GetType(), "L");
var arg = Expression.Constant(null, typeof(TDataEntity));
var body = Expression.PropertyOrField(arg, "PropertyName");
var lambda = Expression.Lambda(body);
SetOwnedCollectionMapping(lambda);
using (var db = new ConsoleContext())//
{
db.UpdateGraph(class1, graphMapping);
db.SaveChanges();
}
}
protected static Expression<Func<IUpdateConfiguration<TDataEntity>, object>> graphMapping { get; set; }
protected void SetOwnedCollectionMapping<T>(Expression<Func<TDataEntity, ICollection<T>>> mapping)
{
Expression<Func<IUpdateConfiguration<TDataEntity>, object>> template =
_ => _.OwnedCollection(mapping);
var map = Expression.Parameter(
typeof(IUpdateConfiguration<TDataEntity>),
"map");
graphMapping = Expression.Lambda<Func<IUpdateConfiguration<TDataEntity>, object>>(
Expression.Call(
((MethodCallExpression)template.Body).Method,
map,
Expression.Quote(mapping)),
map);
}
The error I have is on the call method SetOwnedCollectionMapping(lambda);
Error 1 The type arguments for method
'ConsoleApplication1.Program.SetOwnedCollectionMapping(System.Linq.Expressions.Expression>>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
I have doubt about my lambda expression. Can someone help me please ?

It looks like at AddOrUpdate you need to update following line to make compiler know what type are you going to pass there
SetOwnedCollectionMapping<T>(lambda);

Related

How to use Type data in C#

I have these lines :
var lambdaExpression = Expression.Lambda<Func<ClassOne, bool>>(eq, parameter);
var myList = db.ClassOne.Where(lambdaExpression).ToList();
I want to make those lines generic. My attempt is this:
mytype = typeof(ClassOne);
var lambdaExpression = Expression.Lambda<Func<mytype, bool>>(eq, parameter);
var myList = db.mytype.Where(lambdaExpression).ToList();
But I get :
'mytype' is a variable but is used like a type
And
'Entities' does not contain a definition for 'mytype' and no extension
method 'mytype' accepting a first argument of type 'Entities' could be
found (are you missing a using directive or an assembly reference?)
As you can see I want to pass the name of ClassOne via a variable. How can I do that? Thanks.
Given a very simple:
public class ClassOne
{
public int ID { get; set; }
}
Then we can:
public static class MyWhereHelper
{
public static readonly MethodInfo WhereMethod = typeof(MyWhereHelper).GetMethod("Where", BindingFlags.Static | BindingFlags.Public);
public static List<T> Where<T>(IQueryable<T> baseQuery, Expression<Func<T, bool>> where)
{
return baseQuery.Where(where).ToList();
}
}
and then we use it like:
// Sample data
var db = new
{
Data = new[]
{
new ClassOne { ID = 1 },
new ClassOne { ID = 2 },
new ClassOne { ID = 3 },
}.AsQueryable(),
};
var mytype = typeof(ClassOne);
// Real code begins here
var parameter = Expression.Parameter(mytype);
var eq = Expression.Equal(Expression.Property(parameter, "ID"), Expression.Constant(2));
// In truth lambdaExpression is Expression<Func<mytype, bool>>
LambdaExpression lambdaExpression = Expression.Lambda(eq, parameter);
// Search of mytype inside db
// We look for the property that is implementing IQueryable<mytype>
// We could lookup by name if the name is .mytype
var iqueryableMyType = typeof(IQueryable<>).MakeGenericType(mytype);
var prop = db.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Single(x => iqueryableMyType.IsAssignableFrom(x.PropertyType));
// db.mytype value
var propValue = prop.GetValue(db);
// MyWhereHelper.Where<mytype>
MethodInfo method = MyWhereHelper.WhereMethod.MakeGenericMethod(mytype);
var myList = (IEnumerable<object>)method.Invoke(null, new object[] { propValue, lambdaExpression });
We use reflection to find the db.mytype and for executing the query (note that we are using a "trick" here: we try to put everything that needs to use the <mytype> generic argument inside a single method, and use reflection once to execute that method. Clearly I'm speaking of MyWhereHelper.Where<>).
Inside MyWhereHelper.Where<> everything is strongly typed. Inside MyWhereHelper.Where we have a List<T> (that is a List<mytype>). But once we exit MyWhereHelper.Where<>, the caller is back again not knowing the exact type of mytype at compile time, so it can manipulate the result only as a IEnumerable<object> (or use once again reflection to manipulate it)
This is a method I always use to get a Expression<Func<type1, type2>>. This the power of reflection:
public static MethodInfo GetLambdaExpressionMethod(Type type1, Type type2)
{
return typeof(Expression).GetMethods().Where(x => x.Name == "Lambda").First()
.MakeGenericMethod(typeof(Func<,>).MakeGenericType(type1, type2));
}
You can use it, like this:
var lambda = GetLambdaExpressionMethod(yourType, typeof(bool)).Invoke(null,
new object[] {
eq,
new ParameterExpression[] { parameter }
});
Now cast the lambda variable (that is object right now) to the Expression<Func<yourType, bool>> of yourType

Get Class Reference from Class Type or String

So I've been looking around at a ton of similar questions but none of them seem to be attempting the same thing I am. I need a reference to a class, not a class instance.
I am trying to dynamically make a class reference for a generic type function. My function is as follows:
private void CleanupTable<T, U>(DbSet<T> dbSet, CleanupModel.Tables table, DbSet<U> lastDbSet, dynamic removedRec) where T : class where U : class
{
ParameterExpression tpe = Expression.Parameter(typeof(T));
Expression idProp = Expression.Property(tpe, typeof(T).GetProperty(GetIdProperty(lastDbSet)));
Expression constIdProp = Expression.Constant(removedRec.GetType().GetProperty(GetIdProperty(lastDbSet)).GetValue(removedRec, null), typeof(int));
Expression completeExpression = Expression.Equal(idProp, constIdProp);
Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(completeExpression, tpe);
List<T> removedRecs = dbSet.Where(expression).ToList();
removedRecs.ForEach(rec =>
{
DbSet nextSet = GetNextSet(dbSet);
//Here is where I'm trying to create a reference using nextSet
CleanupTable</*nextSetType reference*/, T>(nextSet, GetNextTable(dbSet), dbSet, rec);
dbSet.Remove(rec);
reportHelper.ReportSuccess(table, ReportHelper.ReportReasons.Linked, rec);
});
}
Here is the code for GetNextSet():
private DbSet GetNextSet(CleanupModel.Tables table)
{
switch (table)
{
case CleanupModel.Tables.Version: return context.Page;
//More cases
default: return null;
}
}
I have tried using things like GetType() but the generic does not accept a Type. Is what I'm trying to do even possible?
SOLUTION
So I solved it thanks to the help of InBetween. I ended up letting CleanupTable() infer the types from the arguments. The problem originally was that it could not infer the type of nextSet because it was a generic DbSet type and was not implicitly typed. I solved this issue by just setting the related values to dynamics and let CleanupTable() figure it out at run-time.
Here's the updated code:
CleanupTable()
private void CleanupTable<T, U>(DbSet<T> dbSet, CleanupModel.Tables table, DbSet<U> lastDbSet, dynamic removedRec) where T : class where U : class
{
ParameterExpression tpe = Expression.Parameter(typeof(T));
Expression idProp = Expression.Property(tpe, typeof(T).GetProperty(GetIdProperty(lastDbSet)));
Expression constIdProp = Expression.Constant(removedRec.GetType().GetProperty(GetIdProperty(lastDbSet)).GetValue(removedRec, null), typeof(int));
Expression completeExpression = Expression.Equal(idProp, constIdProp);
Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(completeExpression, tpe);
List<T> removedRecs = dbSet.Where(expression).ToList();
removedRecs.ForEach(rec =>
{
dynamic nextSet = GetNextSet(table);
if (nextSet == null)
CleanupTable(nextSet, GetNextTable(table), dbSet, rec);
dbSet.Remove(rec);
reportHelper.ReportSuccess(table, ReportHelper.ReportReasons.Linked, rec);
});
}
GetNextSet()
private dynamic GetNextSet(CleanupModel.Tables table)
{
switch (table)
{
case CleanupModel.Tables.Version: return context.Page;
//More cases
default: return null;
}
}

Override IsUnicode using Generic Extension

I'm trying to make a generic extension method on EntityTypeConfiguration<T> that will allow me to enumerate all the string properties on T and set the IsUnicode(false) for them. Here is what I have so far, but I'm stuck at trying to obtain the StringPropertyConfiguration, the constructor for this class is internal, and I don't know where to obtain an instance of it.
public static void SetStringsToBeNonUnicode<T>(this EntityTypeConfiguration<T> config) where T : class
{
PropertyInfo[] properties = typeof (T).GetProperties();
foreach (PropertyInfo p in properties)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, p);
var funcType = typeof(Func<,>).MakeGenericType(typeof(T), p.PropertyType);
var lambda = Expression.Lambda(funcType, property, parameter);
//This is the line where I need help
StringPropertyConfiguration stringConfig =
new System.Data.Entity.ModelConfiguration.Configuration.StringPropertyConfiguration(config.Property<System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive.StringPropertyConfiguration>((LambdaExpression) property));
stringConfig.IsUnicode(false);
}
}
I know it's too late but if others need it:
((StringPropertyConfiguration)config.GetType().GetMethod("Property", new Type[] { lambda.GetType() }).Invoke((object)config, new object[] { lambda })).IsUnicode(false);

Create an Expression<Func<,>> using reflection

Im using Moq to create mocks of a data set.
I have created a little helper class that allows me to have an in memory storage instead of a database that makes unit testing a breeze. That way I can add and remove items from my mock data set, this allows me to test my insert and delete service calls.
During the setup of the mock I have a line that looks like the following
this.Setup(i => i.AcademicCycles).Returns(mockStore.GetList<AcademicCycle>());
My mock has a lot of properties so I would like to perform this setup step using reflection. I have managed to the Returns part of the process working via reflection but I am stuck on the lambda method to Setup.
Setup takes an
Expression<Func<GoalsModelUnitOfWork, IQueryable<AcademicCycle>>> that corresponds to the i => i.AcademicCycles
and I would like to create this dynamically. Using reflection I have the following:
The name of the property: "AcademicCycles"
The type IQueryable<AcademicCycle>
The type AcademicCycle
I also have the instance of the i in the lambda statement which is a GoalsModelUnitOfWork
The code to create the expression dynamically would be like this:
ParameterExpression parameter = Expression.Parameter(typeof (GoalsModelUnitOfWork), "i");
MemberExpression property = Expression.Property(parameter, "AcademicCycles");
var queryableType = typeof (IQueryable<>).MakeGenericType(typeof (AcademicCycle));
var delegateType = typeof (Func<,>).MakeGenericType(typeof (GoalsModelUnitOfWork), queryableType);
var yourExpression = Expression.Lambda(delegateType, property, parameter);
The result will have the desired type, but the problem is that the return type of Expression.Lambda() is LambdaExpression and you can't perform a type cast to Expression<Func<...>> to pass it as parameter to your setup function because you don't know the generic type parameters for the Func. So you have to invoke the Setup method by reflection, too:
this.GetType().GetMethod("Setup", yourExpression.GetType()).Invoke(this, yourExpression);
I decided to take a crack at it and ended up with this god awful piece of code.
I am no reflection expert and this is just a first attempt to get something working. I'd be very interested in what other approaches people have, or whether any of the relfection wrapper libraries can make this nicer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Xunit;
namespace MyExample
{
public class Tests
{
[Fact]
public void Test()
{
Dictionary<Type, object> data = new Dictionary<Type, object>
{
{ typeof(IQueryable<Cycle>), new List<Cycle> { new Cycle { Name = "Test" } }.AsQueryable() },
{ typeof(IQueryable<Rider>), new List<Rider> { new Rider { Name = "1"}, new Rider { Name = "2" } }.AsQueryable() }
};
var mock = new Mock<IDataContext>();
var setup = mock.GetType().GetMethods().Single(d => d.Name == "Setup" && d.ContainsGenericParameters);
var param = Expression.Parameter(typeof(IDataContext), "i");
foreach (var property in typeof(IDataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// Build lambda
var ex = Expression.Lambda(Expression.MakeMemberAccess(param, property), param);
// Get generic version of the Setup method
var typedSetup = setup.MakeGenericMethod(property.PropertyType);
// Run the Setup method
var returnedSetup = typedSetup.Invoke(mock, new[] { ex });
// Get generic version of IReturns interface
var iReturns = typedSetup.ReturnType.GetInterfaces().Single(d => d.Name.StartsWith("IReturns`"));
// Get the generic Returns method
var returns = iReturns.GetMethod("Returns", new Type[] { property.PropertyType });
// Run the returns method passing in our data
returns.Invoke(returnedSetup, new[] { data[property.PropertyType] });
}
Assert.Equal(1, mock.Object.Cycles.Count());
}
}
public class Cycle
{
public string Name { get; set; }
}
public class Rider
{
public string Name { get; set; }
}
public interface IDataContext
{
IQueryable<Cycle> Cycles { get; set; }
IQueryable<Rider> Riders { get; set; }
}
}
This method ought to construct the lambda expression. Since you are invoking the Setup method by reflection, you do not need a strongly-typed lambda expression; you are going to pass it as part of an object array when you call Invoke:
public LambdaExpression PropertyGetLambda(string parameterName, Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType, parameterName);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}
I don't think you actually need the parameter name. If I'm right about that, you could simplify a bit:
public LambdaExpression PropertyGetLambda(Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}

Creating a property setter delegate

I have created methods for converting a property lambda to a delegate:
public static Delegate MakeGetter<T>(Expression<Func<T>> propertyLambda)
{
var result = Expression.Lambda(propertyLambda.Body).Compile();
return result;
}
public static Delegate MakeSetter<T>(Expression<Action<T>> propertyLambda)
{
var result = Expression.Lambda(propertyLambda.Body).Compile();
return result;
}
These work:
Delegate getter = MakeGetter(() => SomeClass.SomeProperty);
object o = getter.DynamicInvoke();
Delegate getter = MakeGetter(() => someObject.SomeProperty);
object o = getter.DynamicInvoke();
but these won't compile:
Delegate setter = MakeSetter(() => SomeClass.SomeProperty);
setter.DynamicInvoke(new object[]{propValue});
Delegate setter = MakeSetter(() => someObject.SomeProperty);
setter.DynamicInvoke(new object[]{propValue});
The MakeSetter lines fail with "The type arguments cannot be inferred from the usage. Try specifying the type arguments explicitly."
Is what I'm trying to do possible? Thanks in advance.
The Expression API supports this in .NET 4.0, but sadly the C# compiler doesn't add any extra candy to support. But the good news is that you can trivially take a "get" expression (which the C# compiler can write) and re-write it as a "set" expression.
And even better; if you don't have .NET 4.0, there are still at least two other ways of performing a "set" via an expression written as a "get".
Here they all are, for info:
using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo {
public string Bar { get; set; }
static void Main() {
// take a "get" from C#
Expression<Func<Foo, string>> get = foo => foo.Bar;
// re-write in .NET 4.0 as a "set"
var member = (MemberExpression)get.Body;
var param = Expression.Parameter(typeof(string), "value");
var set = Expression.Lambda<Action<Foo, string>>(
Expression.Assign(member, param), get.Parameters[0], param);
// compile it
var action = set.Compile();
var inst = new Foo();
action(inst, "abc");
Console.WriteLine(inst.Bar); // show it working
//==== reflection
MethodInfo setMethod = ((PropertyInfo)member.Member).GetSetMethod();
setMethod.Invoke(inst, new object[] { "def" });
Console.WriteLine(inst.Bar); // show it working
//==== Delegate.CreateDelegate
action = (Action<Foo, string>)
Delegate.CreateDelegate(typeof(Action<Foo, string>), setMethod);
action(inst, "ghi");
Console.WriteLine(inst.Bar); // show it working
}
}
As per my comments - because links go dead - I have posted the full code as an answer to the question. YES it is possible to do what the OP is requesting. and here is a nice little gem from Nick demonstrating it. Nick credits this page and another page for his complete solution along with performance metrics. I provide that below instead of just a link.
// returns property getter
public static Func<TObject, TProperty> GetPropGetter<TObject, TProperty>(string propertyName)
{
ParameterExpression paramExpression = Expression.Parameter(typeof(TObject), "value");
Expression propertyGetterExpression = Expression.Property(paramExpression, propertyName);
Func<TObject, TProperty> result =
Expression.Lambda<Func<TObject, TProperty>>(propertyGetterExpression, paramExpression).Compile();
return result;
}
// returns property setter:
public static Action<TObject, TProperty> GetPropSetter<TObject, TProperty>(string propertyName)
{
ParameterExpression paramExpression = Expression.Parameter(typeof(TObject));
ParameterExpression paramExpression2 = Expression.Parameter(typeof(TProperty), propertyName);
MemberExpression propertyGetterExpression = Expression.Property(paramExpression, propertyName);
Action<TObject, TProperty> result = Expression.Lambda<Action<TObject, TProperty>>
(
Expression.Assign(propertyGetterExpression, paramExpression2), paramExpression, paramExpression2
).Compile();
return result;
}
Action<T> represents a delegate that takes one parameter of type T and returns nothing. The lambda expressions you provide to MakeSetter represent delegates that take no parameter and return either SomeClass.SomeProperty or someObject.SomeProperty.
The error messages you're getting are due to the fact that the compiler cannot infer the types from the lambda expressions you're passing into the MakeSetter method because what you've passed and what the method is expecting are not in sync.
Your MakeSetter is expecting an Action<T> and you are passing it a Func<T> (() => someObject.SomeProperty). Try the following:
Delegate setter = MakeSetter((prop) => {someObject.SomeProperty = prop;});
setter.DynamicInvoke(new object[]{propValue});
EDIT Doesn't look like you can convert statement lambdas into expressions. This is somewhat of a round about way to do it without expressions - straight to delegates:
class Test2 {
delegate void Setter<T>(T value);
public static void Test() {
var someObject = new SomeObject();
Setter<string> setter = (v) => { t.SomeProperty = v; };
setter.DynamicInvoke(new object[]{propValue});
}
}

Categories

Resources