2012-02-17 12 views
5

LINQ-to-SQLは私のPITAでした。これを使ってデータベースと通信し、WCF経由でエンティティをSilverlightアプリケーションに送信します。エンティティとその関連データの編集(CUD)を開始する時間が来るまで、すべてうまくいっていました。LINQ-to-SQL:Func <T, T, bool>を式に変換する<Func <T, T, bool>>

私は最終的にCUDを可能にする2つのforループを考案できました。私はそれらをリファクタリングしようとしました。私はいつもL2Sでラムダをすることができないことを学ぶまで、私はとても近くにいました。呼び出され

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>でなければなりません。

これが可能かどうか教えてもらえますか?

Func<T, T, bool> predicate 

へ:

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

発現は、コンパイラによって生成された私達はちょうどからのパラメータを変更するため、SharePointの2010

答えて

10

に.NET 3.5でなければなりません。

ここで、これを使用する方法が問題です。あなたが(ベースの発現)Enumerable(FUNCベース)LINQクエリだけでなく、SQLのLINQクエリでそれを使用しているので、あなたの場合は

、あなたは、両方のFuncExpressionを必要としています。 oldパラメータが固定されている

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

:で

。これは、我々は一つの引数(「固定」1)を供給し、式を取り戻すことができることを意味

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()多くを使用することによって影響を受ける可能性がある

  1. は二つの問題があります。どのくらいの効果があるのか​​は分かりませんが、私はそれを確認するためにプロファイルします。

  2. これはカレーラムダなので、少し奇妙です。 (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); 
     } 
    } 
} 
+0

Porgesを、私は迅速な対応に感謝しています。しかし、あなたがそれをし、私の.Where節をctx.GetTable ()に変更すると、どこか(述語)、私はコンパイルできません。私は2つの変数(プロパティを比較する必要がある)をExpression/Funcに渡す必要があります。 – DaleyKD

+0

ああ、わかった、あなたの実際の問題は方法内にある。私はそれを逃した - 私は答えを更新します。 – porges

+0

聖なる煙!それは完全に働いた。おそらく、バージョン1.1のパフォーマンスについてこれを再検討します。 ;) ありがとうございました! – DaleyKD

関連する問題