6

約 30 件のスタック オーバーフローの質問と約 20 件のブログを読みましたが、答えが見つかりません。そうは言っても、私が必要とする答えはそこにあると確信しているので、それを知っている場合は指摘してください(私の要件に合わない答えについての最後の段落/文に注意してください). ありがとう!

次のような形のクラスのインスタンスを永続化しています。

public class Data {
    public int Id { get; set; }
}

public class Container {
    public int Id { get; set; }
    public Data Data { get; set; }
}

現在、クエリは、コンテナを受け入れて bool (述語) を返すラムダ式を必要とする抽象化レイヤーを介してコンテナを検索するように記述されています。Entity Framework 5 が選択された ORM であっても、IQueryable は受け入れられません。

私の仕事は、データ型を受け入れるラムダ式に基づく API サーフェスを提示することです。抽象レイヤーを変更できない (コンテナーを受け入れる述語を渡す必要がある) ため、受け取った式を次のように変換しようとしています。

Expression<Func<Data , bool>>
to:
Expression<Func<Container , bool>>

次のように、リポジトリ クラスに追加のメソッドを追加しました。

public Container Find( Expression<Func<Data , bool>> predicate ) {

    IEnumerable<Container> result = QueryStrategy.Fetch( c => predicate.Compile().Invoke( c.Data ) );

    return result.FirstOrDefault();

}

これは、既存の Find メソッドを補完します。

public Container Find( Expression<Func<Container , bool>> predicate ) {

    IEnumerable<Container> result = QueryStrategy.Fetch( predicate );

    return result.FirstOrDefault();

}

前者の方法を使用すると、次の例外が発生します。

LINQ to Entities はメソッド 'Boolean Invoke(Container.Data)' メソッドを認識せず、このメソッドはストア式に変換できません。

Expression クラスであらゆる種類のことを試しましたが、マップする方法がわかりません。

Expression<Func<Data , bool>>
to:
Expression<Func<Container , bool>>

Entity Framework でサポートされていない Invoke を使用しない場合 (ただし、メモリ内の列挙可能なデータでは正常に動作します)。

式を使用して上記のシナリオを機能させるのを手伝ってくれる人はいますか?

LinqKit ライブラリを使用してこれを解決できる可能性があることは理解していますが、サードパーティのライブラリを持ち込まずにこの問題を解決したいと考えています。

更新: 単純化された問題を提示するために、コードが IQueryable にアクセスできること、および/または LinqKit の AsExpandable() を使用するのに適していることを (コード サンプルで最初に DbContext を使用することによって) 暗示しました。実際にはそうではありません。リポジトリ クラスでは、IQueryable やベンダー固有の拡張機能を使用することは許可されていません。上記の例を修正しました。うまくいけば、これがより明確になることを願っています。

この問題は解決されました

4

2 に答える 2

3

Expressions(驚くべきテクノロジー)を4時間読んだりいじったりした後、なんとか問題を解決することができました。

私はこのクラスをまとめました(洗練されたものでも最終的なものでもありませんが、それがどのように達成されたかを示しています):

class ParameterRewriter<TTarget , TSource> : ExpressionVisitor {

    private ParameterExpression Source;
    private MemberExpression Target;

    public Expression<Func<TTarget , bool>> Rewrite( Expression<Func<TSource , bool>> predicate , Expression<Func<TTarget , TSource>> propertyNameExpression ) {

        var parameter = Expression.Parameter( typeof( TTarget ) );

        var propertyName = ( propertyNameExpression.Body as MemberExpression ).Member.Name;

        Source = predicate.Parameters.Single();
        Target = Expression.PropertyOrField( parameter , propertyName );

        var body = Visit( predicate.Body );

        return Expression.Lambda<Func<TTarget , bool>>(
            body ,
            parameter
        );

    }

    protected override Expression VisitParameter( ParameterExpression node ) {

        if ( node == Source ) {
            return Target;
        }

        return base.VisitParameter( node );

    }

}

そしてそれはこのように使用することができます:

var parameterRewriter = new ParameterRewriter<Container , Data>();
Expression<Func<Data , bool>> dataPredicate = d => ( d.Id == 1 );
var containerPredicate = parameterRewriter.Rewrite( dataPredicate , c => c.Data );

理論的には、propertyNameExpressionを調べることで、より深い関係をたどることができるはずですが、今日は十分です。

これで、クエリのフレーバーごとにSQLEntityFrameworkが生成したものが同じであることがわかります。

========================

Expression<Func<Container , bool>> p = c => c.Data.Id == 1

SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Data_Id] AS [Data_Id]
FROM [dbo].[Container] AS [Extent1]
WHERE 1 = [Extent1].[Data_Id]

========================

Expression<Func<Data , bool>> p = d => d.Id == 1

SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Data_Id] AS [Data_Id]
FROM [dbo].[Container] AS [Extent1]
WHERE 1 = [Extent1].[Data_Id]

========================

于 2012-12-06T05:03:32.823 に答える
1

述語データでフィルタリングされた顧客を結合し、結果から顧客を選択します

public Container Find(Expression<Func<Data, bool>> predicate ) 
{
    return MyDbContext.Containers
                      .Join(MyDbContext.Containers.Select(c => c.Data)
                                                  .Where(predicate), 
                            c => c.Data.Id,
                            d => d.Id,
                            (c, d) => c)
                      .FirstOrDefault();
}
于 2012-12-06T00:21:31.950 に答える