c# - LINQ-to-SQL : Convert Func<T, T, bool> 到表达式<Func<T, T, bool>>

标签 c# linq-to-sql lambda expression-trees

LINQ-to-SQL 对我来说就像一个 PITA。我们使用它与数据库通信,然后通过 WCF 将实体发送到 Silverlight 应用程序。一切正常,直到开始编辑 (CUD) 实体及其相关数据。

我终于能够设计出两个允许 CUD 的 for 循环。我试图重构它们,我是如此接近,直到我了解到我不能总是用 L2S 做 Lambda。

public static void CudOperation<T>(this DataContext ctx, IEnumerable<T> oldCollection, IEnumerable<T> newCollection, Func<T, T, bool> predicate)
    where T : class
{
    foreach (var old in oldCollection)
    {
        if (!newCollection.Any(o => predicate(old, o)))
        {
            ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(o => predicate(old, o)));
        }
    }

    foreach (var newItem in newCollection)
    {
        var existingItem = oldCollection.SingleOrDefault(o => predicate(o, newItem));
        if (existingItem != null)
        {
            ctx.GetTable<T>().Attach(newItem, existingItem);
        }
        else
        {
            ctx.GetTable<T>().InsertOnSubmit(newItem);
        }
    }
}

调用者:

ctx.CudOperation<MyEntity>(myVar.MyEntities, newHeader.MyEntities,
    (x, y) => x.PkID == y.PkID && x.Fk1ID == y.Fk1ID && x.Fk2ID == y.FK2ID);

这几乎成功了。但是,我的 Func 需要是一个 Expression>,这就是我遇到的问题。

谁能告诉我这是否可行?由于 SharePoint 2010,我们必须使用 .NET 3.5。

最佳答案

只需更改参数:

 Func<T, T, bool> predicate

收件人:

 Expression<Func<T, T, bool>> predicate

表达式由编译器生成。

现在,问题是如何使用它。

在你的情况下,你需要一个 Func 一个Expression ,因为您在 Enumerable 中使用它LINQ 查询(基于函数)以及 SQL LINQ 查询(基于表达式)。

在:

.Where(o => predicate(old, o))

old参数是固定的。所以我们可以将参数更改为:

Func<T, Expression<Func<T, bool>>> predicate

这意味着我们可以提供一个参数(“固定”参数)并返回一个表达式。

foreach (var old in oldCollection)
{
    var condition = predicate(old);
    // ...
    {
        ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition));
    }
}

我们还需要在 Any 中使用它.要从表达式中获取 Func,我们可以调用 Compile() :

foreach (var old in oldCollection)
{
    var condition = predicate(old);
    if (!newCollection.Any(condition.Compile()))
    {
        ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition));
    }
}

你可以对下一部分做同样的事情。

有两个问题:

  1. 使用 Compile() 可能会影响性能很多。我不确定它实际会产生多大的影响,但我会分析它以进行检查。
  2. 现在的用法有点奇怪,因为这是一个 curry lambda。而不是传递 (x,y) => ...你会路过x => y => ... .我不确定这对您来说是否重要。

可能有更好的方法来做到这一点:)

这里有一个替代方法,它应该会快一点,因为表达式只需要编译一次。创建一个将“应用”一个参数的重写器,如下所示:

class PartialApplier : ExpressionVisitor
{
    private readonly ConstantExpression value;
    private readonly ParameterExpression replace;

    private PartialApplier(ParameterExpression replace, object value)
    {
        this.replace = replace;
        this.value = Expression.Constant(value, value.GetType());
    }

    public override Expression Visit(Expression node)
    {
        var parameter = node as ParameterExpression;
        if (parameter != null && parameter.Equals(replace))
        {
            return value;
        }
        else return base.Visit(node);
    }

    public static Expression<Func<T2,TResult>> PartialApply<T,T2,TResult>(Expression<Func<T,T2,TResult>> expression, T value)
    {
        var result = new PartialApplier(expression.Parameters.First(), value).Visit(expression.Body);

        return (Expression<Func<T2,TResult>>)Expression.Lambda(result, expression.Parameters.Skip(1));
    }
}

然后像这样使用它:

public static void CudOperation<T>(this DataContext ctx,
    IEnumerable<T> oldCollection,
    IEnumerable<T> newCollection,
    Expression<Func<T, T, bool>> predicate)
    where T : class
{

    var compiled = predicate.Compile();

    foreach (var old in oldCollection)
    {
        if (!newCollection.Any(o => compiled(o, old)))
        {
            var applied = PartialApplier.PartialApply(predicate, old);
            ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(applied));
        }
    }

    foreach (var newItem in newCollection)
    {
        var existingItem = oldCollection.SingleOrDefault(o => compiled(o, newItem));
        if (existingItem != null)
        {
            ctx.GetTable<T>().Attach(newItem, existingItem);
        }
        else
        {
            ctx.GetTable<T>().InsertOnSubmit(newItem);
        }
    }
}

关于c# - LINQ-to-SQL : Convert Func<T, T, bool> 到表达式<Func<T, T, bool>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9321329/

相关文章:

c# - 如何将cmd命令输出放入标签中

c# - Visual Studio 2012 无法找到我的测试

Linq单向一对多关联

c# - 在 C# 中可以同时声明和调用 lambda 表达式吗?

android - 接收两个数字并返回它们的和与乘积的 Kotlin 函数

java - 如何将方法作为参数传递?

c# - 如何将 Filter 应用于 PagedCollectionView 但防止 CollectionChanged 事件在编辑时触发?

c# - NHibernate效率

c# - linq-to-sql 查询结果到 c# 字典 - 如何

.net - Linq 在特定领域不同