【问题标题】:Lambda Expression with two IN parameters to Lambda Expression with single IN parameter具有两个 IN 参数的 Lambda 表达式到具有单个 IN 参数的 Lambda 表达式
【发布时间】:2014-06-23 13:29:09
【问题描述】:

我有具有以下签名的扩展方法:

public static class GenericSeeder
{
    public static void Seed<TSeed, TEntity>(this DbContext context, IEnumerable<TSeed> seeds, Expression<Func<TEntity, TSeed, bool>> predicate)
    {
        // code that I'm looking for goes here
    }
}

为了更好地理解该方法的作用,以下是它的使用方法:

context.Seed<SeedClass, EntityClass>(seeds, (entity, seed) => entity.Name == seed.OtherProperty);

所以基本上,我使用谓词来检查种子是否已经被应用。但是,为了进行检查,我必须使用 Linq to Entities 中的 Where 或 FirstOrDefault,其参数如下:

Expression<Func<TEntity, bool>> predicate

所以我的 lambda 表达式是 2 个 IN 参数(TSeed、TEntity)和 1 个 OUT 参数(bool)的函数。我需要遍历提供的 TSeed 对象集合,并且对于每个对象,使用该对象作为我的 lambda 表达式的参数,以生成具有 1 个 IN 参数(TEntity)和 1 个 OUT 参数(bool)的 LINQ 2 Entities lambda 表达式。

有没有办法部分调用 lambda 表达式 / func 以获得另一个 lambda 表达式 / func?

【问题讨论】:

  • 虽然我想我知道你在问什么,但我不是 100% 确定。您可以使用 (x,y)=&gt;{x.Frob();return y==x;} 之类的东西或类似的东西来执行多语句 lambda(在我的脑海中编译得很好,可能不是用真正的编译器)。这将允许您在内部仅使用一个属性然后再使用另一个属性进行操作。我不能保证除了 linq to objects 之外的任何 linq 提供者都能够用它做事(实际上可能不会)。
  • 谓词中的比较总是== ?

标签: c# linq generics lambda


【解决方案1】:

通过使用 LINQKit 允许以将表达式转换为其他表达式的方式调用表达式,您的方法的实现变得相当简单:

public static IQueryable<TEntity> Seed<TSeed, TEntity>(
    this DbContext context,
    IEnumerable<TSeed> seeds,
    Expression<Func<TEntity, TSeed, bool>> predicate)
{
    return context.Set<TEntity>()
            .AsExpandable()
            .Where(entity => seeds.Any(seed => predicate.Invoke(entity, seed)));
}

【讨论】:

  • 抱歉,尽管您的答案对于特定场景具有价值(因此赞成),但对于我的场景,由于实现的其余部分,它并不是更优化。您可能会节省表达式到 T-SQL 转换的时间,但另一方面,我将不得不再次遍历查询结果并为每个种子找到匹配的实体(因为随着查询的增长,您会同时获取所有内容如果使用太多种子,可能会非常大)。
  • @BarisaPuter 如果您的方法接受适当的投影作为参数,或者只是期望调用者对给定输出的种子进行分组,您可以轻松地按种子值对实体进行分组。以任何一种方式对值进行分组所花费的精力并不是特别大。如果您有很多种子,那么进行 很多 次 DB 往返以获取所有种子是一个 问题。每次往返都非常昂贵。
【解决方案2】:

我不知道你在做什么,但这就是你在 c# 中做部分应用的方式:

Func<int,bool, string> twoInFunc= (int a, bool b) => a.ToString() + b.ToString();

int number = 7;

Func<bool, string> oneInFunc= (bool b) => twoInFunc(number,b);

【讨论】:

  • 不幸的是,您的解决方案对我没有帮助,因为由于 Linq 2 实体的要求,我需要表达式转换。如果我尝试您的建议,我最终会得到:LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。
【解决方案3】:

我已经设法自己创建了解决方案。然而,我确实使用了臭名昭著的 LinqKit 扩展库,它是 AsExpandable() 扩展方法。

LinqKit 可以在这里找到:NuGet link

所以这是适用于 Linq 2 实体的实现:

public static void Seed<TSeed, TEntity>(this DbContext context, IEnumerable<TSeed> seeds, Expression<Func<TEntity, TSeed, bool>> predicate)
            where TEntity : class
            where TSeed : class
        {
            foreach (TSeed seed in seeds)
            {
                Expression<Func<TEntity, bool>> matchExpression = (entity) => predicate.Invoke(entity, seed);
                TEntity existing = context.Set<TEntity>().AsExpandable().FirstOrDefault(matchExpression);

            // Rest of code is omitted as it is not related to the original question.
            // The query above is properly executed by Linq 2 Entities.
            }
        }

【讨论】:

  • 这是将查询评估为每个种子的值。你不应该那样做。
  • @Servy 你能提供更好的解决方案吗?
  • 是的,我可以提供更好的解决方案。
  • @Servy 你介意吗? :) 虽然我不担心性能,因为这是仅在迁移运行时才会调用的方法,但我确实有兴趣查看比这更好的方法 :)
猜你喜欢
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-06
  • 2017-03-29
  • 1970-01-01
相关资源
最近更新 更多