LINQ ke Entitas tidak mengenali metode metode 'System.String ToString ()', dan metode ini tidak dapat diterjemahkan ke dalam ekspresi toko

126

Saya memigrasikan beberapa hal dari satu server mysql ke server sql tetapi saya tidak tahu cara membuat kode ini berfungsi:

using (var context = new Context())
{
    ...

    foreach (var item in collection)
    {
        IQueryable<entity> pages = from p in context.pages
                                   where  p.Serial == item.Key.ToString()
                                   select p;
        foreach (var page in pages)
        {
            DataManager.AddPageToDocument(page, item.Value);
        }
    }

    Console.WriteLine("Done!");
    Console.Read();
}

Ketika masuk ke yang kedua foreach (var page in pages)itu melempar pengecualian mengatakan:

LINQ ke Entitas tidak mengenali metode metode 'System.String ToString ()', dan metode ini tidak dapat diterjemahkan ke dalam ekspresi toko.

Adakah yang tahu mengapa ini terjadi?

Erre Efe
sumber

Jawaban:

134

Simpan saja string ke variabel temp dan gunakan dalam ekspresi Anda:

var strItem = item.Key.ToString();

IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == strItem
                           select p;

Masalah muncul karena ToString()tidak benar-benar dieksekusi, itu berubah menjadi MethodGroup dan kemudian diuraikan dan diterjemahkan ke SQL. Karena tidak ada yang ToString()setara, ekspresi gagal.

catatan:

Pastikan Anda juga memeriksa jawaban Alex mengenai SqlFunctionskelas pembantu yang ditambahkan kemudian. Dalam banyak kasus dapat menghilangkan kebutuhan untuk variabel sementara.

Josh
sumber
14
Bagaimana jika ToString () saya diterapkan di sisi kiri kesetaraan? egpSerial.ToString () = item.
dotNET
3
@dotNet Itu masih akan gagal karena semuanya berubah menjadi Ekspresi, yang Entity Framework mencoba mengubahnya menjadi SQL yang valid. Ada beberapa metode yang bisa ditangani, tetapi ToString()bukan salah satunya.
Josh
7
@Josh: Saya mengerti bahwa itu akan gagal. Apa yang saya minta adalah solusi dari skenario itu, karena solusi di atas jelas tidak dapat diterapkan di sana.
dotNET
3
@Josh: Saya berjuang dengan satu skenario seperti itu. Katakanlah kolom OrderNumber saya adalah int, tetapi pengguna saya ingin dapat memfilter daftar OrderNumber ketika dia mengetik . Apakah saya tidak perlu melakukan ToString () pada kolom OrderNumber untuk mencapainya?
dotNET
5
@dNET, ini adalah salah satu skenario di mana ORM jatuh di wajahnya. Saya pikir tidak apa-apa dalam situasi itu untuk drop-down ke SQL baik lurus melalui ExecuteQueryatau dengan menggunakan Entity SQL denganObjectQuery<T>
Josh
69

Seperti orang lain telah menjawab, ini rusak karena. ToString gagal menerjemahkan ke SQL yang relevan dalam perjalanan ke database.

Namun, Microsoft menyediakan kelas SqlFunctions yang merupakan kumpulan metode yang dapat digunakan dalam situasi seperti ini.

Untuk kasus ini, apa yang Anda cari di sini adalah SqlFunctions.StringConvert :

from p in context.pages
where  p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
select p;

Baik ketika solusi dengan variabel sementara tidak diinginkan karena alasan apa pun.

Mirip dengan SqlFunctions Anda juga memiliki EntityFunctions (dengan EF6 usang oleh DbFunctions ) yang menyediakan serangkaian fungsi berbeda yang juga merupakan sumber data agnostik (tidak terbatas pada misalnya SQL).

Alex
sumber
4
Mereka menambahkan kelas SqlFunctions kembali. NET 4 dan saya baru belajar tentang itu? Temuan yang sangat baik.
James Skemp
24

Masalahnya adalah bahwa Anda memanggil ToString di kueri LINQ to Entities. Itu berarti parser sedang mencoba untuk mengubah panggilan ToString menjadi SQL yang setara (yang tidak mungkin ... maka pengecualian).

Yang harus Anda lakukan adalah memindahkan panggilan ToString ke saluran terpisah:

var keyString = item.Key.ToString();

var pages = from p in context.entities
            where p.Serial == keyString
            select p;
Justin Niessner
sumber
9

Punya masalah serupa. Memecahkannya dengan memanggil ToList () pada koleksi entitas dan menanyakan daftar. Jika koleksinya kecil ini adalah opsi.

IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())

Semoga ini membantu.

doktor sinis
sumber
42
Harap dicatat ini akan mengambil semua entitas Halaman dari database, dan melakukan pemfilteran di sisi klien alih-alih db .. biasanya bukan hal yang baik.
lambinator
3
Memang benar metode ini tidak efisien untuk semua tabel yang berisi lebih dari satu catatan, artinya semua tabel ada :-). Namun, jawaban ini memang membantu saya hari ini karena saya sedang melakukan proyeksi. Pilih yang termasuk toString () jadi memanggil .ToList () sebelum tangan tidak memiliki penalti kinerja untuk saya dan memanggil .ToList () memungkinkan saya untuk menggunakan .ToString () pemformatan dan .Pilih pernyataan saya ...
Nathan Prather
6

Ubah seperti ini dan seharusnya berfungsi:

var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == key
                           select p;

Alasan mengapa pengecualian tidak dilemparkan di baris permintaan LINQ dideklarasikan tetapi di baris foreachadalah fitur eksekusi yang ditangguhkan, yaitu permintaan LINQ tidak dieksekusi sampai Anda mencoba mengakses hasilnya. Dan ini terjadi di foreachdan bukan sebelumnya.

Daniel Hilgarth
sumber
6

Cast table to Enumerable, lalu Anda memanggil metode LINQ dengan menggunakan ToString()metode di dalam:

    var example = contex.table_name.AsEnumerable()
.Select(x => new {Date = x.date.ToString("M/d/yyyy")...)

Tapi hati-hati, ketika Anda menelepon AsEnumerableatau ToListmetode karena Anda akan meminta semua data dari semua entitas sebelum metode ini. Dalam kasus saya di atas, saya membaca semua table_namebaris dengan satu permintaan.

neustart47
sumber
5
biasanya bukan pilihan yang baik, .AsEnumerable () memasukkan semua data ke dalam memori, Anda dapat melihatnya lebih lanjut di sini: stackoverflow.com/questions/3311244/…
kavain
5

Memutakhirkan ke Entity Framework Versi 6.2.0 bekerja untuk saya.

Saya sebelumnya di Versi 6.0.0.

Semoga ini membantu,

93Ramadan
sumber
1

Di MVC, anggap Anda sedang mencari catatan berdasarkan kebutuhan atau informasi Anda. Ini berfungsi dengan baik.

[HttpPost]
[ActionName("Index")]
public ActionResult SearchRecord(FormCollection formcollection)
{       
    EmployeeContext employeeContext = new EmployeeContext();

    string searchby=formcollection["SearchBy"];
    string value=formcollection["Value"];

    if (formcollection["SearchBy"] == "Gender")
    {
        List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
        return View("Index", emplist);
    }
    else
    {
        List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
        return View("Index", emplist);
    }         
}
shakti
sumber
2
Untuk praktik yang lebih baik, atau dalam jenis kode produksi, Anda harus selalu memiliki peristiwa basis data di lapisan layanan atau lapisan data dan tidak langsung dalam tindakan.
TGarrett
0

Jika Anda benar-benar ingin mengetik ToStringdi dalam kueri Anda, Anda bisa menulis pengunjung pohon ekspresi yang menulis ulang panggilan ToStringdengan panggilan ke StringConvertfungsi yang sesuai :

using System.Linq;
using System.Data.Entity.SqlServer;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using System;

namespace ToStringRewriting {
    class ToStringRewriter : ExpressionVisitor {
        static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
                 .Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));

        protected override Expression VisitMethodCall(MethodCallExpression node) {
            var method = node.Method;
            if (method.Name=="ToString") {
                if (node.Object.GetType() == typeof(string)) { return node.Object; }
                node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
            }
            return base.VisitMethodCall(node);
        }
    }
    class Person {
        string Name { get; set; }
        long SocialSecurityNumber { get; set; }
    }
    class Program {
        void Main() {
            Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
            var rewriter = new ToStringRewriter();
            var finalExpression = rewriter.Visit(expr);
            var dcx = new MyDataContext();
            var query = dcx.Persons.Where(finalExpression);

        }
    }
}
Zev Spitz
sumber
Harus menggunakan FirstOrDefault dan bukan hanya Pertama ... Jika ini adalah kunci utama, maka gunakan Find, karena kinerjanya lebih baik.
TGarrett
@TGarrett Satu-satunya penggunaan di Firstsini adalah pada hasil GetMethods()yang kembali MethodInfo[]. AFAIK, MethodInfo[]tidak memiliki Findmetode, juga tidak ada metode ekstensi seperti itu. Tetapi saya benar-benar harus menggunakan Singlekarena metode ini ditemukan melalui refleksi, dan tidak akan ada kesalahan waktu kompilasi jika metode yang sesuai tidak dapat diselesaikan.
Zev Spitz
0

Saya mendapat kesalahan yang sama dalam hal ini:

var result = Db.SystemLog
.Where(log =>
    eventTypeValues.Contains(log.EventType)
    && (
        search.Contains(log.Id.ToString())
        || log.Message.Contains(search)
        || log.PayLoad.Contains(search)
        || log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
    )
)
.OrderByDescending(log => log.Id)
.Select(r => r);

Setelah menghabiskan terlalu banyak waktu untuk debugging, saya menemukan kesalahan yang muncul dalam ekspresi logika.

Baris pertama search.Contains(log.Id.ToString())tidak berfungsi dengan baik, tetapi baris terakhir yang berhubungan dengan objek DateTime membuatnya gagal total:

|| log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)

Hapus garis yang bermasalah dan masalah dipecahkan.

Saya tidak sepenuhnya mengerti mengapa, tetapi sepertinya ToString () adalah ekspresi LINQ untuk string, tetapi tidak untuk Entitas. LINQ untuk Entitas berhubungan dengan permintaan basis data seperti SQL, dan SQL tidak memiliki gagasan ToString (). Dengan demikian, kita tidak bisa melempar ToString () ke klausa .Where ().

Tetapi bagaimana cara kerja baris pertama? Alih-alih ToString (), SQL punya CASTdan CONVERT, jadi tebakan terbaik saya sejauh ini adalah bahwa LINQ untuk entitas menggunakan itu dalam beberapa kasus sederhana. Objek DateTime tidak selalu ditemukan sesederhana itu ...

pekaaw
sumber
-8

Cukup putar LINQ menjadi kueri Entitas menjadi kueri LINQ ke Objek (mis. Panggil ToArray) kapan saja Anda perlu menggunakan pemanggilan metode dalam kueri LINQ Anda.

T. Webster
sumber
3
"kapan saja Anda perlu menggunakan metode panggilan" adalah saran yang buruk - dengan banyak catatan ini bisa menjadi masalah besar. Jawaban yang diterima jauh lebih baik untuk skenario ini.
PeteGO