2009-12-17 12 views
13

Questo opere in LINQ to SQL:Perché Entity Framework non è in grado di utilizzare ToString() in una istruzione LINQ?

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 riceve un messaggio di errore di che in pratica dice che non può "tradurre in SQL", qui è il messaggio originale di errore in tedesco:

" 'LINQ to Entities' erkennt muoiono Methode 'System.String ToString()' nicht, und diese Methode kann nicht einen in Speicherausdruck Übersetzt werden. "

Traduzione:.

" 'LINQ alle entità' non riconosce Metodo 'System.String ToString()', questo metodo non può essere tradotto in un'espressione memoria

Qualcuno può far luce su come possiamo far funzionare questo tipo di istruzione in Entity Framework o spiegare perché si ottiene t il suo errore?

+0

è l'ID di una stringa già? Non sono sicuro del motivo per cui l'EF potrebbe causare un problema, ma il messaggio di errore sembra indicare che si tratta di un ostacolo. Stringa di cose.ToString() funzionerebbe abbastanza rapidamente: P –

+0

Id è un numero intero nell'esempio L2S. Nel nostro vero esempio EF è un GUID. È interessante notare che ToString() è in intellisense e compila ma ottiene quell'errore di runtime. –

+0

@Edward Tanguay: ho appena visto la tua domanda e ho aggiunto una risposta, spero che sia ancora utile anche dopo così tanto tempo ... – Matt

risposta

9

In poche parole: LINQ su Entità non conosce la conversione dal 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 possibile, rendili uguali, a quel punto non avrai più nessun problema. Non so se ci sono altri modi per eseguire conversioni in LINQ su entità - ci potrebbe essere - ma l'allineamento dei tipi sarebbe più pulito.

A proposito, davvero sembra che sia 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 IntelliSense perché il compilatore non ha una vera idea di cosa la query sta a significare o come sarà tradotto. È C# perfettamente valido e può generare un albero di espressioni valido: è solo che EF non sa come convertire quell'albero di espressioni in SQL.

Si potrebbe provare a utilizzando Convert.ToString(c.Id) invece di chiamare c.Id.ToString() ...

+0

è un database storico che stiamo usando dove a.ReferenzID è davvero una stringa, il motivo è che può essere unito a un int in una tabella ma un GUID in un altro. –

+1

grazie ma Convert.ToString (c.Id) ci dà lo stesso errore –

+0

Prova a convertire il contrario? Chiama invece 'nuovo Guid (a.ReferenzId)'? –

-2

LINQ to Entities, per quanto ho capito (per v1) è molto primitiva. 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 conosce CAST()).

Così

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

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

Uno dei downfalls che vorrei dire sull'utilizzo di Lambda (in Entity Framework) per generare l'espressione SQL anziché LINQ puro.

Tenete presente anche che l'utilizzo CAST sul lato sinistro del segno di uguale in SQL è un po 'malato di eseguire :-)

+0

scusa per il ritardo DV: '(stringa) c.Id' non verrebbe compilato con un" Impossibile convertire il tipo 'int' in 'stringa'. " –

+0

Da quello che hai scritto, si potrebbe pensare che Linq2SQL utilizzi IEnumerable piuttosto che IQueryable, che ovviamente NON è vero. –

+1

@Wiktor Zychla: L'ho verificato, non puoi semplicemente eseguire il cast in una query EF usando '(string)' come operatore di cast. Ma date un'occhiata alla mia risposta, ora ho due opzioni (pubblicate [qui] (http://stackoverflow.com/a/17447930/1016343) che funzionano bene per me. – Matt

8

Non ha senso per me perché Linq2EF non si traduce in un vero .ToString() SQL dichiarazione, come fa Linq2SQL, solo il Microsoft team di sviluppo conosce il motivo per cui non hanno ancora applicarla. :-(

Ma si può alzare la priorità per la sua attuazione, se si voto per questa funzione seguendo this link.

Per fortuna ci sono 2 soluzioni alternative disponibili anche, entrambi ho usato di recente in query EF:


I) ciò che mi ha aiutato a ottenere intorno a questa limitazione è stato quello di modificare la query in un elenco, in questo modo:

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

La dichiarazione .ToList() convertiti IEnumerable<T>, dove .ToString() è disponibile. Note: che, a seconda dei requisiti, è possibile utilizzare anche .AsEnumerable(), il che ha il vantaggio di supportare l'esecuzione differita che è meglio se si hanno più query di linq l'una a seconda dell'altra o se si utilizzano valori di parametro diversi (molti grazie a Divega per questo suggerimento!).

In seguito è possibile utilizzare questa query come si desidera, ad es .:

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 avete bisogno è possibile aggiungere più proprietà agli oggetti di customersTest come richiesto. Puoi anche ottimizzare la query sopra, ho solo usato 3 passaggi per la leggibilità di questo esempio.


II) Per le conversioni semplici, e se si dispone di riutilizzare la query generato in ulteriori subquery (ed è necessario rimanere IQueryable), utilizzare SqlFunctions da System.Data.Objects.SqlClient, saranno tradotti in query SQL in modo corretto.

Esempio 1: Data di conversione (è necessario utilizzare dateparts come di seguito indicati)

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: numerico alla conversione stringa

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

Questo dovrebbe aiutare sei fuori dalla 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 implemented by Microsoft in EF 6.1 (finalmente!). A tutti coloro che hanno partecipato: Grazie per aver votato! La tua voce è stata ascoltata.

Ad esempio:

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

verrà ora tradotto a:

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 (CONVERT(NVarChar,[t0].[EmployeeID])).

+1

Puoi anche noi AsEnnumerable() invece di ToList(). Questa potrebbe essere una soluzione migliore se si desidera preservare l'esecuzione differita e la possibilità di eseguire la query più volte con valori di parametri diversi. – divega

+0

@divega: Grazie per il suggerimento, l'ho provato e funziona bene! Ho anche aggiornato la mia risposta. – Matt

+0

** Un altro suggerimento **: Se stai usando 'SqlFunctions.StringConvert (...)', allora avrai bisogno di '.Trim() 'in alcuni casi per rimuovere gli spazi creati dalla funzione di conversione. Sorprendentemente, puoi usare 'SqlFunctions.StringConvert (...). Trim()' in una query! :-) – Matt

6

Entity Framework 6.1 RTM che è stato appena rilasciato ora supportare .ToString()

+0

Sì, ho notato per coincidenza che lo supporta. Ma questo non è menzionato in alcun riferimento. Potrebbe per favore fornire un riferimento a questo problema? –

+0

È pubblicato nella risposta sopra http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/2327346-support-tostring-method-in-linq-to-entities –

Problemi correlati