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.