【问题标题】:Generate e => new { e.Id, e.CompanyId } with Expressions使用表达式生成 e => new { e.Id, e.CompanyId }
【发布时间】:2014-07-08 11:53:10
【问题描述】:

这个问题是这个问题here 的延续。如果有人想知道我为什么需要做这样的事情,你可以在那个问题中找到理由。没关系,真的。

我需要这样的方法:

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 =&gt; 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 个多小时......那么,我该如何解决这个问题?

【问题讨论】:

  • 您对那些T 子类有任何控制权吗?设置界面会以最优雅的方式解决问题。
  • 我已经在另一个问题中详细解释过,但简而言之,不,这行不通,因为并非所有人都有这些字段,所以有些类会覆盖它,有些则不会。我需要这个作为基础实现。
  • 这真的是你的工作代码吗?它不会为我编译...
  • 是的。你得到什么错误?
  • 如果它们没有相同的字段集,那么您可能只需要几个接口(例如ISomethingWithIdISomethingWithIdAndCompanyId 等)。在我看来,就代码复杂性而言,反射对于几乎所有事情来说都是相当昂贵的解决方案。

标签: c# .net lambda


【解决方案1】:

这个 Lamba 的主体是 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; }
}

然后把你的 lambda 改成这样。

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

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

由于您需要一个实际的类型来创建此表达式,因此您有两种选择。

1 - 编写一些通用类,例如 .NET Framework 的 Tuple 类。此解决方案的核心数量仅限于最大数量的属性。

优点:易于创建和使用 - 缺点:属性数量有限。

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 - 您可以使用 Reflection.Emit 并在运行时创建一个类型 http://www.codeproject.com/Articles/121568/Dynamic-Type-Using-Reflection-Emit

赞成:无限的财产数量 - 反对:复杂

当你有一个类型时,你可以使用你已经知道的表达式树 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 };

【讨论】:

    【解决方案2】:

    尝试使用反射?

    请注意,我面前没有 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);
    }
    

    【讨论】:

    • 如果如 Davor 所说,并非所有类都可以保证实现这两个属性,那么我相信这就是实现它的方法。
    • 从上下文来看,这似乎与实体框架一起使用。我认为这不会翻译。
    • 这行不通,如果他需要表达式树本身而不是值
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-23
    • 1970-01-01
    • 2017-12-08
    相关资源
    最近更新 更多