2

コード自体に語らせます。

public interface ISoftDeletable {
  bool IsDeleted {get; set;}
}

public static class Extensions {
  public IQueryable<T> Active<T>(this IQueryable<T> q) where T : ISoftDeletable {
    return q.Where(t => !t.IsDeleted);
  }
}

public partial class Thing : ISoftDeletable {
  ...
}

...
var query = from tc in db.ThingContainers
            where tc.Things.Active().Any(t => t.SatisfiesOtherCondition)
            select new { ... }; // throws System.NotSupportedException

エラーは次のとおりです。

LINQ to Entities はメソッド 'System.Collections.Generic.IQueryable`1[Thing] ActiveThing' メソッドを認識せず、このメソッドをストア式に変換できません。

ISoftDeletableお分かりいただけると思いますが、単純で再利用可能なコードを使用して「where」句を追加できるように、これを流暢に表現する方法が必要です。Active()Linq2Entities は私のメソッドで何をすべきかわからないため、ここの例は機能しません。

ここで示した例は単純なものですが、実際のコードでは、Active()拡張機能にはさらに複雑な条件のセットが含まれているため、コード全体にそれをコピーして貼り付けたくはありません。

助言がありますか?

4

2 に答える 2

2

コードに 2 つの無関係な問題があります。1 つ目は、拡張メソッドが行う式のキャストを Entity Framework が処理できないことです。

この問題の解決策はclass、拡張メソッドに制限を追加することです。その制限を追加しない場合、式はキャストを含むようにコンパイルされます。

.Where (t => !((ISoftDeletable)t.IsDeleted))

上記のキャストは Entity Framework を混乱させるため、実行時エラーが発生します。

制限が追加されると、式は単純なプロパティ アクセスになります。

 .Where (t => !(t.IsDeleted))

この式は、エンティティ フレームワークで問題なく解析できます。

2 つ目の問題は、クエリ構文ではユーザー定義の拡張メソッドを適用できないが、Fluent 構文では使用できることです。

db.ThingContainers.SelectMany(tc => tc.Things).Active()
    .Any(t => t.SatisfiesOtherCondition); // this works

問題を確認するには、実際に生成されたクエリがどうなるかを確認する必要があります。

 db.ThingContainers
       .Where(tc => tc.Things.Active().Any(t => t.StatisfiesOtherCondition))
       .Select(tc => new { ... });

Active() 呼び出しは実行されませんが、EF が解析する式として生成されます。案の定、EF はそのような関数をどう処理するかを知らないため、救済します。

明らかな回避策 (常に可能というわけではありませんが) は、クエリをThingではなく sで開始することThingContainersです。

db.Things.Active().SelectMany(t => t.Container);

もう 1 つの考えられる回避策は、モデル定義関数を使用することですが、これはより複雑なプロセスです。詳細については、これこれ、およびこのMSDN の記事を参照してください。

于 2013-08-29T17:13:27.830 に答える
0

@felipeは回答のクレジットを獲得しましたが、代わりに自分の回答を投稿することも考えました。

var query = from tc in db.ThingContainers.Active() // ThingContainer is also ISoftDeletable!
            join t in db.Things.Active() on tc.ID equals t.ThingContainerID into things
            where things.Any(t => t.SatisfiesOtherCondition)
            select new { ... };

これには、クエリの構造を多かれ少なかれ同じに保つという利点がありますが、 と の間の暗黙的な関係の流暢さは失われThingContainerますThing。私の場合、条件を明示的に指定するよりも、関係を明示的に指定する方がよいというトレード オブ バランスがうまくいきますActive()

于 2013-08-29T20:50:46.913 に答える