質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQL Server

SQL Serverはマイクロソフトのリレーショナルデータベース管理システムです。データマイニングや多次元解析など、ビジネスインテリジェンスのための機能が備わっています。

LINQ to SQL

LINQ to SQLは.NET Framework 3.5のコンポーネントで、リレーショナル データをオブジェクトとして管理するためのランタイム インフラストラクチャを提供します。

解決済

EntityFrameworkCoreでの自作拡張メソッドの使用方法

yanelmo
yanelmo

総合スコア4

Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQL Server

SQL Serverはマイクロソフトのリレーショナルデータベース管理システムです。データマイニングや多次元解析など、ビジネスインテリジェンスのための機能が備わっています。

LINQ to SQL

LINQ to SQLは.NET Framework 3.5のコンポーネントで、リレーショナル データをオブジェクトとして管理するためのランタイム インフラストラクチャを提供します。

1回答

0評価

0クリップ

884閲覧

投稿2021/09/14 10:58

前提・実現したいこと

現在、EntityFrameworkCoreを使用してDBへのアクセスを行う.NET Core MVCアプリを作成しています。
言語はC#です。
EF・MVCともに初めて触るもので、学習途中です。

データベースから、GroupByメソッドを使用して集計した値からその中央値を計算した結果を取得したいです。
イメージとしては、以下のようなテーブルに対して、

idvalue
11
12
13
22
25
23

以下のようなLINQクエリを使ってデータを取得したいと考えています。
({id = 1,median = 2},{id=2,median=3}のリストを取得するイメージです)

C#

var result = dbContext.table .GroupBy(item => item.id) .Select(item => new { id = item.Key, median = item.Median(elem => elem.value) }) .ToList();

また、中央値を求めるMedian<double?>IEnumerable<double?>の拡張メソッドとして以下のように定義しています。

C#

public static double Median(this IEnumerable<double> src) { //対象のシーケンスを昇順にソート var sortedSeq = src.OrderBy(item => item).ToArray(); //シーケンスの数に応じて処理を分岐 if (sortedSeq.Count() % 2 == 0) { //偶数個 var retSeq = sortedSeq.Count() / 2; return (sortedSeq[retSeq - 1] + sortedSeq[retSeq]) / 2; } else { //奇数個 var retSeq = sortedSeq.Count() / 2; return sortedSeq[retSeq]; } } public static double? Median<T>(this IEnumerable<T> src, Func<T, int?> selector) { var srcArray = new double[src.Count()]; //IEnumrable<double?>に変換し、中央値を返す foreach (var item in src.Select((item, i) => new { Value = item, Seq = i })) { srcArray[item.Seq] = selector(item.Value).Value; } return srcArray.Median(); }

発生している問題・エラーメッセージ

以下のコードのようにアクセスしたときに、中央値を取得することができません。
idで集計し、その値から中央値を取得できることを期待しています。

C#

var result = dbContext.table .GroupBy(item => new { item.id , item.value }) .Select(item => new { id = item.id, median = item.Median(item => item.value ) }).ToList();

エラーコードは以下の通りです。
(実際のエラー内容をペーストしているので、上記イメージコードと内容が異なります)

System.InvalidOperationException: 'Processing of the LINQ expression 'GroupByShaperExpression: KeySelector: p.rank_id, ElementSelector:EntityShaperExpression: EntityType: PredictionDatum ValueBufferExpression: ProjectionBindingExpression: EmptyProjectionMember IsNullable: False ' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in Entity Framework. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.'

試したこと

当初はエラー内容にある記事の内容から、GroupByをサーバー側で評価できないために発生しているのかと考えました。

しかし、標準で存在する平均値を求めるAverageメソッドを使用すると、
問題なく取得することができました。
このことから、

  • 拡張メソッドを定義してもLINQ To SQLでは使用できないのでは?
  • SQLとして実行するには別の何かを定義する必要があるのでは?

などと考えましたが、有用な情報が得られませんでした。

(以下のコードはidごとに平均値を取得することができました)

C#

var result = dbContext .table .GroupBy(item => item.id) .Select(item => new { id = item.Key, average = item.Average(elem => elem.value) }) .ToList();

また、最初にAsEnumerableメソッドを使用し、クライアント側での処理を行うことで問題なく取得できることがわかりました。
しかし、これではすべての値を一度クライアント側に取ってきた上で計算を行っていると思います。
SQLServer側で集計できる方法が知りたいです。

var result = dbContext .table .AsEnumerable() .GroupBy(item => item.id) .Select(item => new { id = item.Key, median = item.Median(elem => elem.value) });

補足情報(FW/ツールのバージョンなど)

  • .NET Core 5 MVC
  • C#
  • EntityFrameworkCore 5.0.9
  • Microsoft SQL Server Developer (64-bit) バージョン:15.0.4153.1
  • Visual Studio 2019

での開発をしています。

質問内容のまとめ

改めて、質問内容をまとめて記載します。

  1. 自作したIEnumerable<T>の拡張メソッドでは、データベースへのアクセスに使用することはできないのでしょうか。
  2. 上記ができない場合、それを可能にする方法はあるでしょうか。
  3. 上記コードで、Averageメソッドでは取得できるのに自作のMedianでは取得できない理由はどういったものでしょうか。

よろしくお願いします。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

gentaro

2021/09/14 11:56

その疑問に対する情報はエラー・メッセージのリンクを読めばわかるんじゃない? > This may indicate either a bug or a limitation in Entity Framework. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.' 読んで質問してるのかどうか知らんけど。
yanelmo

2021/09/14 13:36

gentaro さん すみません、このリンクは読んだ上での質問でした。 が、ご指摘受けて改めて読んだところ理解していなかった箇所がありました。 「サポートされていないクライアント評価」で書いてあるように、上記の自分のコードではSelectメソッド内でMedianメソッドが使用されているため、SQLに変換できず実行時エラーになっていると考えています。 そのため、 1:SQLに変換できないため不可能 3:標準の集計関数はSQLへの変換が可能だから という結論になりました。 ここまでは合っているでしょうか。 2の、自作のメソッドを使えるようにする方法があるか、ないかというのはわかりませんでした。 読む限りでは、AsEnumerableメソッドを使用してクライアント側で評価しなさいということでしょうか。
SurferOnWww

2021/09/14 13:48

Linq to Entities は、Linq to Objects とは違って、そのクエリ式が SQL Server などの DB で使われる SQL に変換できる必要があるという話ではないかと言っているのですが、話を聞いてもらってます?
yanelmo

2021/09/14 13:50 編集

SurferOnWww さん ありがとうございます。 EntitiesとObjectの違いで間違いないです。 「SQLに変換できる必要がある」という点が質問時点で分かっていませんでした。 リンクのCodeZineの記事も読みました。非常に参考になりました。 できるか、できないかを知りたいのですが、こういった自作メソッドをEFで評価できるように実装するというのは可能なのでしょうか。 (追記) すみません、リンク先の確認に時間かかっていました。 そういったお話で間違いないです。
SurferOnWww

2021/09/14 13:59

紹介した記事にも書きましたが、LINQ to Entities クエリで使用する SQL Server 関数を公開する SqlFunctions クラスのメソッドに該当するものがあれば、それを使うという手もあるかもしれません・・・が、自作メソッドとか、それ以前に EF Core ではダメかも。 ダメなら AsEnumerable なり Tolist なりで Object にしてそれに Linq を適用することを考える方が良いかもしれません。
yanelmo

2021/09/14 14:23

SqlFunctionsクラスにはありませんでした… もう少し調べていると、DB側に定義した関数をLinq to Entities中から呼び出すことができるというものがありました。 他に呼び出す方法はなさそうですので、一旦これで解決とさせていただきます。 回答ありがとうございました。 https://docs.microsoft.com/ja-jp/dotnet/framework/data/adonet/ef/language-reference/how-to-call-custom-database-functions

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQL Server

SQL Serverはマイクロソフトのリレーショナルデータベース管理システムです。データマイニングや多次元解析など、ビジネスインテリジェンスのための機能が備わっています。

LINQ to SQL

LINQ to SQLは.NET Framework 3.5のコンポーネントで、リレーショナル データをオブジェクトとして管理するためのランタイム インフラストラクチャを提供します。