2012-02-06 12 views
3

主キーに基づいてエンティティの操作を実行する一般的なlinqtoSQLを作成しようとしています。私が働いているいくつかのエンティティは、複合主キーを持っています。エンティティが主キーを持っている唯一の要件でlinqtoSQLの主キーの使用

if(PrimaryKey(foo) == PrimaryKey(bar)) ... 

または、

from oldEntity in oldTableOrCollection 
join newEntity in newTableOrCollection 
on PrimaryKeyOf(oldEntity) equals PrimaryKeyOf(newEntity) 
select new {oldEntity, newEntity} 

基本的に私は、次のようなことを行うことができるようにしたいです。ここで

は、私がこれまでに見つけたものです:

それはlinqtosqlは(プロパティ名が一致して)比較のために匿名型を使用します。ここで、このような

var query = from row in oneTableOfRows 
      join otherRow in anotherTableOfRows on 
      new { row.Column1, row.Column2 } 
      equals 
      new { otherRow.Column1, otherRow.Column2} 
      select ...; 

として物事を行うことが可能です。

私はできジェネリック行タイプを許可するために参加するように列を選択するため、次に抽象メソッド:keySelectorは主キーを選択する場所

void DoQuery<RowType,KeyType>(Table<RowType> oneTableOfRows, 
         Table<RowType> anotherTableOfRows, 
         Func<RowType, KeyType> keySelector) 
{ 
    var query = from row in oneTableOfRow 
      join otherRow in anotherTableOfRows on 
      keySelector(row) 
      equals 
      keySelector(otherRow) 
      select ...; 
} 

... 

Func<rowType, keyType> myKeySelector = row => new { row.Column1, row.Column2 }; 

DoQuery(table, otherTable, myKeySelector); 

は私が今まで移動しようとしている、さ任意のRowTypeの私は自分のエンティティを生成するのにhttp://www.codeplex.com/l2st4に基づくカスタムテンプレートを使用しています(それ自体はVSのデフォルトとかなり似ています)。 私は理想的には、生成された各RowTypeに、dbmlから集められた主キーを選択する機能を与えることを望んでいます。これまでouptutは、次のようになります。

public interface IPrimaryKeyEntity<PrimaryKeyType> 
{ 
    PrimaryKeyType PrimaryKey { get; } 
} 

//Here, Product is a table with composite primary key based on its ProductID and it's ProductPriceVersionID columns 

//I've tried using both class and struct as the primary key type for entities 
public class ProductPrimaryKey 
{ 
    public Guid ProductID; 
    public Guid ProductPriceVersionID; 
} 

public partial class Product : IPrimaryKeyEntity<ProductPrimaryKey>, 
           INotifyPropertyChanging, INotifyPropertyChanged 
{ 
    #region IPrimaryKeyEntity Implementation 
public ProductPrimaryKey PrimaryKey 
    { 
     get 
     { return new ProductPrimaryKey() 
        { 
         ProductID = this.ProductID, 
         ProductPriceVersionID = this.ProductPriceVersionID 
        }; 
     } 
    } 
#endregion 

    ... 
    //Rest is mostly standard LinqToSql entity generation 
} 

元狙いに行く、私は今、すべての私の主キーエンティティの次をコンパイルすることができます

from oldEntity in oldTableOrCollection 
join newEntity in newTableOrCollection 
on oldEntity.PrimaryKey equals newEntity.PrimaryKey 
select new {oldEntity, newEntity} 

ただし、実行時に、私が手悪名高い[PrimaryKey] "SQLへのサポートされた変換がありません"例外。

誰でも向上させることができた場合、私は...私がLinq.Expressionsにかなり慣れていないですし、私の状況にそれを適用しようとまだ進展がなかったてきたがExpression年代を使用する必要があるSQLに変換するために

をそれを理解....私は知って感謝されると思い、これまでに得たか、一般的にはより良い方法を持ってきたもので

乾杯

+1

デバッガで(動作中の)クエリ用に生成された 'Expression'を探索するのが良いでしょう。これは「非公開メンバー」>「queryExpression」の下にあります。私もこれに新しいですが、私は何かを見つける場合あなたに教えてあげます:) –

+0

@ VladislavZorov:提案をありがとう!私は明日オフィスに戻ったときにそれが助けになるかどうか確かに分かるでしょう。 – EdF

答えて

2

問題は、あなたがここに機能を呼び出しているということです。

join ... on oldEntity。 PrimaryKeyはnewEntityと等しくなります。 PrimaryKey

property-getは関数呼び出しです。 Linq to SQLはこの関数を知らないので、変換できません。

この作品を作るための唯一の方法は、このSQLに対応して式を構築することです:

 join otherRow in anotherTableOfRows on 
     new { row.Column1, row.Column2 } 
     equals 
     new { otherRow.Column1, otherRow.Column2} 

、私はあなたを確保することができます他の方法はありません。あなたはこのようにそれを行うことができます:まず、カスタムタプルクラスを宣言します。

class CustomTuple2<T1,T2> 
{ 
public T1 Item1 { get; set; } 
public T2 Item2 { get; set; } 
} 

あなたが(例えば、8または16のための)可能なすべてのメンバ数のためにこれを行います。

の参加方法を見てみましょう翻訳さ:

IQueryable<T1> t1 = ...; //table 1 
IQueryable<T2> t2 = ...; //table 2 
var joined = t1.Join(t2, _t1 => _t1.Key, _t2 => _t2.Key); 

ラムダパラメータが表現されています。 式ツリーを使用してこれらの2つの式を構築する必要があります。これは魔法です!例えば

、最初のラムダ:

var param = Expression.Parameter(typeof(T1), "_t1"); 
var key = Expression.Property(param, "Key"); 
var lambda1 = Expression.Lambda(param, key); 

は、第二のラムダのために同じことを行います。 (これは擬似コードであったことを覚えておいて、私は正確にすべてのものが呼び出されたか覚えていません。。)

最後に、結合を呼び出す:

var joined = typeof(Queryable).GetMethod("Join").Invoke(t1, t2, lambda1, lambda2); 

をだから、あなたは、実行時に参加構築方法です!これはすべて擬似コードであることに注意してください。 APIを理解して動作させるためにいくつかの研究が必要ですが、確かに行うことができます。

また、コンポジットキーがある場合、キーメンバーを投影するには前述のタプルクラスが必要です。

+0

ありがとう、有望そうです。私が現在生成しているように、タプルではなく、明示的に定義された 'FooKey'構造体/クラスでこのアプローチが機能するかどうかについての考えはありますか?また、linqtosqlに通常のランタイムプロパティアクセスと同じインタフェースを使用させるために、プロパティで式ツリーの要素を入れておくといいでしょうか?必要なものに応じて式ツリーを完全に評価するか返すかのようなものですか? – EdF

+0

a)明示的なクラスでも動作します。重要なことは、DTOクラスにデフォルトのctorと代入可能な自動プロパティがあることです。 b)あなたが何を意味するのか分かります。これは非常に困難です。あなたが望むのは、L2SとL2Objectsで動作する "計算カラム"を持つことです。おそらく、これを行う最良の方法は、datacontextのIQueryable実装の前にファサードを置き、L2Sにヒットする前に式ツリーを書き直すことです。しかし、これはおそらくあなたの範囲外です。 – usr