2013-10-28 7 views
6

は、私はこれらの3つのテーブルを結合し、セレクタとしてExpression<Func<TableA, TableB, TableC, T>>を受け入れる関数を定義したい以下のテーブル構造LINQ倍でのIQueryableを結合結果セレクタ式

--------- 
TableA 
ID 
Name 

--------- 
TableB 
ID 
TableAID 

--------- 
TableC 
ID 
TableBID 

を想像して変更します。

だから私は、次のようなものたい:上記の私はそれがやりたいしません

public IQueryable<T> GetJoinedView<T>(Expression<Func<TableA, TableB, TableC, T>> selector) 
{ 
    return from a in DbContext.Set<TableA>() 
      join b on DbContext.Set<TableB>() a.ID equals b.TableAID 
      join c on DbContext.Set<TableC>() b.ID equals c.TableBID 
      select selector; 
} 

を、明らかに、これは私の表現型のIQueryableを与えるだろう。私はメソッド連鎖構文を使うことができましたが、結局、メソッド連鎖呼び出しごとに複数のセレクタが必要になりました。これを行うことができる方法についての

public IQueryable<T> GetJoinedView<T>(Expression<Func<TableA, TableB, TableC, T>> selector) 
{ 
    var query = from a in DbContext.Set<TableA>() 
       join b on DbContext.Set<TableB>() a.ID equals b.TableAID 
       join c on DbContext.Set<TableC>() b.ID equals c.TableBID 
       select new 
       { 
        A = a, B = b, C = c 
       }; 

    // I need the input selector to be modified to be able to operate on 
    // the above anonymous type 
    var resultSelector = ModifyInputSelectorToOperatorOnAnonymousType(selector); 

    return query.Select(resultSelector); 
} 

任意のアイデア:セレクタを取ると、次の不完全な機能のように匿名型に適用する方法はありますか?

答えて

10

あなたが代わりに匿名型を使用してのに選択するように使い捨ての仲介オブジェクトを定義することができます。

public class JoinedItem 
{ 
    public TableA TableA { get; set; } 
    public TableB TableB { get; set; } 
    public TableC TableC { get; set; } 
} 

新しい方法:

public IQueryable<T> GetJoinedView<T>(Expression<Func<JoinedItem, T>> selector) 
{ 
    return DbContext.Set<TableA>() 
        .Join(DbContext.Set<TableB>(), 
          a => a.ID, 
          b => b.TableAID, 
          (ab) => new { A = a, B = b}) 
        .Join(DbContext.Set<TableC>(), 
          ab => ab.B.ID, 
          c => c.TableBID 
          (ab, c) => new JoinedItem 
           { 
            TableA = ab.A, 
            TableB = ab.B, 
            TableC = c 
           }) 
        .Select(selector); 
} 

は、あなたが本当に作るのに十分なこれらの3つのテーブルに参加しますがあなたがLINQで直接何をしたいかを表現するだけでなく、この方法を使うのがより明確になりますか?私は、毎回このクエリを作成するのに必要な余分な行が、このメソッドを使うよりはっきりしていると主張します。

+0

メソッドはいくつかの異なる場所から呼び出され、データベースで選択を実行できる必要があります。提供されるソリューションがデータベース上で実行されるとは思わないが、実際にはTargetInvocationExceptionが発生する可能性があります。 – mhand

+0

EFはIQueryableの式ツリーでマッピングされていない型を使用できるように修正しました。 – mhand

+1

ええ、得られた式ツリーがSQLに変換できる限り、それはすべて良いです。 – Ocelot20

0

は、多分それはあなたが探している解決策ではないですが、私はそれを投稿します。

public class JoinedDataModel 
{ 
    public TableA DataA { get; set; } 
    public TableB DataB { get; set; } 
    public TableC DataC { get; set; } 
} 

私は、それぞれ次のようなあなたのデータベースに対して実行する「選択」のためのDataModelをお勧めします

あなたの「選択は、」あなたはすでに

public IQueryable<JoinedDataModel> GetJoinedView() 
{ 
    return from a in DbContext.Set<TableA>() 
      join b on DbContext.Set<TableB>() a.ID equals b.TableAID 
      join c on DbContext.Set<TableC>() b.ID equals c.TableBID 
      select new JoinedDataModel() 
      { 
       DataA = a, 
       DataB = b, 
       DataC = c 
      }; 
} 

を行い、その後、あなたはあなたの「セレクタ」を表しているか、少なくとも私はあなたがselectoを意味だと思う何マッパー」のいくつかの種類を必要とすると同じことをR:

public static Mapper() 
{ 
    private static Dictionary<MapTuple, object> _maps = new Dictionary<MapTuple, object>(); 

    public static void AddMap<TFrom, TTo>(Action<TFrom, TTo, DateTime> map) 
    { 
     Mapper._maps.Add(MapTuple.Create(typeof(TFrom), typeof(TTo)), map); 
    } 

    public static TTo Map<TFrom, TTo>(TFrom srcObj) 
    { 
     var typeFrom = typeof(TFrom); 
     var typeTo = typeof(TTo); 
     var key = MapTuple.Create(typeFrom, typeTo); 
     var map = (Action<TFrom, TTo, DateTime>) Mapper._maps[key]; 

     TTo targetObj = new TTo(); 

     map(srcObj, targetObj); 

     return targetObj; 
    } 

、あなたは、少なくとも1つのマッピング方法を定義する必要があります。

AddMap<JoinedDataModel, YourResultModel>( 
    (src, trg) => 
    { 
     trg.SomePropertyA = src.DataA.SomeProperty; 
     trg.SomePropertyB = src.DataB.SomeProperty; 
    } 
); 

を、あなたは簡単に呼び出すことができます。

public IList<YourResultModel> CallDb() 
{ 
     return (from item in GetJoinedView() 
       select Mapper.MapTo<JoinedDataModel, YourResultModel>(item) 
      ).ToList(); 
} 

私はあなたには、いくつかの種類に渡したいことを知っていますメソッドへのExpressionの私はこれが動作するつもりはないと思うが、多分誰かが解決策を考え出した。

1

私たちができることは、データを匿名オブジェクトに参加させるという正確な方法から始めようということです。当社の実際のための今すぐ

public class ReplaceVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public ReplaceVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

public static Expression Replace(this Expression expression, 
    Expression searchEx, Expression replaceEx) 
{ 
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); 
} 

:私たちがやる

最初の事は、私たちは与えられた式で別の式と1式のすべてのインスタンスを置き換えるためにできるようにするには、このシンプルなヘルパークラスとメソッドから始めています方法。 3つのパラメータコンストラクタを使用してこれらの匿名オブジェクトのシーケンスをマップするには、入力メソッドを最初のパラメータにマッピングする表現と他の2つのパラメータのセレクタを表す式を受け入れる方法があります。その後、 "実際の"セレクタの本体の最初のパラメータが最初のパラメータのセレクタの本体と一致する場合、すべてのインスタンスを置き換えることができます。

匿名型の型推論を可能にするために、最初にパラメータを追加する必要があることに注意してください。今

public static Expression<Func<TInput, TOutput>> 
    ModifyInputSelectorToOperatorOnAnonymousType 
    <TInput, TOutput, TParam1, TParam2, TParam3>(
    //this first param won't be used; 
    //it's here to allow type inference 
    IQueryable<TInput> exampleParam, 
    Expression<Func<TInput, TParam1>> firstSelector, 
    Expression<Func<TInput, TParam2>> secondSelector, 
    Expression<Func<TInput, TParam3>> thirdSelector, 
    Expression<Func<TParam1, TParam2, TParam3, TOutput>> finalSelector) 
{ 
    var parameter = Expression.Parameter(typeof(TInput), "param"); 

    var first = firstSelector.Body.Replace(firstSelector.Parameters.First(), 
     parameter); 
    var second = secondSelector.Body.Replace(secondSelector.Parameters.First(), 
     parameter); 
    var third = thirdSelector.Body.Replace(thirdSelector.Parameters.First(), 
     parameter); 

    var body = finalSelector.Body.Replace(finalSelector.Parameters[0], first) 
     .Replace(finalSelector.Parameters[1], second) 
     .Replace(finalSelector.Parameters[2], third); 

    return Expression.Lambda<Func<TInput, TOutput>>(body, parameter); 
} 

は我々だけで型推論を満たすために、クエリに渡すことができ、それを呼び出すために、用セレクタ匿名オブジェクトの第一、第二、第三のパラメータだけでなく、私たちの最終的なセレクター:

var resultSelector = ModifyInputSelectorToOperatorOnAnonymousType(
    query, x => x.A, x => x.B, x => x.C, selector); 

残りの部分は既にあります。

関連する問題