Basta cambiare il parametro da:
Func<T, T, bool> predicate
A:
Expression<Func<T, T, bool>> predicate
l'espressione viene generato dal compilatore.
Ora, il problema è come usare questo.
Nel tuo caso, è necessario sia un Func
e un Expression
, dal momento che si sta utilizzando in Enumerable
query LINQ (basati func), così come le query SQL LINQ (espressione based).
In:
.Where(o => predicate(old, o))
Il parametro old
è fissa. Così abbiamo potuto cambiare il parametro:
Func<T, Expression<Func<T, bool>>> predicate
Questo significa che possiamo fornire un argomento (la 'fissa' uno) e tornare un'espressione.
foreach (var old in oldCollection)
{
var condition = predicate(old);
// ...
{
ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition));
}
}
Abbiamo anche bisogno di usare questo in Any
. Per ottenere un Funz da un'espressione che possiamo chiamare Compile()
:
foreach (var old in oldCollection)
{
var condition = predicate(old);
if (!newCollection.Any(condition.Compile()))
{
ctx.GetTable<T>().DeleteAllOnSubmit(ctx.GetTable<T>().Where(condition));
}
}
Puoi fare la stessa cosa con la parte successiva.
Ci sono due questioni:
- L'andamento potrebbe essere influenzato utilizzando
Compile()
lotti. Non sono sicuro di quanto effetto avrebbe effettivamente, ma lo definirei per verificare.
- L'utilizzo è ora un po 'strano, poiché questo è un lambda al curry. Invece di passare
(x,y) => ...
passerai davanti allo x => y => ...
. Non sono sicuro se questo è un grosso problema per te.
Ci potrebbe essere un modo migliore per fare questo :)
Ecco un metodo alternativo, che dovrebbe essere un po 'più veloce, poiché l'espressione deve solo essere compilato una sola volta. Creare un masterizzatore in grado di 'applicare' un argomento, come questo:
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));
}
}
quindi utilizzarlo in questo modo:
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);
}
}
}
Porges, apprezzo la pronta risposta. Tuttavia, quando lo fai e cambia la clausola .Where in ctx.GetTable(). Dove (predicato), non riesco a compilare. Ho bisogno di passare le due variabili (che devono avere le loro proprietà confrontate) con l'espressione/Func. –
DaleyKD
Ah, capisco, il tuo problema attuale è all'interno del metodo. Mi sono perso - aggiornerò la risposta. – porges
Holy smokes! Questo ha funzionato completamente. Forse lo rivisiteremo per le prestazioni nella versione 1.1. ;) Grazie! – DaleyKD