Linq a EntityFramework DateTime


108

Nella mia applicazione sto usando Entity Framework.

Il mio tavolo

-Article
-period
-startDate

Ho bisogno di record che corrispondono => DateTime.Now > startDate and (startDate + period) > DateTime.Now

Ho provato questo codice ma ora funziona

Context.Article
    .Where(p => p.StartDate < DateTime.Now)
    .Where(p => p.StartDate.AddDays(p.Period) > DateTime.Now)

Quando eseguo il codice, si verifica la seguente eccezione

LINQ to Entities non riconosce il metodo "System.DateTime AddDays (Double)" e questo metodo non può essere tradotto in un'espressione di archivio.


Che tipo è period? AddDaysè la funzione sbagliata se è un file double.
Craig Stuntz

Risposte:


201

Quando si usa LINQ to Entity Framework, i predicati all'interno della clausola Where vengono tradotti in SQL. Stai ricevendo quell'errore perché non esiste una traduzione in SQL per il DateTime.Add()quale ha senso.

Una soluzione rapida sarebbe leggere i risultati della prima istruzione Where in memoria e quindi utilizzare LINQ to Objects per terminare il filtraggio:

Context.Article.Where(p => p.StartDate < DateTime.Now)
               .ToList()
               .Where(p => p.StartDate.AddDays(p.Period) > DateTime.Now);

Puoi anche provare il metodo EntityFunctions.AddDays se stai usando .NET 4.0:

Context.Article.Where(p => p.StartDate < DateTime.Now)
               .Where(p => EntityFunctions.AddDays(p.StartDate, p.Period)
                   > DateTime.Now);

Nota: EF 6è ora System.Data.Entity.DbFunctions.AddDays.


42
Questa è una soluzione pericolosa, cosa succede se ToList () restituisce un'enorme quantità di dati?
Stefan P.

7
Grazie per il suggerimento di EntityFunctions.AddDays Sto usando EF .net 4.0 ma non sapevo di EntityFunctions, lo esaminerò.
Stefan P.

2
@SaeedAlg - Nel primo esempio è necessario leggere gli elementi in memoria. Nel secondo esempio ho mantenuto due clausole Where in modo che corrispondessero al formato originale. Anche se comprimi i due in una singola clausola Where, Entity Framework genera identità SQL, quindi è davvero una questione di leggibilità.
Justin Niessner

2
@Justin Niessner grazie mille, sto provando a farlo per quattro ore, mi salvi la vita in pochi secondi grazie ancora
Yucel

2
Quando forniamo soluzioni "rapide" con EF, dovremmo tutti evitare l'uso dei metodi ToList e ToArray. È altrettanto veloce calcolare la data in precedenza e confrontarla con quel valore, e funziona in una query EF senza istanziare i risultati in memoria.
Isaac Llopis

92

Penso che questo sia ciò che l'ultima risposta stava cercando di suggerire, ma piuttosto che provare ad aggiungere giorni a p.startdat (qualcosa che non può essere convertito in un'istruzione sql) perché non fare qualcosa che può essere equiparato a sql:

var baselineDate = DateTime.Now.AddHours(-24);

something.Where(p => p.startdate >= baselineDate)

2
@Adrian Carr - Può essere migliore per questo caso d'uso ma non per tanti quanti la EntityFunctionssoluzione. Qui, il secondo operando non viene recuperato da un'altra entità nella query e può essere calcolato prima della query. Se entrambi gli operandi dovessero essere trovati in db, la EntityFunctionssoluzione sarebbe comunque adatta mentre la soluzione di questa risposta non funzionerebbe più.
Frédéric

Questa è una soluzione migliore della soluzione contrassegnata come risposta. La risposta di Justin / risposta contrassegnata restituirà risultati non necessari dal database. Questa soluzione invia effettivamente la data nell'SQL in cui filtra e utilizza SQL per filtrare i dati, che è molto più performante.
Paul,

3

Che ne dici di sottrarre 2 giorni da DateTime.Now:

Context.Article
.Where(p => p.StartDate < DateTime.Now)
.Where(p => p.StartDate > DateTime.Now.Subtract(new TimeSpan(2, 0, 0, 0)))

Ad essere onesti, non sono sicuro di cosa stai cercando di ottenere, ma potrebbe funzionare


Gli articoli inizieranno ad essere mostrati in StartDate e finiranno dopo x (periodo) giorni. Questo è quello che sto cercando di fare. La seconda soluzione di Justin Niessner ha funzionato molto bene per quello che voglio vedere
Yucel

3

Se hai bisogno che la tua espressione venga tradotta in SQL puoi provare a usare

Metodo System.Data.Entity.Core.Objects.AddDays.

In realtà è contrassegnato come obsoleto ma funziona. Dovrebbe essere sostituito da System.Data.Entity.DbFunctions.AddDays ma non riesco a trovarlo ...


Questo non aggiunge altro che la risposta accettata di 4 anni prima!
Andrew Harris

@AndrewHarris anche la mia risposta è di 4 anni fa. Nella prima versione della risposta c'era una ToList (=> materializzazione dei dati) prima del Where (vedi il primo commento della risposta).
bubi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.