Generate e => new { e.Id, e.CompanyId } with Expressions

本文关键字:CompanyId with Expressions Id new gt Generate | 更新日期: 2023-09-27 18:12:01

这个问题是这个问题的延续。如果有人想知道我为什么要做这样的事情,你可以从这个问题中找到理由。这并不重要,真的。

我需要一个这样的方法:

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 }; 
}

问题是,没有超类型的T,我可以用它来拉出IdCompanyId,我必须动态地做到这一点。由于参考问题的答案,我已经成功地为一个属性(e => e.Id)构建并使用了这种方法,但是我在实现它的两个问题上遇到了问题。仅为了可见性,一个字段的解决方案是:

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);
}

我已经疯了6个多小时了…怎么解呢?

Generate e => new { e.Id, e.CompanyId } with Expressions

这个Lamba的Body是一个MemerInitExpression.

这是容易的部分。这里更大的问题是您在Lambda中使用了匿名类型。

Expression<Func<TranslatedText, object>> exp;
exp = p => new { p.LanguageId, p.TextId};

如果你使用这样的AnonymousType,编译器将检查你的代码,检测AnonymousType声明,并动态地创建一个这样的类型。

public class f__AnonymousType0
{
    public int LanguageId { get; set; }
    public int TextId { get; set; }
}

然后把你的变成这样

exp = p => new f__AnonymousType0 { LanguageId = p.LanguageId, TextId = p.TextId };

因为你想在运行时创建lambda,所以MemberInitExpression所需的f__AnonymousType0类型不存在。

因为你需要一个实际的类型来创建这个表达式,你有两个选择来获得一个。

1 -编写一些泛型类,比如。net框架的Tuple类。在核心中,此解决方案被限制为最大数量的属性。

pro:易于创建和使用- con:限制属性计数。

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 -你可以使用反射。在运行时发出并创建类型http://www.codeproject.com/Articles/121568/Dynamic-Type-Using-Reflection-Emit

pro: unlimited property count - con: complicated

当你有一个类型时,你可以使用表达式树api来创建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);

这将创建一个如下所示的表达式

exp = p => new KeyTuple<int, int> { Item1 = p.LanguageId, Item2 = p.TextId };

尝试使用反射?

请不要,我没有visual studio或任何其他IDE在我面前,所以我的代码包含一些错误和拼写

还注意,如果你不需要properties,你可能需要fields,还有GetField()GetFields()看看它们。

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);
}