LINQ to Entitiesでサポートされているのはパラメーターなしのコンストラクターと初期化子だけです


132

このlinq式にこのエラーがあります:

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              )).ToList();

この問題をどのように解決するか考えていますか?私は表現の任意の組み合わせで試してみます...:/


1
支払いクラスを表示できますか?または、少なくともここで呼び出されるctor、具体的には、その8パラメータのctor呼び出しを0パラメータのctor呼び出しに安全に交換して、オブジェクトに8つのプロパティを設定できるかどうか。
James Manning

23
私が「更新」していたオブジェクトにクラスの代わりに構造体を使用したときにも同じエラーが発生しました。
HuckIt 2013

3
TL; DRのことは、EF-LINQがselectステートメントをEFプロバイダーに送信しようとしていることです。それをSQLに変換します。EF-LINQから抜け出すには、オブジェクトを作成する前にToList()を呼び出します。

回答:


127

「Payments」に関する詳細情報がなければ、これはあまり役に立ちませんが、Paymentsオブジェクトを作成し、列の値に基づいてそのプロパティの一部を設定することを想定しています。

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty = nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia = nalTmp.DataRozliczenia,
                                  TerminPlatnosci = nalTmp.TerminPlatnosci,
                              }).ToList();

10
これはうまく機能します。クラスに空のコンストラクタを追加することを忘れないでください。
live-love

58
この答えを追加するために、構造体ではこれを行うことはできません。クラスのみです。少し考えてみました。
naspinski

4
はい、私はTonyの答えが当面の問題を実際に解決するので、これよりも良いと思います。これは、Paymentsクラスの性質を変更し、場合によっては不変になるのを防ぐことで問題を回避します。
Stephen Holt 14

これは醜いafに見えます。EF6のより良い方法は?
ツールキット、

115

プロパティではなく初期化にコンストラクタを引き続き使用する場合(この動作は初期化の目的で必要になる場合があります)、ToList()またはを呼び出してクエリを列挙し、ToArray()次に使用しますSelect(…)。したがって、コレクションへのLINQを使用し、パラメーターを使用してコンストラクターを呼び出せないという制限があります。Select(…)なります。

したがって、コードは次のようになります。

var naleznosci = db.Naleznosci
                          .Where(nalTmp => nalTmp.idDziecko == idDziec)
                          .ToList() // Here comes transfer to LINQ to Collections.
                          .Select(nalImp => new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              ))
                          .ToList();

21
なぜこれが機能するのかを明確にするために、最初に述べたコードの問題は、Entity Frameworkが残りのLINQクエリと一緒にSQLにコンストラクター呼び出しを渡そうとすることであり、もちろんSQLが構築を行う方法がありません複雑なオブジェクト!ToList()呼び出しを挿入することにより、まだ実行されていないSQLクエリからメモリ内のオブジェクトの具体的なリストに列挙子を移動し、好きな方法で操作できます。
スティーブンホルト14

19
ToX()これには使用しないでくださいAsEnumerable()
ローリング、2014

1
.ToList()// LINQ to Collectionsに転送されます。私のために問題を解決する行です。
Ram

15
これにより、通常は必要な列のみが選択されるdbレベルですべての列が選択されることに注意してください
Hugh Jeffner

4
それだけでなく、おそらく複数の列挙があるでしょう。この解決策は好きではありません。
Bluebaron

47

自分でこのエラーに遭遇したので、Payment型がである場合、型がパラメーターなしのコンストラクターをサポートしていないstructため、同じエラーが発生することも考えましたstruct

その場合、Paymentクラスに変換し、オブジェクト初期化子構文を使用すると、問題が解決します。


これは私から問題を解決します。実際、構造体セレクターを使用したこのクエリはLINQ-2-SQLでサポートされており、EntityFrameworkにアップグレードするときに問題になります。
Tomas Kubes 2015

構造体は嫌いです。彼らは私がしたいことをしてしまうことはありません
Simon_Weaver

DateTimeQuery内に(構造体である)を作成したため、同じエラーが発生しました。ローカル変数に抽出すると、修正されました。構造体のヒントをありがとう。
LuckyLikey 2017

20

あなたが私のようなもので、作成しているクエリごとにプロパティを入力する必要がない場合は、この問題を解決する別の方法があります。

var query = from orderDetail in context.OrderDetails
            join order in context.Orders on order.OrderId equals orderDetail.orderId
            select new { order, orderDetail };

この時点で、匿名オブジェクトを含むIQueryableがあります。カスタムオブジェクトにコンストラクターを設定する場合は、次のようにするだけです。

return query.ToList().Select(r => new OrderDetails(r.order, r.orderDetail));

これで、カスタムオブジェクト(パラメーターとして2つのオブジェクトを受け取る)で、必要に応じてプロパティを設定できます。


これは私にとってはうまくいき、最もクリーンな解決策になりました。コンストラクタを削除して初期化構文を使用することを提案した人は、コンストラクタ内にロジックを持っていてはなりません。オブジェクトのプロパティを設定するためにコンストラクターに頼るのはそのときだけです。共有していただきありがとうございます。
Bonez024

9

最初に私は解決策を避けます

from ....
select new Payments
{
  Imie = nalTmp.Dziecko.Imie,
  ....
}

これは空のコンストラクターを必要とし、カプセル化を無視するので、新しいPayments()はデータのない有効な支払いですが、代わりにオブジェクトにはドメインに応じて少なくとも値とおそらく他の必須フィールドが必要です。

必須フィールドのコンストラクターを用意することをお勧めしますが、必要なデータのみを持ち込みます。

from ....
select new
{
  Imie = nalTmp.Dziecko.Imie,
  Nazwisko = nalTmp.Dziecko.Nazwisko
  ....
}
.ToList() // Here comes transfer to LINQ to Collections.
.Select(nalImp => new Payments
 (
  nalTmp.Imie,//assume this is a required field
  ...........
  )
  {
     Nazwisko = nalTmp.Nazwisko //optional field
  })
.ToList();

これはより小さな悪です。
Chalky

私もこのようなものが好きです。私はタプルを使用することを試みましたが、タプルにはパラメーターの少ないコンストラクターがありません。匿名オブジェクトを入力してから、タプルを選択します。
Tchaps 2017年

カプセル化とドメインを採用するために1つ
inrandomwetrust

2

同じことを試すことができますが、拡張の方法を使用します。データベース使用のプロバイダーは何ですか?

var naleznosci = db.Naleznosci
                          .Where<TSource>(nalTmp => nalTmp.idDziecko == idDziec)
                          .Select<TSource, TResult>(
                             delegate(TSource nalTmp) { return new Payments
                             (
                                 nalTmp.Dziecko.Imie,
                                 nalTmp.Dziecko.Nazwisko,
                                 nalTmp.Miesiace.Nazwa,
                                 nalTmp.Kwota,
                                 nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                 nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                 nalTmp.DataRozliczenia,
                                 nalTmp.TerminPlatnosci
                             ); })
                          .ToList();

2

ただ、前の文..実際には、クエリとして保存され、それがまだ満たされていません。呼び出した後、オブジェクトを操作していて、クエリでデフォルト以外のコンストラクタを使用できます。ToList()DbSetSelectDbSetToList()

使用時間に関して最も効率的な方法ではありませんが、小規模なセットではオプションです。


1

ええ、こうやってみてください。

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments()
                              {
                                  Dziecko.Imie,
                                  Dziecko.Nazwisko,
                                  Miesiace.Nazwa,
                                  Kwota,
                                  RodzajeOplat.NazwaRodzajuOplaty,
                                  RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia,
                                  TerminPlatnosci
                              }).ToList();

これにより、パラメーターのないコンストラクターを使用してPaymentオブジェクトが新しくなり、中括弧内にリストされているプロパティが初期化されます { }


3
()PayIntsのFYIは必要ないため、 `select new Payments {// init values}
PostMan

エラーが発生しました: 'System.Collections.IEnumerable'を実装していないため、コレクション初期化子でタイプ 'Payments'を初期化できません
netmajor

右-(Paymentsクラスのインスタンスではなく)anonタイプを作成する場合、設定するプロパティは暗黙的に読み取られるプロパティの名前になるため、Muadのコードは問題ありません。ただし、これは「実際の」クラスなので、さまざまな値に設定するプロパティを指定する必要があります。
James Manning、

1

前述のメソッドに加えて、次のようにEnumerableコレクションとして解析することもできます。

(from x in table
....
).AsEnumerable()
.Select(x => ...)

これには、次のように、匿名オブジェクトを作成する際の作業が簡単になるという利点もあります。

 (from x in tableName
select x.obj)
.Where(x => x.id != null)
.AsEnumerable()
.Select(x => new {
   objectOne = new ObjectName(x.property1, x.property2),
   parentObj = x
})
.ToList();

ただし、コレクションをEnumerableとして解析するとコレクションがメモリに取り込まれるため、リソースを大量に消費する可能性があることに注意してください。ここでは注意が必要です。


1

また、複数のオブジェクトを持つコンストラクターを使用して初期化する場合、Linqから値が返されないとエラーが発生する可能性があります。

だからあなたはこのようなことをしたいかもしれません:

(from x in table_1
   join y in table_2
   on x.id equals y.id
   select new {
   val1 = x,
   val2 = y
})
.DefaultIfEmpty()
.ToList()
.Select(a => new Val_Constructor(a.val1 != null ? a.val1 : new Val_1_Constructor(),
                            a.val2 != null ? a.val2 : new Val_2_Constructor()))
.ToList();

1

発見後のパーティーに遅刻のため申し訳ありませんが、私はこれを、私はそれは私が見つけることができるきれいな、最速でも省メモリの実装だと、これは共有されなければならないと思いました。

あなたの例に適応して、あなたは書くでしょう:

public static IQueryable<Payments> ToPayments(this IQueryable<Naleznosci> source)
{
  Expression<Func<Naleznosci, Payments>> createPayments = naleznosci => new Payments
  {
    Imie = source.Dziecko.Imie,
    Nazwisko = source.Dziecko.Nazwisko,
    Nazwa= source.Miesiace.Nazwa,
    Kwota = source.Kwota,
    NazwaRodzajuOplaty = source.RodzajeOplat.NazwaRodzajuOplaty,
    NazwaTypuOplaty = source.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
    DataRozliczenia = source.DataRozliczenia,
    TerminPlatnosci = source.TerminPlatnosci,
  };

  return source.Select(createPayments);
}

ここでの大きな利点(Damien Guardがリンクのコメントで指摘したとおり)は次のとおりです。

  • 発生するたびに初期化パターンを使用しないようにします。
  • による使用とvar foo = createPayments(bar);myIQueryable.ToPayments()による使用が可能です。

1

今日も同じ問題があり、私の解決策はヨーダがリストしたものに似ていましたが、それは流暢な構文でのみ機能します。

私のソリューションをコードに適応させる:次の静的メソッドをオブジェクトクラスに追加しました

    /// <summary>
    /// use this instead of a parameritized constructor when you need support
    /// for LINQ to entities (fluent syntax only)
    /// </summary>
    /// <returns></returns>
    public static Func<Naleznosci, Payments> Initializer()
    {
        return n => new Payments
        {
             Imie = n.Dziecko.Imie,
             Nazwisko = n.Dziecko.Nazwisko,
             Nazwa = n.Miesiace.Nazwa,
             Kwota = n.Kwota,
             NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
             NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
             DataRozliczenia = n.DataRozliczenia,
             TerminPlatnosc = n.TerminPlatnosci
        };
    }

次に、基本クエリを次のように更新しました。

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select new Payments.Initializer());

これは、論理的にはJames Manningのソリューションと同等であり、メンバー初期化の膨張をクラス/データ転送オブジェクトにプッシュするという利点があります。

注:元々は「Initializer」よりもわかりやすい名前を使用していましたが、使用方法を確認したところ、「少なくともInitilizer」で十分であることがわかりました(少なくとも私の目的では)。

最終メモ:
このソリューションを思いついた後、最初は同じコードを共有し、これをクエリ構文でも機能するように調整するのは簡単だと思っていました。私はもはやそうであるとは思わない。このタイプの速記構文を使用できるようにしたい場合は、オブジェクトクラス自体に存在できる上記の各(query、fluent)fluentのメソッドが必要になると思います。

クエリ構文の場合、拡張メソッド(または使用されている基本クラス外のメソッド)が必要になります。(クエリ構文ではTではなくIQueryableを操作する必要があるため)

これが最終的にこれをクエリ構文で機能させるために使用したもののサンプルです。(ヨーダはすでにこれを釘付けにしたが、私は最初にそれを得なかったので、使用法はより明確になると私は思う)

/// <summary>
/// use this instead of a parameritized constructor when you need support
/// for LINQ to entities (query syntax only)
/// </summary>
/// <returns></returns>
public static IQueryable<Payments> Initializer(this IQueryable<Naleznosci> source)
{
    return source.Select(
        n => new Payments
        {
            Imie = n.Dziecko.Imie,
            Nazwisko = n.Dziecko.Nazwisko,
            Nazwa = n.Miesiace.Nazwa,
            Kwota = n.Kwota,
            NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
            NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
            DataRozliczenia = n.DataRozliczenia,
            TerminPlatnosc = n.TerminPlatnosci
    };
}

と使い方

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select nalTmp).Initializer().ToList();

最初の回答がうまく拡張されていないことに気付いたとき、完全を期すためにクエリ構文に関するセクションを追加しました。@yodaの回答は、おそらくクエリ構文の点で優れています。
wode

0

答えるのは遅いですが、それでも苦痛のある人を助けることができます。エンティティへのLINQは、パラメーターのないオブジェクト構築をサポートしていないためです。ただし、IEnumerableのプロジェクションメソッド。

したがって、選択する前に、次のコードを使用してIQueryableIEnumerableに変換するだけです。

var result = myContext.SomeModelClass.AsEnumerable().Select(m => m.ToString());

正常に動作します。ただし、当然、ネイティブクエリの利点は失われます。


0
IQueryable<SqlResult> naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty =                          nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                              NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                              DataRozliczenia = nalTmp.DataRozliczenia,
                              TerminPlatnosci = nalTmp.TerminPlatnosci,
                          });
Repeater1.DataSource  = naleznosci.ToList(); 
Repeater1.DataBind();


public class SqlResult
{
        public string Imie { get; set; }
        public string Nazwisko { get; set; }
        ...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.