LINQ to Entities, 'System.String Format (System.String, System.Object, System.Object)' yöntemini tanımıyor


88

Bu linq sorgum var:

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

Yine de sorunları var. Görevler oluşturmaya çalışıyorum. Her yeni görev için bağlantı metnini "Merhaba" gibi sabit bir dizeye ayarladığımda sorun değil. Ancak yukarıda, faturanın özelliklerini kullanarak mülkiyet bağlantı metnini oluşturmaya çalışıyorum.

Bu hatayı alıyorum:

base {System.SystemException} = {"LINQ to Entities, 'System.String Format (System.String, System.Object, System.Object)' yöntemini tanımıyor ve bu yöntem bir mağaza ifadesine çevrilemez." }

Nedenini bilen var mı? Bunu yapmak için alternatif bir yol bilen var mı?


Evet, aslında bunu kaçırdım
AnonyMouse

Yanıtlar:


148

Entity Framework, projeksiyonunuzu SQL tarafında yürütmeye çalışıyor, burada string.Format. AsEnumerable()Linq to Objects ile bu parçanın değerlendirilmesini zorlamak için kullanın .

Tabanlı önceki cevap üzerine ben böyle Sorgunuzla yeniden yapılandıracak size verdik:

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

Ayrıca, sorguda ( Organisation.Name) ilgili varlıkları kullandığınızı görüyorum, sorgunuza uygun olanı eklediğinizden emin olun Includeveya bu özellikleri daha sonra kullanmak için özellikle somutlaştırın, yani:

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

Yeni görev seç kısmının, ifade ağacı çevirisinden dolayı sunucu tarafında gerçekleşemeyeceği gerçeğine ek olarak, bunun istenmediği de unutulmamalıdır. Muhtemelen, görevlerin istemci tarafında oluşturulmasını istiyorsunuz. Bu nedenle, sorgunun ayrılması ve görevlerin oluşturulması daha da açık olabilir.
Tormod

3
Ayrıca, içinde yalnızca gerekli olan InvoiceNumber ve Organisation.Name bulunan anonim bir tür seçmenizi tavsiye ederim. Faturalar varlığı büyükse, sonraki AsEnumerable ile i seçin, yalnızca iki tane kullanıyor olsanız bile her sütunu geri çekecektir.
Devin

1
@Devin: Evet, katılıyorum - aslında ikinci sorgu örneğinin yaptığı tam olarak bu.
BrokenGlass

15

IQueryabletüretilmiştir IEnumerableC # anlatmak, ince andır ana benzerlik Sorgunuzu yaparken o 's dilinde veritabanı motoruna yayınlanmıştır olmasıdır, sunucu (değil istemci tarafında) verileri işlemek için veya sapa SQL söylemek veri.

Yani temelde dediğinizde IEnumerable.ToString(), C # veri toplamayı alır ToString()ve nesneyi çağırır . Ancak IQueryable.ToString()C # dediğinizde SQL'e ToString()nesneyi çağırmasını söyler ancak SQL'de böyle bir yöntem yoktur.

Dezavantajı, C # 'da verileri işlediğinizde, aradığınız tüm koleksiyonun, C # filtreleri uygulamadan önce bellekte oluşturulması gerektiğidir.

Bunu yapmanın en verimli yolu IQueryable, uygulayabileceğiniz tüm filtrelerde olduğu gibi sorguyu yapmaktır .

Ve sonra onu hafızada oluşturun ve veri formatını C # ile yapın.

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

3

SQL bir ile ne yapacağını string.Formatbilmese de, dize birleştirme gerçekleştirebilir.

Aşağıdaki kodu çalıştırırsanız, peşinde olduğunuz verileri almalısınız.

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

Sorguyu bir kez gerçekleştirdiğinizde, bu kullanmaktan çok daha hızlı olmalıdır AsEnumerable(en azından sizinle aynı orijinal hatayı yaptıktan sonra kendi kodumda bulduğum şey budur). C # ile daha karmaşık bir şey yapıyorsanız, yine de kullanmanız gerekecektir AsEnumerable.


2
Linq'in neden FORMATMESSAGE işlevini kullanacak şekilde uyarlanamadığından emin değilim docs.microsoft.com/en-us/sql/t-sql/functions/… O zamana kadar çözüm sizin çözümünüzdür (materyalizasyonu zorlamadan)
MemeDeveloper

2
Veritabanı yapısına ve ilgili sütunların sayısına bağlı olarak, bunun yerine bu yöntemi kullanmak AsEnumerable()çok daha verimli olabilir. Kaçının AsEnumerable()ve ToList()tüm sonuçları gerçekten hafızaya almak isteyene kadar.
Chris Schaller
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.