Просто измените параметр с:
Func<T, T, bool> predicate
На:
Expression<Func<T, T, bool>> predicate
Выражение генерируется компилятором.
Теперь вопрос заключается в том, какиспользуйте это.
В вашем случае вам понадобятся и Func
, и , и Expression
, поскольку вы также используете его в Enumerable
LINQ-запросах (на основе func).как запросы SQL LINQ (на основе выражений).
In:
.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));
}
}
Вы можете сделать то же самое со следующей частью.
Есть две проблемы:
- На производительность может повлиять использование
Compile()
лотов.Я не уверен, какой эффект это будет на самом деле, но я бы профилировал это, чтобы проверить. - Использование теперь немного странно, так как это карри лямбда.Вместо прохождения
(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);
}
}
}