Linq à EntityFramework DateHeure

108

Dans mon application, j'utilise Entity Framework.

Ma table

-Article
-period
-startDate

J'ai besoin d'enregistrements qui correspondent => DateTime.Now > startDate and (startDate + period) > DateTime.Now

J'ai essayé ce code mais il fonctionne maintenant

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

Lorsque j'exécute mon code, l'exception suivante se produit

LINQ to Entities ne reconnaît pas la méthode 'System.DateTime AddDays (Double)', et cette méthode ne peut pas être traduite en une expression de magasin.

Yucel
la source
Quel type est period? AddDaysest la mauvaise fonction si c'est un double.
Craig Stuntz

Réponses:

201

Lorsque vous utilisez LINQ to Entity Framework, vos prédicats à l'intérieur de la clause Where sont traduits en SQL. Vous obtenez cette erreur car il n'y a pas de traduction en SQL pour DateTime.Add()ce qui a du sens.

Une solution rapide consiste à lire les résultats de la première instruction Where en mémoire, puis à utiliser LINQ to Objects pour terminer le filtrage:

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

Vous pouvez également essayer la méthode EntityFunctions.AddDays si vous utilisez .NET 4.0:

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

Remarque: dans EF 6 c'est maintenant System.Data.Entity.DbFunctions.AddDays.

Justin Niessner
la source
42
C'est une solution de contournement dangereuse, que se passe-t-il si ToList () renvoie une énorme quantité de données?
Stefan P.
7
Merci pour le conseil EntityFunctions.AddDays J'utilise EF .net 4.0 mais je ne connaissais pas EntityFunctions, je vais l'examiner.
Stefan P.
2
@SaeedAlg - Dans le premier exemple, il est nécessaire de lire les éléments en mémoire. Dans le deuxième exemple, j'ai conservé deux clauses Where pour correspondre au format d'origine. Même si vous compressez les deux en une seule clause Where, Entity Framework génère l'identité SQL, donc c'est vraiment une question de lisibilité.
Justin Niessner
2
@Justin Niessner merci beaucoup, j'essaye de faire ça pendant quatre heures, tu me
sauves la
2
Lorsque vous donnez des solutions de contournement «rapides» avec EF, nous devrions tous éviter l'utilisation des méthodes ToList et ToArray. Il est tout aussi rapide de calculer la date précédemment et de la comparer à cette valeur, et cela fonctionne dans une requête EF sans instancier ses résultats en mémoire.
Isaac Llopis
92

Je pense que c'est ce que cette dernière réponse essayait de suggérer, mais plutôt que d'essayer d'ajouter des jours à p.startdat (quelque chose qui ne peut pas être converti en instruction SQL), pourquoi ne pas faire quelque chose qui peut être assimilé à SQL:

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

something.Where(p => p.startdate >= baselineDate)
Andrew
la source
2
@Adrian Carr - Cela peut être mieux pour ce cas d'utilisation mais pas pour autant que la EntityFunctionssolution. Ici, le deuxième opérande n'est pas extrait d'une autre entité dans la requête et peut être calculé avant la requête. Si les deux opérandes devaient être trouvés dans db, la EntityFunctionssolution serait toujours appropriée tandis que la solution de cette réponse ne fonctionnerait plus.
Frédéric
C'est une meilleure solution que la solution indiquée comme réponse. La réponse de Justin / réponse marquée renverra des résultats inutiles de la base de données. Cette solution envoie en fait la date dans le SQL où filtre et utilise SQL pour filtrer les données, ce qui est beaucoup plus performant.
Paul
3

Que diriez-vous de soustraire 2 jours de DateTime.

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

Pour être honnête, je ne sais pas ce que vous essayez d'accomplir, mais cela peut fonctionner

TimC
la source
Les articles commenceront à être affichés le StartDate et se termineront après x (période) jours. C'est ce que j'essaye de faire. La deuxième solution de Justin Niessner a très bien fonctionné pour ce que je veux voir
Yucel
3

Si vous avez besoin que votre expression soit traduite en SQL, vous pouvez essayer d'utiliser

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

En fait, il est marqué obsolète mais cela fonctionne. Il devrait être remplacé par System.Data.Entity.DbFunctions.AddDays mais je ne le trouve pas ...

bubi
la source
Cela n'ajoute rien de plus que la réponse acceptée de 4 ans auparavant!
Andrew Harris
@AndrewHarris aussi ma réponse est la réponse il y a 4 ans. Dans la première version de la réponse, il y avait une ToList (=> matérialisation des données) avant le Where (voir le premier commentaire de la réponse).
bubi