web-dev-qa-db-ja.com

LINQ to Entitiesメッセージでは、パラメーターなしのコンストラクターとイニシャライザーのみがサポートされています

EFモデルからデータを返すメソッドがあります。

上記のメッセージが表示されますが、問題を回避する方法がわかりません。

    public static IEnumerable<FundedCount> GetFundedCount()
    {
        var today = DateTime.Now;
        var daysInMonth = DateTime.DaysInMonth(today.Year, today.Month);

        var day1 = DateTime.Now.AddDays(-1);
        var day31 = DateTime.Now.AddDays(-31);

        using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
        {
            var r = new Repository<MatchHistory>(uow.Context);

            return r.Find()
                .Where(x =>
                    x.AppliedOn >= day1 && x.AppliedOn <= day31 &&
                    x.ResultTypeId == (int)MatchResultType.Accepted)
                .GroupBy(x => new { x.BuyerId, x.AppliedOn })
                .Select(x => new FundedCount(
                    x.Key.BuyerId,
                    x.Count() / 30 * daysInMonth))
                .ToList();
        }
    }

FundedCountはEFエンティティではありませんが、MatchHistoryはそうであるため、不満の理由を理解できません。

すべてのアドバイスに感謝します。

29
dotnetnoob

文句を言うのは、Select()をSQL式に変換する方法がわからないためです。 ではないエンティティであるPOCOにデータ変換を行う必要がある場合、まずEFから関連データを取得し、それをPOCOに変換する必要があります。

あなたの場合、ToList()を前に呼び出すのと同じくらい簡単です:

_return r.Find()
        .Where(x => x.AppliedOn >= day1 && x.AppliedOn <= day31 &&
                    x.ResultTypeId == (int)MatchResultType.Accepted)
        .GroupBy(x => new { x.BuyerId, x.AppliedOn })
        .ToList() // this causes the query to execute
        .Select(x => new FundedCount(x.Key.BuyerId, x.Count() / 30 * daysInMonth));
_

ただし、これには注意し、テーブル全体をメモリにロードしようとしないように、ToList()によって返されるデータセットのサイズをできるだけ制限するようにしてください。

51
Yuck

メッセージは明確です:エンティティへのlinqは、パラメーターなしのctorのないオブジェクトをサポートしません。

そう

Solution1

前に列挙する(または中間の匿名型を使用して、その上で列挙する)

.ToList()
.Select(x => new FundedCount(
                    x.Key.BuyerId,
                    x.Count() / 30 * daysInMonth))
                .ToList();

Solution2

fundedCountクラスにパラメーターなしのctorを追加します(可能な場合)

public FundedCount() {}

そして使用する

.Select(x => new FundedCount{
                        <Property1> = x.Key.BuyerId,
                        <Property2> = x.Count() / 30 * daysInMonth
                         })
                    .ToList();
12

FundedCountへの参照をSQLステートメントに変換できないため、文句を言っています。

すべてのLINQプロバイダーは、LINQステートメントと式を、ターゲットが理解できる操作に変換します。 LINQ to SQLおよびLINQ to EFはLINQをSQLに変換し、PLINQはそれをタスクと並列操作に変換し、LINQ to SharepointはそれをCAMLに変換します。

変換できない場合はどうなるかはプロバイダーによって異なります。一部のプロバイダーは中間結果を返し、クエリの残りをLINQ to Objectsクエリに変換します。他のユーザーは、単にエラーメッセージで失敗します。

データベースと通信するとき、メッセージで失敗することは実際にはより良いオプションです。そうしないと、実際に1つまたは2つしか必要ない場合、サーバーはすべての列をクライアントに返す必要があります。

あなたの場合、必要なデータを含む匿名型を返すように選択を変更し、ToList()を呼び出して、FundedCountオブジェクトを作成します。次に例を示します。

.Select( x=> new {Id=x.Key.BuyerId,Count=x.Count()/30 * daysInMonth)
.ToList()
.Select(y => new FundedCount(y.Id,y.Count))
.ToList();

最初のToList()は、SQLステートメントの生成を強制し、必要なデータのみを返すクエリを実行します。クエリの残りは実際にはLinq to Objectsであり、データを取得して最終的なオブジェクトを作成します

3

GroupByでも同じ例外がありました。 「パラメーターなしのコンストラクターとイニシャライザーのみがLINQ to Entitiesでサポートされている」という例外は、100%正確な説明ではないことがわかりました。

GroupByのキーとして構造体を使用する「Linq to EntityFrameworkクエリ」にGroupBy()がありました。それはうまくいきませんでした。その構造体を通常のクラスに変更すると、すべて正常に機能しました。

コードサンプル

var affectedRegistrationsGrouped = await db.Registrations
  .Include(r => r.Person)
  .Where(r =>
      //whatever
  )
  .GroupBy(r => new GroupByKey
  {
      EventId = r.EventId, 
      SportId = r.SportId.Value
  })
 .ToListAsync();

...
...
// this does not work
private struct GroupByKey() {...}

// this works fine
private class GroupByKey() {...}
3
David Votrubec