This question is a continuation of this question here. If someone cares to know why I need to do things like this, you can find the rationale in that question. Not that it matters, really.
I need a method like this:
public virtual Expression<Func<T, object>> UpdateCriterion()
{
// this doesn't work because the compiler doesn't know if T has Id & CompanyId
return e => new { e.Id, e.CompanyId };
}
The problem is, there is no supertype for T
that I could use to pull out Id
and CompanyId
from, I have to do it dynamically. Thanks to the answer to that referenced question, I have successfully built and used this kind of method for one property (e => e.Id
), but I'm having issues implementing it for two. Just for visibility, the solution for one field is:
public virtual Expression<Func<T, object>> UpdateCriterion()
{
var param = Expression.Parameter(typeof(T));
var body = Expression.Convert(Expression.Property(param, "ID"), typeof(object));
return Expression.Lambda<Func<T, object>>(body, param);
}
I've been going nuts with this for over 6 hours... So, how do I solve this?
The Body of this Lamba is a MemerInitExpression.
That was the easy part. The bigger Problem here is that you use an Anonymous Type in your Lambda.
Expression<Func<TranslatedText, object>> exp;
exp = p => new { p.LanguageId, p.TextId};
If you use such an AnonymousType, the Compiler will inspect your code, detect the AnonymousType declaration and will create a Type like this on the fly.
public class f__AnonymousType0
{
public int LanguageId { get; set; }
public int TextId { get; set; }
}
And change your lambda into something like this.
exp = p => new f__AnonymousType0 { LanguageId = p.LanguageId, TextId = p.TextId };
Because you would like to create the lambda at runtime the f__AnonymousType0 type you need for the MemberInitExpression does not exist.
As you need an actual Type to create this Expression you have two options to get one.
1 - Write some generic classes like the Tuple class of the .NET Framework. Of cores this solution is limited to a maximum amount of properties.
pro: easy to create and use – con: limited property count.
public class KeyTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
public class KeyTuple<T1, T2, T3>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
public T3 Item3 { get; set; }
}
public class KeyTuple<T1, T2, T3>
public class KeyTuple<T1, T2, T3, T4>
public class KeyTuple<T1, T2, T3, T4, T5>
public class KeyTuple<T1, T2, T3, T4, T5, T6>
2 - You could use Reflection.Emit and create a type at runtime
http://www.codeproject.com/Articles/121568/Dynamic-Type-Using-Reflection-Emit
pro: unlimited property count – con: complicated
When you have a type you can use the Expression tree api you already know to crate the lambda
var keys = new[] { "LanguageId", "TextId" };
var param = Expression.Parameter(typeof(TranslatedText));
var properties = keys.Select(p => Expression.Property(param, p)).ToList();
var keyTupleType = typeof(KeyTuple<,>).Assembly.GetType(string.Format("AnonymousTypeExpression.KeyTuple`{0}",keys.Count()));
keyTupleType = keyTupleType.MakeGenericType(properties.Select(p => p.Type).ToArray());
var bindings = properties.Select((p,i) => Expression.Bind(keyTupleType.GetProperty(string.Format("Item{0}",i + 1)),p)).ToArray();
var body = Expression.MemberInit(Expression.New(keyTupleType), bindings);
var result= Expression.Lambda<Func<TranslatedText, object>>(body, param);
This creates an expression that looks like this
exp = p => new KeyTuple<int, int> { Item1 = p.LanguageId, Item2 = p.TextId };
Try using reflection ?
Please not i don't have visual studio or any other IDE in front of me so the code my contain some bugs and typos
Also notes if you don't need properties
, you may need fields
there are also GetField()
and GetFields()
look them around.
public virtual Expression<Func<T, object>> UpdateCriterion()
{
return e => new { GetPropertyValue<T>(e,"id"), GetPropertyValue<T>(e,"CompanyId") };
}
public object GetPropertyValue<T>(T TargetObject,string PropertyName)
{
var prop = typeof(T).GetProperty(PropertyName).GetValue(TargetObject, null);
}