Perché Entity Framework non è in grado di utilizzare ToString () in una istruzione LINQ?

Funziona in LINQ-to-SQL:

var customersTest = from c in db.Customers select new { Id = c.Id, Addresses = from a in db.Addresses where c.Id.ToString() == a.ReferenzId select a }; foreach (var item in customersTest) { Console.WriteLine(item.Id); } 

Ma un esempio simile in Entity Framework ottiene un messaggio di errore che dice fondamentalmente che non può “tradurlo in SQL”, ecco il messaggio di errore originale in tedesco:

“‘LINQ alle entity framework‘ erkennt die Methode ‘System.String ToString ()’ nicht, und diese Methode kann nicht in einen Speicherausdruck übersetzt werden.”

Traduzione:

“‘LINQ to Entities’ non riconosce il metodo ‘System.String ToString ()’, questo metodo non può essere tradotto in un’espressione di memoria.

Qualcuno può far luce su come possiamo far funzionare questo tipo di istruzione in Entity Framework o spiegare perché riceve questo errore?

In poche parole: LINQ to Entities non conosce la conversione dal tuo tipo di ID a una stringa.

Qual è il tipo di c.ID ? C’è qualche ragione per cui è un tipo per ID, ma un altro per ReferenzId ? Se ansible, rendili uguali, a quel punto non avrai più nessun problema. Non so se ci sono altri modi per eseguire conversioni in LINQ su entity framework – ci potrebbe essere – ma l’allineamento dei tipi sarebbe più pulito.

A proposito, sembra davvero un join:

 var query = from c in db.Customers join a in db.Addresses on c.Id equals a.ReferenzId into addresses select new { Id = c.Id, Addresses = addresses }; 

EDIT: per rispondere al tuo commento – ToString appare in IntelliSense perché il compilatore non ha idea di cosa significhi la tua query o di come verrà tradotta. È C # perfettamente valido e può generare un albero di espressioni valido: è solo che EF non sa come convertire quell’albero di espressioni in SQL.

Potresti provare a utilizzare Convert.ToString(c.Id) invece di chiamare semplicemente c.Id.ToString()

Non ha senso per me perché Linq2EF non traduca .ToString() in un’istruzione SQL corretta, come fa Linq2SQL, solo il team di sviluppo di Microsoft conosce il motivo per cui non l’hanno ancora implementato. 🙁

Ma puoi aumentare la priorità per implementarlo se si vota per questa funzione seguendo questo link.

Fortunatamente ci sono anche 2 soluzioni alternative , entrambe utilizzate recentemente nelle query EF:


I) Ciò che mi ha aiutato a superare questa limitazione è stato quello di cambiare la query in una lista , in questo modo:

 var customersList = (from c in db.Customers select c).ToList(); // converts to IEnumerable ... var customersTest = (from c in customersList select new {Id=c.ID.ToString()}); // ... which allows to use .ToString() 

L’istruzione .ToList() converte in IEnumerable , dove .ToString() è disponibile. Si noti che, in base ai requisiti, è ansible utilizzare anche .AsEnumerable() , che presenta il vantaggio che è supportata l’esecuzione differita, il che è migliore se si hanno più query di linq in base l’una all’altra o se si utilizzano valori di parametri diversi ( molte grazie a Divega per questo suggerimento!).

Successivamente è ansible utilizzare questa query come si desidera, ad esempio:

 var customersTest2 = from c in customersTest select new { Id = c.Id, Addresses = from a in db.Addresses where c.Id == a.ReferenzId select a }; 

Naturalmente se hai bisogno puoi aggiungere più proprietà agli oggetti dei customersTest . Prova come richiesto. Puoi anche ottimizzare la query sopra, ho solo usato 3 passaggi per la leggibilità di questo esempio.


II) Per conversioni semplici e se è necessario riutilizzare la query generata in ulteriori sottoquery (e deve rimanere IQueryable ), utilizzare SqlFunctions da System.Data.Objects.SqlClient , verranno SqlFunctions correttamente in query SQL.

Esempio 1 : conversione della data (devi usare dateparts come mostrato di seguito)

 var customersTest = from c in db.Customers select new { strDate=SqlFunctions.DateName("dd", c.EndDate) +"."+SqlFunctions.DateName("mm", c.EndDate) +"."+SqlFunctions.DateName("yyyy", c.EndDate) } 

Esempio 2 : conversione da numerico a stringa

 var customersTest = from c in db.Customers select new { strID=SqlFunctions.StringConvert((double)c.ID) } 

Questo dovrebbe aiutarti nella maggior parte delle situazioni in cui sono richieste conversioni in stringhe.


Aggiornamento: Se hai seguito il link che ti ho dato all’inizio della mia risposta, questa caratteristica mancante ha nel frattempo ricevuto 75 voti ed è ora (finalmente!) Implementata da Microsoft in EF 6.1 . A tutti coloro che hanno partecipato: Grazie per aver votato! La tua voce è stata ascoltata.

Per esempio:

 var query = from e in context.Employees where e.EmployeeID.ToString() == "1" select e; 

ora sarà tradotto in:

 DECLARE @p0 NVarChar(1000) = '1' SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title], [t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address],[t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension], [t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath] FROM [Employees] AS [t0] WHERE (CONVERT(NVarChar,[t0].[EmployeeID])) = @p0 

cioè e.EmployeeID.ToString() traduce in (CONVERT(NVarChar,[t0].[EmployeeID])) .

Entity Framework 6.1 RTM appena rilasciato ora supporta .ToString ()

LINQ to Entities per quanto ho capito (per v1) è molto primitivo. In altre parole non sa come prendere il metodo di estensione “ToString ()” e generare l’SQL per esso.

In LINQ to SQL, esegue il metodo di estensione “ToString ()” prima di generare l’SQL. La differenza è che LINQ to Entities utilizza IQueryable invece di IEnumerable.

MA, da quello che ricordo il casting dovrebbe funzionare (perché il casting è un tipo di dati e SQL sa di CAST ()).

Così

c.Id.ToString () dovrebbe davvero essere (stringa) c.Id

(Inoltre, assicurarsi che sia (stringa) e non (stringa)).

Uno degli inconvenienti che vorrei dire sull’utilizzo di Lambda (in Entity Framework) per generare l’espressione SQL invece del puro LINQ.

Tenete a mente anche che l’utilizzo di CAST sul lato sinistro del segno di uguale in SQL è un po ‘malandato 🙂