LINQ-to-SQL:将Func <t,t,=“”bool =“”>转换为表达式<func <t,t,=“”bool =“”>&gt; </func <t,> </吨,>

时间:2012-02-17 00:38:15

标签: 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需要是一个表达式&gt;,而这就是我被困住的地方。

有没有人可以告诉我这是否可行?由于SharePoint 2010,我们必须使用.NET 3.5。

1 个答案:

答案 0 :(得分:11)

只需更改参数:

 Func<T, T, bool> predicate

要:

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

表达式由编译器生成。

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

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

在:

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

old参数已修复。所以我们可以将参数更改为:

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

这意味着我们可以提供一个参数(&#39;固定的&#39;一个)并获得一个表达式。

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. 使用现在有点奇怪,因为这是一个咖喱lambda。您将通过(x,y) => ...而不是传递x => y => ...。我不确定这对你来说是不是很重要。
  3. 可能有更好的方法:)

    这是另一种方法,它应该更快一些,因为Expression只需要编译一次。创建一个将会应用&#39;的重写器。一个论点,像这样:

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