只需更改参数:
Func<T, T, bool> predicate
To:
Expression<Func<T, T, bool>> predicate
表达式由编译器生成。
现在的问题是如何使用它。
就您而言,您需要两个Func
and an Expression
,因为你在使用它Enumerable
LINQ 查询(基于函数)以及 SQL LINQ 查询(基于表达式)。
In:
.Where(o => predicate(old, o))
The 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));
}
}
您可以对下一部分做同样的事情。
有两个问题:
- 性能可能会受到使用的影响
Compile()
很多。我不确定它实际上会产生多大的影响,但我会对其进行分析以进行检查。
- 现在的用法有点奇怪,因为这是一个柯里化的 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);
}
}
}