Hanya konstruktor dan penginisialisasi tanpa parameter yang didukung di LINQ untuk Entitas

132

Saya memiliki kesalahan ini dalam ekspresi LINQ ini:

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();

Adakah yang tahu bagaimana menyelesaikan masalah ini? Saya mencoba dengan kombinasi ekspresi apa pun:: /

netmajor
sumber
1
dapatkah Anda menunjukkan kelas Pembayaran? atau setidaknya ctor dipanggil ke sini, dan secara khusus apakah panggilan ctor 8-param dapat dengan aman ditukar untuk panggilan ctor 0-param dan mengatur 8 properti pada objek?
James Manning
23
Saya mendapatkan kesalahan yang sama ketika menggunakan Struct, bukan Class untuk objek yang saya "baru".
HuckIt
3
TL; DR adalah bahwa EF-LINQ sedang mencoba untuk mengirim pernyataan pilih ke penyedia EF, yaitu. mengubahnya menjadi SQL. Untuk keluar dari EF-LINQ, panggil ToList () sebelum membuat objek apa pun.

Jawaban:

127

tanpa info lebih lanjut tentang 'Pembayaran' ini tidak banyak membantu, tetapi dengan asumsi Anda ingin membuat objek Pembayaran dan menetapkan beberapa propertinya berdasarkan pada nilai kolom:

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();
James Manning
sumber
10
Ini berfungsi dengan baik, jangan lupa untuk menambahkan konstruktor kosong untuk kelas.
live-love
58
Hanya untuk menambahkan jawaban ini, Anda tidak dapat melakukan ini dengan Structs, hanya Kelas - butuh saya sedikit untuk mencari tahu!
naspinski
4
Ya, saya pikir jawaban Tony lebih baik daripada yang ini karena itu benar-benar menyelesaikan masalah langsung, sedangkan yang satu ini menghindari masalah dengan mengubah sifat kelas Pembayaran dan mungkin mencegahnya agar tidak berubah.
Stephen Holt
ini terlihat jelek af. Adakah cara yang lebih baik dengan EF6?
Toolkit
115

Jika Anda masih ingin menggunakan konstruktor untuk inisialisasi dan bukan properti (kadang-kadang perilaku ini diinginkan untuk tujuan inisialisasi), sebutkan permintaan dengan menelepon ToList()atau ToArray(), dan kemudian gunakan Select(…). Dengan demikian ia akan menggunakan LINQ untuk Koleksi dan bahwa keterbatasan tidak dapat memanggil konstruktor dengan parameter di Select(…)akan menghilang.

Jadi kode Anda akan terlihat seperti ini:

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();
Tony
sumber
21
Hanya untuk memperjelas mengapa ini bekerja, masalah dengan kode yang dinyatakan semula adalah bahwa Entity Framework mencoba untuk meneruskan panggilan konstruktor ke SQL bersama dengan sisa permintaan LINQ, dan tentu saja tidak ada cara bagi SQL untuk terus membangun benda kompleks! Dengan memasukkan panggilan ToList (), Anda memindahkan enumerable dari kueri SQL yang belum dieksekusi ke daftar objek konkret dalam memori, yang kemudian dapat Anda manipulasi dengan cara apa pun yang Anda suka.
Stephen Holt
19
Jangan gunakan ToX()untuk ini, gunakan AsEnumerable().
Rawling
1
.ToList () // Ini dia transfer ke LINQ to Collections. adalah garis yang menyelesaikan masalah bagi saya.
Ram
15
Ketahuilah bahwa ini akan memilih semua kolom pada level db di mana seperti biasanya hanya akan memilih kolom yang diperlukan
Hugh Jeffner
4
Bukan hanya itu tetapi Anda mungkin akan memiliki beberapa enumerasi. Saya tidak suka solusi ini.
Bluebaron
47

Baru saja mengalami kesalahan ini, saya pikir saya akan menambahkan bahwa jika Paymentjenisnya adalah struct, Anda juga akan mengalami kesalahan yang sama karena structjenis tidak mendukung konstruktor tanpa parameter.

Dalam peristiwa itu, mengonversi Paymentke kelas dan menggunakan sintaks penginisialisasi objek akan menyelesaikan masalah.

Gene C
sumber
Ini menyelesaikan masalah dari saya. Sebenarnya permintaan ini dengan pemilih pemilih didukung dalam LINQ-2-SQL dan ini merupakan masalah ketika Anda meningkatkan ke EntityFramework.
Tomas Kubes
Saya benci struct. Mereka tidak pernah akhirnya melakukan apa yang saya inginkan
Simon_Weaver
Dibuat DateTime(yang merupakan struct) di dalam Permintaan saya, yang menghasilkan Kesalahan yang sama. mengekstraknya ke Variabel lokal memperbaikinya untuk saya. Terima kasih atas petunjuk struct.
LuckyLikey
20

Jika Anda seperti saya dan tidak ingin harus mengisi properti Anda untuk setiap permintaan yang Anda buat, ada cara lain untuk menyelesaikan masalah ini.

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

Pada titik ini Anda memiliki IQueryable yang berisi objek anonim. Jika Anda ingin mengisi objek kustom Anda dengan konstruktor, Anda cukup melakukan sesuatu seperti ini:

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

Sekarang objek kustom Anda (yang mengambil dua objek sebagai parameter) dapat mengisi properti Anda sesuai kebutuhan.

Justin Helgerson
sumber
Ini bekerja untuk saya dan menjadi solusi terbersih. Mereka yang telah menyarankan untuk menghilangkan konstruktor dan menggunakan sintaksis initializer harus tidak memiliki logika di dalam konstruktor. Itulah satu-satunya waktu saya bersandar pada konstruktor untuk mengisi properti untuk suatu objek. Terima kasih sudah berbagi.
Bonez024
9

Pertama saya akan menghindari solusinya

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

Ini membutuhkan konstruktor kosong dan mengabaikan enkapsulasi sehingga Anda mengatakan Pembayaran baru () adalah pembayaran yang valid tanpa data apa pun, tetapi objek tersebut harus memiliki setidaknya nilai dan mungkin bidang yang diperlukan lainnya tergantung pada domain Anda.

Lebih baik memiliki konstruktor untuk bidang yang diperlukan tetapi hanya membawa data yang diperlukan:

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();
eugen
sumber
Ini adalah kejahatan yang lebih rendah.
Chalky
Saya juga lebih suka yang seperti ini. Saya mencoba mengikat untuk menggunakan Tuple tetapi Tuple tidak memiliki konstruktor parameter kurang. Saya mengisi objek anonim dan kemudian pilih Tuple.
Tchaps
satu untuk merangkul enkapsulasi dan domain
inrandomwetrust
2

Anda dapat mencoba melakukan hal yang sama, tetapi menggunakan metode ekstensi. Apa yang digunakan penyedia database?

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();
Tuan Tarakanoff
sumber
2

Hanya ToList()yang DbSetsebelum Selectpernyataan .. sebenarnya DbSetdisimpan sebagai query, itu belum terpenuhi. Setelah memanggil ToList()Anda bermain dengan objek, dan kemudian Anda bisa menggunakan konstruktor non-default dalam kueri.

Bukan cara penggunaan waktu yang paling efisien, tetapi ini merupakan pilihan pada set kecil.

Eiran
sumber
1

ya, coba seperti ini ....

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();

ini akan menambah objek Pembayaran Anda menggunakan konstruktor tanpa parameter, dan kemudian menginisialisasi properti yang tercantum di dalam kurung kurawal { }

Muad'Dib
sumber
3
FYI ()dalam Payemnts tidak diperlukan, sehingga bisa `pilih Pembayaran baru {// nilai init}
PostMan
sekarang saya memiliki kesalahan: Tidak dapat menginisialisasi jenis 'Pembayaran' dengan penginisialisasi koleksi karena tidak menerapkan 'System.Collections.IEnumerable'
netmajor
kanan - jika Anda membuat tipe anon (bukan turunan dari kelas Pembayaran), kode Muad akan baik-baik saja karena properti yang akan disetel secara implisit adalah nama properti yang sedang dibaca. Karena ini adalah kelas 'nyata', Anda harus menentukan properti mana yang akan disetel ke berbagai nilai.
James Manning
1

Selain metode yang disebutkan di atas, Anda juga dapat menguraikannya sebagai koleksi Enumerable, seperti:

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

Ini juga memiliki manfaat tambahan untuk membuat hidup lebih mudah ketika membangun objek anonim, seperti ini:

 (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();

Akan tetapi, mengingat bahwa mem-parsing koleksi saat Enumerable menariknya ke dalam memori, sehingga bisa menjadi sumber daya yang intensif! Perhatian harus digunakan di sini.

Kesederhanaan Xtra
sumber
1

Juga, jika Anda ingin menggunakan konstruktor dengan beberapa objek untuk menginisialisasi, Anda mungkin mendapatkan kesalahan jika tidak ada nilai yang dikembalikan oleh Linq.

Jadi, Anda mungkin ingin melakukan sesuatu seperti ini:

(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();
Mahesh
sumber
1

Maaf karena terlambat ke pesta, tapi saya setelah menemukan ini , saya pikir ini harus dibagikan karena ini adalah implementasi yang paling bersih, tercepat dan juga hemat memori yang bisa saya temukan.

Disesuaikan dengan contoh Anda, Anda akan menulis:

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);
}

Keuntungan besar di sini (seperti yang ditunjukkan Damien Guard dalam komentar di tautan) adalah:

  • Membuat Anda aman dari menggunakan pola inisialisasi pada setiap kejadian.
  • Penggunaan melalui var foo = createPayments(bar);serta penggunaan melalui myIQueryable.ToPayments () mungkin.
Yoda
sumber
1

Saya memiliki masalah yang sama hari ini dan solusi saya mirip dengan apa yang terdaftar di Yoda, namun hanya bekerja dengan sintaks yang lancar.

Menyesuaikan solusi saya ke kode Anda: Saya menambahkan metode statis berikut ke kelas objek

    /// <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
        };
    }

dan kemudian memperbarui kueri dasar sebagai berikut:

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

Ini secara logis setara dengan solusi James Manning dengan keuntungan mendorong penggelembungan inisialisasi anggota ke Kelas / Objek Transfer Data

Catatan: Awalnya saya menggunakan lebih banyak nama deskriptif yang "Initializer" tetapi setelah meninjau bagaimana saya menggunakannya, saya menemukan bahwa "Initilizer" sudah cukup (setidaknya untuk tujuan saya).

Catatan Akhir:
Setelah menemukan solusi ini, saya awalnya berpikir akan mudah untuk membagikan kode yang sama dan mengadaptasinya agar berfungsi untuk sintaks Query juga. Saya tidak lagi percaya hal itu terjadi. Saya berpikir bahwa jika Anda ingin dapat menggunakan jenis steno konstruksi ini Anda akan memerlukan metode untuk setiap (query, fasih) fasih seperti yang dijelaskan di atas yang dapat ada di kelas objek itu sendiri.

Untuk sintaks kueri diperlukan metode ekstensi (atau beberapa metode di luar kelas dasar yang digunakan). (karena sintaks kueri ingin mengoperasikan IQueryable daripada T)

Berikut adalah contoh dari apa yang saya gunakan untuk akhirnya mendapatkan ini berfungsi untuk sintaks kueri. (Yoda sudah memakukan ini tapi saya pikir penggunaannya bisa lebih jelas karena saya tidak mendapatkannya pada awalnya)

/// <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
    };
}

dan penggunaannya

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select nalTmp).Initializer().ToList();
melayang
sumber
menambahkan bagian tentang sintaks kueri untuk kelengkapan ketika saya menyadari jawaban awal saya tidak berkembang dengan baik. @ yoda's anwer mungkin lebih baik dalam hal sintaks kueri.
Mengendarai
0

Meskipun terlambat untuk menjawab, itu masih bisa membantu seseorang dalam kesulitan. Karena LINQ ke entitas tidak mendukung konstruksi objek tanpa parameter. Namun, metode proyeksi untuk IEnumerable .

Jadi sebelum seleksi, konversikan IQueryable Anda ke IEnumerable dengan menggunakan kode ini:

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

Ini akan bekerja dengan baik. Namun, tentu saja, akan kehilangan manfaat dari kueri asli.

arslanahmad656
sumber
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; }
        ...
}
Jair Marques
sumber