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);
Related
The answer of Marc Gravell in this post explains how to create expressions just like that:
var lambda = CreateExpression<SomeModel, bool>("IsAlive");
It's possible avoid the explicit type bool and that the method obtains the return type from "IsAlive" property?
Something like this:
var lambda = CreateExpression<SomeModel>("IsAlive");
And lambda will be a Expression<Func<SomeModel, bool>>.
And in:
var lambda = CreateExpression<SomeModel>("StringProperty");
lambda will be a Expression<Func<SomeModel, string>>.
You can in fact do this, but the type becomes unknown. The object returned from this method will be of the proper type Expression<Func<Test, string>> but cannot be strong typed at compile time:
static LambdaExpression CreateExpression<TModel>(string propertyName)
{
var t = typeof(TModel);
var param = Expression.Parameter(typeof(TModel), "x");
//get the type for the 2nd generic arg
var propType = t.GetProperty(propertyName).PropertyType;
//make the generic type Func<TModel, TProp>
Type genericFuncType = typeof(Func<,>).MakeGenericType(new Type[] { typeof(TModel), propType });
//get the Expression.Lambda method
MethodInfo mi = typeof(Expression).GetMethods().First(a => a.Name == "Lambda" && a.GetParameters().Length == 2);
//get the Expression.Lambda<Func<TModel, TProp>> method
MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { genericFuncType });
//Call Expression.Lambda<Func<TModel, TProp>>
return (LambdaExpression)mi2.Invoke(null, new object[] { Expression.PropertyOrField(param, propertyName), new ParameterExpression[] { param }});
}
However note that the return type is now somewhat unknown and will need cast to use (or use dynamic).
So now you need even more code to cast it. Perhaps this would be useful in some sort of factory or such - not sure your use case.
class Program
{
public static void Main(string[] args)
{
var theExpression = CreateExpression<Test>("Name");
var theExpressionStrongType = theExpression as Expression<Func<Test, string>>;
//now you could use theExpressionStrongType
//or do this and go wild. :)
dynamic d = theExpression;
Console.ReadKey();
}
}
class Test
{
public string Name { get; set; }
}
Disclaimer: if you seriously want to use this in a production environment, I would clean up the code a LOT to just get refection types once, etc, etc...
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
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);
This problem has been discussed to an extent in this question: Create Generic Expression from string property name but perhaps I'm missing the answer, or it is subtly different.
I have the following queryable extension method:
public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
var sourceType = typeof (TSource);
var parameter = Expression.Parameter(sourceType, "item");
var orderByProperty = Expression.Property(parameter, propertyName);
var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderByDescending(source, (dynamic) orderBy);
}
For the purposes of this problem let us assume that the queryable source instance has a property called 'Created' which is a type of DateTime. If we define a class that has the property 'Created' on it and then OrderByDescending then the above will work fine. e.g.
var queryable = new List<EntityClassWithCreatedProperty>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
If we do the same thing but with an interface such as ICreated which has the Created property on it:
public interface ICreated
{
DateTime Created { get; }
}
Then the following also works:
var queryable = new List<ICreated>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
If however, you have the following interface hierarchy:
public interface ITimestamped : ICreated
{
...
}
Then the following will fail:
var queryable = new List<ITimestamped>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
With the similar error message to that of the other question: Instance property 'Created' is not defined for type ITimestamped. I'm assuming that I need to find the property on the declaring type of interface (which I can do by crawling the source type) but then what do I do with it? Most attempts I have tried result in the incorrect original source type not being cast-able back to the IQueryable. Do I need to use a ConvertType call somewhere? Thanks.
I would use the other Expression.Property() method that takes a propertyInfo, e.g
public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
var sourceType = typeof (TSource);
var parameter = Expression.Parameter(sourceType, "item");
var propertyInfo = FindMyProperty(sourceType, propertyName);
var orderByProperty = Expression.Property(parameter, propertyInfo);
var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderByDescending(source, (dynamic) orderBy);
}
private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
return type.GetProperty(propertyName);
}
At this point your question has nothing to do with expressions, it's a simple reflection question, and you have to find the correct way to get the properties you want. There are a lot of scenarios here. For your exact one, meaning get property from parent interface, you can do something like:
private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
var result = type.GetProperty(propertyName);
if (result == null)
{
foreach(var iface in type.GetInterfaces())
{
var ifaceProp = FindMyProperty(iface, propertyName);
if (ifaceProp != null)
return ifaceProp;
}
}
return result;
}
DISCLAIMER!
This is by no means the best way to get a property from a type, but it should work in your case. You should google around to find an implementation for FindMyProperty that satisfies all your requirements :)
Edit I think I can ask better (no code needed at all in this case). So the question in general is: how to use an Expression tree to build a call to a generic method (Select<TEntity,TResult> in my case) when TResult is created at runtime? Ignore all the code and text below, that was unclear version of the question, left it to not confuse those who answered.
I need an example how to build an Expression tree for a "Select" call. The problem is that I don't know the result type at compile time (it can be defined by user through some GUI in runtime). Here is some code of how I'm trying to do this (remember I can't use any generics)
class Pet {
public int Id { get; set; }
public string Name { get; set; }
}
Using this class in Main:
List<Pet> list = new List<Pet>();
Expression eList = Expression.Constant(list);
ParameterExpression pe = Expression.Parameter(typeof(Pet), "p");
MethodInfo method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
Expression selectorBody = Expression.Call(method, Expression.Constant(properties));
Expression selector = Expression.Lambda(selectorBody, pe);
Expression res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);
What I'm trying to do is create a Type at runtime using Reflection.Emit (method "CreateType" in code above, it is used in some of my projects and looks fine) and somehow pass it to call to "Select", but I get exception:
No generic method 'Select' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments.
Any suggestions?
Upd my real problem is building an Expression tree for a "Join" call for runtime types which is more complicated, so i decided to ask about "Select", because I'm having the same exception in the last line (Expression.Call(...))
Upd2 included my helper methods and edited main code, however this is not the main problem.
static Type CreateType(IEnumerable<PropertyInfo> properties) {
TypeBuilder typeBuilder = CreateTypeBuilder("ResultDynamicAssembly", "ResultModule", "ResultType");
foreach (PropertyInfo propertyInfo in properties) {
CreateAutoImplementedProperty(typeBuilder, propertyInfo.Name, propertyInfo.PropertyType);
}
return typeBuilder.CreateType();
}
static object CreateObject(IEnumerable<PropertyInfo> properties) {
Type type = CreateType(properties);
return Activator.CreateInstance(type);
}
You can simplify this problem by removing the dynamic type from the equation. You can reproduce the same issue with the code below, which does exactly the same thing, but without the dynamic type.
static class Program
{
private static void Main(string[] args)
{
var list = new List<Pet>();
var eList = Expression.Constant(list);
var pe = Expression.Parameter(typeof(Pet), "p");
var method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
var selectorBody = Expression.Call(method, Expression.Constant(properties));
var selector = Expression.Lambda(selectorBody, pe);
var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);
}
private static Type CreateType(IEnumerable<PropertyInfo> properties)
{
return typeof (DynamicType);
}
private static object CreateObject(IEnumerable<PropertyInfo> properties)
{
var type = CreateType(properties);
return Activator.CreateInstance(type);
}
class Pet
{
public int Id { get; set; }
public string Name { get; set; }
}
class DynamicType
{
public string Name { get; set; }
}
}
So the problem is the method signature of CreateObject. Since its return type isn't the dynamic type, the lambda isn't valid. You can see this by changing the type of CreateObject.
// this works fine
private static DynamicType CreateObject(IEnumerable<PropertyInfo> properties)
{
var type = CreateType(properties);
return (DynamicType) Activator.CreateInstance(type);
}
Since you're dealing with a dynamic type, you need to build an expression to cast the result of CreateObject to your dynamic type. Try using something like this:
// create dynamic type
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
var dynamicType = CreateType(properties);
// build expression
var list = new List<Pet>();
var eList = Expression.Constant(list);
var pe = Expression.Parameter(typeof(Pet), "p");
var createObjectMethod = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var createObjectCall = Expression.Call(createObjectMethod, Expression.Constant(properties));
var castExpression = Expression.Convert(createObjectCall, dynamicType);
var selectorExpression = Expression.Lambda(castExpression, pe);
var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), dynamicType }, eList, selectorExpression);
Create a factory for each type. You know what the source so it is really simple:
interface PetResultFactory<T>
{
string TypeName {get; }
List<T> Transform(IEnumerable<Pet> pets);
}
You have to register all possible types in container and show the registered types on the UI for the users. When a user selects one you know how to create that type becouse it is described in the factory.