Le membre de type spécifié 'Date' n'est pas pris en charge dans l'exception LINQ to Entities

105

J'ai eu une exception lors de la mise en œuvre des déclarations suivantes.

 DateTime result;
 if (!DateTime.TryParse(rule.data, out result))
     return jobdescriptions;
 if (result < new DateTime(1754, 1, 1)) // sql can't handle dates before 1-1-1753
     return jobdescriptions;
 return jobdescriptions.Where(j => j.JobDeadline.Date == Convert.ToDateTime(rule.data).Date );

Exception

The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

Je sais ce que signifie l'exception mais je ne sais pas comment m'en débarrasser. De l'aide?

nébuleuse
la source
C'est dans EF6 et inférieur. Le noyau EF prend en charge .Date.
Gert Arnold

Réponses:

102

LINQ to Entities ne peut pas traduire la plupart des méthodes de date .NET (y compris la conversion que vous avez utilisée) en SQL car il n'existe pas de SQL équivalent.

La solution consiste à utiliser les méthodes Date en dehors de l'instruction LINQ, puis à transmettre une valeur. Il semble que Convert.ToDateTime (rule.data) .Date est à l'origine de l'erreur.

L'appel de Date sur une propriété DateTime ne peut pas non plus être traduit en SQL, une solution de contournement consiste donc à comparer les propriétés .Year .Month et .Day qui peuvent être traduites en LINQ car ce ne sont que des entiers.

var ruleDate = Convert.ToDateTime(rule.data).Date;
return jobdescriptions.Where(j => j.Deadline.Year == ruleDate.Year 
                       && j.Deadline.Month == ruleDate.Month 
                       && j.Deadline.Day == ruleDate.Day);
Judo
la source
6
Et j => j.JobDeadline.Date?
nébuleuse
1
La date est-elle une propriété sur JobDeadline? Cela ne devrait pas causer d'erreur en soi - peut-être un conflit de nom (mais pas sûr à ce sujet). Si la ligne cause toujours une erreur, renommez-la simplement Date limite ou similaire.
Judo
1
La date est la propriété sur JobDeadline. JobDeadline est le type DateTime dont je veux extraire la date.
nébuleuse
Ensuite, pour travailler avec LINQ, vous devez simplement comparer la propriété JobDeadline, par exemple j.JobDeadline> ruleDate. Cela nécessite un peu de test mais peut être fait pour fonctionner. Vous pouvez également comparer les trois propriétés de .Month .Day et .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Pas élégant mais cela fonctionne car ce ne sont que des entiers.
Judo
Hmm. Cette idée fonctionne. Sale mais fonctionne. Si vous écrivez comme réponse, je peux la marquer comme correcte.
nébuleuse
230

Vous pouvez utiliser la méthode TruncateTime des EntityFunctions pour obtenir une traduction correcte de la Datepropriété en SQL:

using System.Data.Objects; // you need this namespace for EntityFunctions

// ...

DateTime ruleData = Convert.ToDateTime(rule.data).Date;
return jobdescriptions
    .Where(j => EntityFunctions.TruncateTime(j.JobDeadline) == ruleData);


Mise à jour: EntityFunctions est obsolète dans EF6, utilisezDbFunctions.TruncateTime

Slauma
la source
Eh bien, j'ai remarqué que ruleDatac'est du DateTimetype et que le temps j.JobDeadlineest tronqué. Ça ne va pas. Je n'ai pas eu d'exception mais n'a pas obtenu le résultat attendu.
nébuleuse
@aneal: Il renvoie tous les enregistrements où JobDeadlinea la même date que rule.data, quelle que soit l' heure de la journée . N'est-ce pas ce que vous voulez réaliser avec votre requête dans la question? Pourquoi ça ne va pas?
Slauma
1
+1 et je suis d'accord avec ci-dessus, est certainement une meilleure réponse pour 99% des implémentations
jim tollan
26
Notez que EntityFunctionsc'est obsolète dans EF6, vous devez maintenant utiliser DbFunctions.
Julien N
2
L'espace de noms pour DbFunctions dans> EF6 est System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF
39

Pour EF6, utilisez plutôt DbFunctions.TruncateTime (mydate).

KingOfHypocrites
la source
9

"EntityFunctions.TruncateTime" ou "DbFunctions.TruncateTime" dans ef6 fonctionne mais il a des problèmes de performances dans le Big Data.

Je pense que la meilleure façon est d'agir comme ceci:

DateTime ruleDate = Convert.ToDateTime(rule.data);

DateTime  startDate = SearchDate.Date;

DateTime  endDate = SearchDate.Date.AddDay(1);

return jobdescriptions.Where(j.Deadline >= startDate 
                       && j.Deadline < endDate );

c'est mieux que d'utiliser des parties de la date à. car la requête est exécutée plus rapidement dans les grandes données.

Mahdi Shahbazi
la source
+1 pour cette réponse. EntityFunctions.TruncateTime(remplacés ultérieurement par DbFunctions.TruncateTime) sont implémentés par conversion en SQL où la date / heure est convertie en chaîne et tronquée. Cela ralentit considérablement l'exécution de la requête, proportionnellement au nombre d'enregistrements traités.
urig
3

Besoin d'inclure using System.Data.Entity;. Fonctionne bien même avecProjectTo<>

var ruleDate = rule.data.Date;
return jobdescriptions.Where(j => DbFunctions.TruncateTime(j.Deadline) == ruleDate);
Omkar
la source
Comme déjà répondu ici
Gert Arnold
1

Cela signifie que LINQ to SQL ne sait pas comment transformer la Datepropriété en une expression SQL. C'est parce que la Datepropriété de la DateTimestructure n'a pas d'analogue dans SQL.

Adam Robinson
la source
1

Cela a fonctionné pour moi.

DateTime dt = DateTime.Now.Date;
var ord = db.Orders.Where
      (p => p.UserID == User && p.ValidityExpiry <= dt);

Source: Forums Asp.net

MRT2017
la source
0

J'ai le même problème mais je travaille avec des plages de date / heure. Ma solution est de manipuler l'heure de début (avec n'importe quelle date) à 00:00:00 et l'heure de fin à 23:59:59 Donc je ne dois plus convertir mon DateTime en Date, il reste plutôt DateTime.

Si vous n'avez qu'un seul DateTime, vous pouvez également définir l'heure de début (avec n'importe quelle date) sur 00:00:00 et l'heure de fin sur 23:59:59. Ensuite, vous effectuez une recherche comme s'il s'agissait d'une période.

var from = this.setStartTime(yourDateTime);
var to = this.setEndTime(yourDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);

Vous pouvez le faire aussi avec DateTime-Range:

var from = this.setStartTime(yourStartDateTime);
var to = this.setEndTime(yourEndDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);
peter70
la source
0

vous pouvez obtenir Enum comme:

DateTime todayDate = DateTime.Now.Date; var check = db.tableName.AsEnumerable().Select(x => new
        {
            Date = x.TodayDate.Date
        }).Where(x => x.Date == todayDate).FirstOrDefault();
Omid Soleiman
la source
C'est effectivement énumérer au préalable tout le contenu de la table afin d'appliquer le filtre de date ... semble être potentiellement une très mauvaise idée!
Javier Rapoport
0

Comme beaucoup l'ont souligné ici, l'utilisation de la fonction TruncateTime est lente.

L'option la plus simple si vous le pouvez est d'utiliser EF Core. Il peut le faire. Si vous ne pouvez pas, une meilleure alternative à la troncature est de ne pas changer du tout le champ interrogé, mais de modifier les limites. Si vous effectuez une requête de type «entre» normale où les limites inférieure et supérieure sont facultatives, ce qui suit fera l'affaire.

    public Expression<Func<PurchaseOrder, bool>> GetDateFilter(DateTime? StartDate, DateTime? EndDate)
    {
        var dtMinDate = (StartDate ?? SqlDateTime.MinValue.Value).Date;
        var dtMaxDate = (EndDate == null || EndDate.Value == SqlDateTime.MaxValue.Value) ? SqlDateTime.MaxValue.Value : EndDate.Value.Date.AddDays(1);
        return x => x.PoDate != null && x.PoDate.Value >= dtMinDate && x.PoDate.Value < dtMaxDate;
    }

Fondamentalement, plutôt que de rogner PoDate à la partie Date, nous incrémentons la limite supérieure de la requête et l'utilisateur <au lieu de <=

statler
la source