LINQ ke Entitas tidak mengenali metode 'System.String Format (System.String, System.Object, System.Object)'

88

Saya memiliki kueri linq ini:

private void GetReceivedInvoiceTasks(User user, List<Task> tasks)
{
    var areaIds = user.Areas.Select(x => x.AreaId).ToArray();

    var taskList = from i in _db.Invoices
                   join a in _db.Areas on i.AreaId equals a.AreaId
                   where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
                   select new Task {
                       LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name),
                       Link = Views.Edit
                   };
}

Ini memiliki masalah. Saya mencoba membuat tugas. Untuk setiap tugas baru ketika saya menyetel teks tautan ke string konstan seperti "Halo" tidak apa-apa. Namun di atas saya mencoba membangun teks tautan properti menggunakan properti faktur.

Saya mendapatkan kesalahan ini:

base {System.SystemException} = {"LINQ ke Entitas tidak mengenali metode metode 'System.String Format (System.String, System.Object, System.Object)', dan metode ini tidak dapat diterjemahkan ke dalam ekspresi penyimpanan." }

Ada yang tahu kenapa? Adakah yang tahu cara alternatif melakukan ini untuk membuatnya berhasil?

AnonyMouse
sumber
Ya, awalnya melewatkan itu
AnonyMouse

Jawaban:

148

Kerangka Kerja Entitas mencoba menjalankan proyeksi Anda di sisi SQL, di mana tidak ada yang setara dengan string.Format. Gunakan AsEnumerable()untuk memaksa evaluasi bagian itu dengan Linq ke Objek.

Berdasarkan jawaban sebelumnya yang saya berikan kepada Anda, saya akan menyusun ulang kueri Anda seperti ini:

int statusReceived = (int)InvoiceStatuses.Received;
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray();

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select i)
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name),
                  Link = Views.Edit
                });

Juga saya melihat Anda menggunakan entitas terkait di query ( Organisation.Name) pastikan Anda menambahkan yang tepat Includeke kueri Anda, atau secara khusus mewujudkan properti tersebut untuk digunakan nanti, yaitu:

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name})
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName),
                  Link = Views.Edit
                });
Gelas pecah
sumber
Selain fakta bahwa bagian tugas-baru-pilih tidak dapat terjadi di sisi server karena terjemahan pohon ekspresi, juga harus dicatat bahwa tidak diinginkan untuk melakukannya. Agaknya, Anda ingin tugas dibuat di sisi klien. Oleh karena itu, pemisahan kueri dan pembuatan tugas bisa lebih eksplisit.
Tormod
3
Saya juga akan merekomendasikan memilih jenis anonim yang hanya memiliki InvoiceNumber dan Organisation.Name yang dibutuhkan di dalamnya. Jika entitas faktur besar maka pilih i dengan AsEnumerable berikutnya akan menarik kembali setiap kolom meskipun Anda hanya menggunakan dua.
Devin
1
@ Devin: Ya, saya setuju - sebenarnya itulah yang dilakukan oleh contoh kueri kedua.
BrokenGlass
15

IQueryableberasal dari IEnumerable, kemiripan utamanya adalah ketika Anda membuat kueri, kueri tersebut dikirim ke mesin database dalam bahasanya, saat yang tepat adalah saat Anda memberi tahu C # untuk menangani data di server (bukan sisi klien) atau memberi tahu SQL untuk menangani data.

Jadi pada dasarnya ketika Anda mengatakan IEnumerable.ToString(), C # mendapatkan pengumpulan data dan panggilan ToString()pada objek. Tetapi ketika Anda mengatakan IQueryable.ToString()C # memberitahu SQL untuk memanggil ToString()objek tetapi tidak ada metode seperti itu dalam SQL.

Kekurangannya adalah ketika Anda menangani data di C #, seluruh koleksi yang Anda cari harus dibangun di memori sebelum C # menerapkan filter.

Cara paling efisien untuk melakukannya adalah dengan membuat kueri seperti IQueryablesemua filter yang bisa Anda terapkan.

Dan kemudian membangunnya di memori dan membuat format data di C #.

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe");

 var inMemCollection = dataQuery.AsEnumerable().Select(c => new
                                                  {
                                                     c.ID
                                                     c.Name,
                                                     c.ZIP,
                                                     c.DateRegisterred.ToString("dd,MMM,yyyy")
                                                   });
Nikolay
sumber
3

Sementara SQL tidak tahu apa yang harus dilakukan dengan string.Formatitu, ia dapat melakukan penggabungan string.

Jika Anda menjalankan kode berikut maka Anda harus mendapatkan data yang Anda cari.

var taskList = from i in _db.Invoices
               join a in _db.Areas on i.AreaId equals a.AreaId
               where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
               select new Task {
                   LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name),
                   Link = Views.Edit
               };

Setelah Anda benar-benar melakukan kueri, ini seharusnya sedikit lebih cepat daripada menggunakan AsEnumerable(setidaknya itulah yang saya temukan di kode saya sendiri setelah mengalami kesalahan asli yang sama seperti Anda). Jika Anda melakukan sesuatu yang lebih kompleks dengan C # maka Anda masih perlu menggunakannya AsEnumerable.

d219
sumber
2
Tidak yakin mengapa Linq tidak dapat diadaptasi untuk menggunakan fungsi FORMATMESSAGE docs.microsoft.com/en-us/sql/t-sql/functions/… Hingga saat itu solusi Anda adalah (tanpa memaksa materialisasi)
MemeDeveloper
2
Bergantung pada struktur database dan jumlah kolom terkait, menggunakan metode ini AsEnumerable()dapat jauh lebih efisien. Hindari AsEnumerable()dan ToList()sampai Anda benar-benar ingin membawa semua hasil ke dalam ingatan.
Chris Schaller