3

免責事項:サービス/エンティティ/などの名前をメモ帳のSOの一般的なものに変更しました。クラス名に関して矛盾が見られる場合は、問題ではないので無視してください。

クライアント用の WCF サービスに取り組んでおり、シリアライズしている式に問題があります。現在、Serialize.Linqを使用して式のシリアル化を処理しています。その上で、DataContract クラスを使用してクライアント側で式を作成し、それを Entity クラスを使用して式に変換しています。

次の 2 つのクラスを想定します。

  1. MyEntity (プロジェクトの DataContract 名前空間の一部)
  2. MyEntity (プロジェクトのエンティティ名前空間の一部)

どちらも同じプロパティを持ち、AutoMapper を使用して EF 経由で取得したエンティティを DataContract オブジェクトに変換し、そのオブジェクトをクライアントに送り返します。

式の変換に対処するために、ExpressionVisitor クラスを使用しています。

class MyExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression ParameterExpression { get; private set; }

    public MyExpressionVisitor(ParameterExpression newParameterExp)
    {
        ParameterExpression = newParameterExp;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return ParameterExpression;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.DeclaringType == typeof(DataContracts.MyEntity))
        {
            return Expression
                .MakeMemberAccess(this.Visit(node.Expression), typeof(Entities.MyEntity).GetMember(node.Member.Name).FirstOrDefault());
        }

        return base.VisitMember(node);
    }
}

これは私が自分のサービスを呼び出す方法です:

Expression<Func<DataContracts.MyEntity, bool>> expression =
    fl.SomeNameField1 == "John Doe" || fl.SomeNameField2 == "John Doe";
var entities = manager.MyService.GetFilteredEntities(expression.ToExpressionNode());

これは私の現在のサービス実装です(部分的、戻り行の省略など...)GetFilteredEntities

// Using Serialize.Linq for send expressions over WCF.
// query is an ExpressionNode from the Serialize.Linq library.
// This DOES NOT execute the where clause on the database.
var expression = query.ToExpression<Func<DataContracts.MyEntity, bool>>();
var visitor = new MyExpressionVisitor(Expression.Parameter(typeof(Entities.MyEntity), expression.Parameters[0].Name));
var entityExpression = Expression.Lambda<Func<Entities.MyEntity, bool>>(visitor.Visit(expression.Body), visitor.ParameterExpression);
var func = entityExpression.Compile();
var entities = this.Entities.MyEntities.Where(func);

このコードはすべて機能しますがWhere、データベース レベルでは適用されず、テーブルのすべての行のメモリ内セットに対して適用されます。テーブルには 15 万行以上あるため、これには長い時間がかかります。

サービスで必要な場所をハードコーディングすると、データベース レベルで where 句が適用されます。

// This DOES execute the where clause on the database.
var temp1 = this.Entities.MyEntities.Where(fl => fl.SomeNameField1 == "John Doe" || fl.SomeNameField2 == "John Doe");

// This DOES execute the where clause on the database.
Func<Entities.MyEntity, bool> func2 = fl => fl.SomeNameField1 == "John Doe" || fl.SomeNameField2 == "John Doe";
var temp2 = this.Entities.MyEntities.Where(func2);

ユーザーが名前、IDなどを渡してフィルタリングできるようにする一連のさまざまなサービス操作を作成できることはわかっていますが、このテーブルには途方もない量の列(200以上)があり、上記のデータベースにはゼロの入力があります. 私が書いているクライアントを使用している可能性のある他の開発者にとって、好きなデータと列を使用して式を作成できるようにする方がはるかに簡単なので、db レベルで where を適用してこれを取得したいと思います。

この投稿に関連するすべてを含めたことはほぼ確実です。SQL Server プロファイラーを使用して、EF が実行していたクエリを確認しました。これにより、どのクエリがどこで使用されたかがわかります。詳細については、必要に応じてお問い合わせください。

ありがとう!

/walloftext

4

3 に答える 3

2

おそらく問題は次の行にあります。

var func = entityExpression.Compile();

コンパイルされたデリゲート関数で呼び出しWhereているため、実際に DB に送信される Where のオーバーロードを呼び出していません。

式オブジェクトを渡す必要がありますWhere(つまり、これらのいずれかを呼び出します)

于 2012-10-11T19:22:07.580 に答える
1

Where句で使用する前に式をコンパイルする必要はないと思います。これを行うと、名前空間System.Coreによって提供される拡張メソッドが呼び出されます。System.Linqタイプのオブジェクトを受け取るメソッドのオーバーロードが必要ですExpression

つまり、次の行を削除してみてください。

var func = entityExpression.Compile();
于 2012-10-11T19:28:29.620 に答える
0

を使用LinqKitして複合クエリを作成してみましたか? を使用すると PredicateBuilder、式を簡単に操作できます。

このアドレスを見てください。ソース コードは非常に単純で、私の場合は奇跡を起こしました。

于 2012-10-11T19:21:30.697 に答える