12

オブジェクトに対してgroupbyを実行することは可能ですか?

from item in context.Items
group item by item.MyObject
select ... 

Item.MyObjectは、次のような単純なオブジェクトです。

public class MyObject {
    public int SomeValue { get; set; }
    public string SomeName { get; set; }
    public string SomeOtherProperty { get; set; }
}

明らかに、私は次のことを行うことができます:

from item in context.Items
group item by new { item.SomeValue, item.SomeName, item.SomeOtherProperty }
select ... 

ただし、多くのプロパティを持つオブジェクトでグループ化する場合、このアプローチは面倒でエラーが発生しやすくなります。

上記のコードにより、NotSupportedExceptionが発生し、次のメッセージが表示されます。「「GroupBy」メソッドの呼び出しのキーセレクタータイプは、基になるストアプロバイダーで比較できません」。EqualsとGetHashcodeをオーバーライドしても効果はありません。本当の問題は、エンティティフレームワークがSQLの表現方法を知らないことだと思います...?

4

1 に答える 1

9

残念ながら、いいえ、それはできません。ComplexTypeは有望に見え、優れた「列のグループ」のようですが、EFチームはそれを完全に他の用途向けに設計しただけであり、「列のグループ」としての使用に関連する多くの機能が欠落しています。

私の知識が及ぶ限り、「明らかに私は次のことを行うことができました:」が可能な唯一の方法です。サブ列ごとにグループ化を定義するか、データベースが比較方法を認識しているタイプへの射影によってグループ化を定義する必要があります。

主要な問題は、EFチームがComplexTypeを、エンティティを多くのフィールドで整理できる列のグループとは見なさないことですが、それらの場合、それは別のタイプのエンティティであり、一時的なものです。。ComplexTypeは、たとえば、通常の既存のテーブル/クラスにマップできない5列の結果セットを返すストアドプロシージャ(またはテーブルを返す関数)から返されるオブジェクトを表すことを目的としています。たとえば、50個の列/フィールドを含むテーブル/クラスCustomerがあり、それらのほとんどがnullではなく、機密データを表しているとします。顧客のIDを取得し、その{名前、住所、連絡先の電話番号、靴のサイズ}を返すストアドプロシージャを作成します。明らかに、そのようなトリミングされた結果セットがあると、 nullにできないフィールドが大量にあるCustomerクラスにデコード/マップすることはできません。では、EFで結果セットをどのように表現しますか?

最初のオプションは、非常に不完全なものですが、EFを無視してデータベースと直接通信し、行/列を手動で読み取り/変換/解凍することです。まあ、私たちはSqlClientを知っています、私たちはそれを行うことができます。

2番目のオプション(醜いもの)は、EFでスタブテーブル/ビュー(たとえばCustomerView)を定義し、その{名前、住所、連絡先電話番号、靴のサイズ}列だけを含み、ストアドプロシージャの結果セットをそれにマップすることです。それはうまく機能しますが、誤ってEFモデルから「データベースを生成」した場合、その余分な未使用のテーブルも取得されます。

救助の3番目のオプションはComplexTypeです。シャドウテーブルまたはビューを作成しようとする代わりに、EFに、独自のテーブルにマップされることはなく、PrimaryKeyが定義されることもない「追加のデータ型がいくつかある」ことを伝えるだけで、EFは便利なことに、他のデータオブジェクトと同じようにクラスにマップします。さて、そのような型を持っているので、それをプロシージャから返し、うまく型付けされたオブジェクトとして取得したり、プロシージャのパラメータとして渡したりすることができます。しかし、それ自体を永続化することはできません。独自のテーブルマッピングも、独自の主キーもありません。アイデンティティはありません。

それは素晴らしいことですが、なぜそのようなデータ型をプロシージャから返す必要があったのでしょうか。おそらく、プロシージャがいくつかのデータを処理または生成したので、それらをいくつかのテーブルに格納したいとします。通常のエンティティ全体を定義していないため、これは単なる「データパック」であるため、他のデータと一緒にその結果値をそのテーブルに書き込む可能性があります。これが、EFでComplexTypeをテーブルの一部の列にマップできる理由です。プロシージャから一時的な結果オブジェクトを取得し、それをどこかに書き込みたい場合は、列ごとに手動で行う必要はありません。 、顧客の「名前、住所、連絡先の電話番号、靴のサイズ」の列にマップするだけです。

最も重要な点は、データベースサーバーがそのようなComplexTypeが存在することを決して知らないということです。私が言ったように、それはアイデンティティを持っていませんでした。次のようなLINQクエリを実行しようとすると

aset.Where(item => item.complexProperty == complexValue)

その場合、complexValueはどのテーブルにもマップされないCLRオブジェクトであり、IDも、したがってPKもありません。したがって、EFには、「左側のオブジェクト」が「右側のオブジェクト」と同じであるかどうかを確認する方法がまったくありません。オブジェクトに何らかのIDが定義されていれば、サーバー側でPKをチェックしたり、クライアント側でオブジェクト参照の比較を行ったりできますが、ここでは単に失敗します。

私は、これがEFに欠けているもう1つの非常に便利な機能であることに完全に同意します。EFチームがComplexTypesを列ごとに比較するのは非常に簡単だと思いますが、そうではありませんでした。彼らは、ComplexTypesを使用してテーブルを整理できるとは考えていませんでした。それらは、ストアドプロシージャおよび関数との間で受け渡される一時的なデータパックであることを意味しています。残念です。

于 2012-04-24T21:52:45.573 に答える