2010-08-13 15 views
1

Je suis assez nouveau à C#, donc je pourrais faire quelque chose de stupide, mais j'ai passé du temps à le regarder et je ne vois toujours pas quel est le problème.La dernière méthode() ne fonctionne pas sur l'objet IQueryable

Voici quelques extraits de code:

 double work = 0; 
     ProjectRepository pr = new ProjectRepository(); 
     IQueryable<CalendarDetail> cds; 

     // Find matching day of week 
     // Then for that day, cycle through all working times 

     // Return list of working times in day 
     cds = pr.GetCalDetails(calendarID, startTime.DayOfWeek.GetHashCode()); 

     foreach (CalendarDetail cd in cds) 
     { 
      DateTime wts = startTime.Date + cd.WorkingTimeStart.Value.TimeOfDay; 
      DateTime wtf = startTime.Date + cd.WorkingTimeFinish.Value.TimeOfDay; 

      //more code here.... 

      if ((cds.Last().CalendarDetailID == cd.CalendarDetailID) && (finishTime > wtf)) 
       work += Work(startTime.Date.AddDays(1), finishTime, calendarID); 
     } 

L'erreur est en cours d'exécution en temps jeté en raison de mon utilisation de l'appel de méthode cds.Last(). Cependant, cds a été déclaré et est utilisé comme un objet IQueryable, alors quel est le problème?

Texte d'erreur: L'opérateur de requête 'Last' n'est pas pris en charge. À défaut d'une solution, je suis sûr que je peux «logique» ma façon de contourner le problème, mais cela semblait élégant.

Merci,

Jonathan

+0

pouvez-vous publier votre exception? –

+0

Quelle erreur est générée? –

+2

Heh, je viens de réaliser que vous avez utilisé [wtf] (http: // thedailywtf.com /) en tant que nom de variable réel. –

Répondre

2

Je ne pense pas qu'il y ait suffisamment de détails dans cette question pour y répondre, parce que vous ne donnez pas ce que l'exception était en fait.

Il peut être utile de remplacer l'appel de .Last() par .AsEnumerable(). Last(), car cela indiquerait si le problème concernait Last() ou s'il se trouvait ailleurs dans la requête. Last() peut être plus lent et ne peut pas être plus rapide, donc ça ne vaut pas la peine de le faire en général si vous pouvez l'éviter, mais (A) peut-être que vous ne pouvez pas à cause de certaines limitations dans le fournisseur de requête et (B) le résultat de l'essayer et voir si vous obtenez la même exception vous en dira plus sur votre problème.

+0

Cela ressemble beaucoup à l'ajout de la méthode AsEnumerable() dans la chaîne fonctionne. Merci beaucoup! Si quelqu'un pouvait expliquer pourquoi, cela serait excellent. – JonRed

+0

Juste passé par le code et il fonctionne parfaitement. Merci encore! – JonRed

+0

'AsEnumerable'" termine "la requête LINQ to * whatever *. Le reste de la requête (par exemple, "Last") est évalué en utilisant LINQ to Objects. Le problème de performance indiqué par Jon est dû au fait que '.AsEnumerable(). Last()' lira l'intégralité du jeu de résultats (tous les objets 'CalendarDetails 'correspondants) en mémoire et l'itétera juste pour prendre le dernier. Il peut être plus efficace de créer une autre requête, en inversant l'ordre dans 'cds' et ensuite en appelant' First'; cela ne récupérerait qu'un seul enregistrement de la base de données. –

1

Il est difficile de deviner la cause avec en sachant l'exception que vous êtes jeté mais vous pouvez réellement faire tout cela dans LINQ donc je pensais que peut-être des informations précieuses pour vous

var lastID = cds.Last().CalendarDetailID; 
var work = (from cd in cds 
      let wts = startTime.Date + cd.WorkingTimeStart.Value.TimeOfDay 
      let wtf = startTime.Date + cd.WorkingTimeFinish.Value.TimeOfDay 
      where (lastID == cd.CalendarDetailID) && (finishTime > wtf) 
      select Work(startTime.Date.AddDays(1), finishTime, calendarID)).Sum(); 

(sur la supposition que le travail peut être implicitement converti en quelque chose que .Sum() peut gérer, c'est-à-dire que + = n'est pas un opérateur personnalisé)

+0

Merci pour ça. Le code manquant est logique quant à l'heure de début à utiliser, mais c'est un bon exemple de logique de requête linq. – JonRed

1

Vous devez nous dire ce que l'erreur/exception est pour aider correctement mais la cause la plus commune pour Last() échouer, c'est que le fournisseur IQeryable ne le supporte pas. par exemple. Je ne pense pas que LinqToSQL supporte Last()

+0

Il semble que Linq ne le supporte pas, et c'est le problème. L'ajout de AsEnumerable m'a fait dépasser ce point dans le code; Je dois juste m'assurer qu'il fait la bonne chose. – JonRed

+0

Sachez qu'appeler AsEnumerable() exécute l'arborescence expresion. Si cela obtient des données d'une base de données avec 1 million de lignes, il récupère toutes les 1 million de lignes, puis rejette tout sauf le dernier. –

+0

Merci pour les heads up. Le nombre maximum de lignes que j'attends est d'environ quatre, normalement deux. – JonRed

2

Il semble que Linq ne supporte pas Last() dans IQueryable. Vous pouvez le faire:

List<CalendarDetail> cds; 

    // Find matching day of week 
    // Then for that day, cycle through all working times 

    // Return list of working times in day 
    cds = pr.GetCalDetails(calendarID, startTime.DayOfWeek.GetHashCode()).ToList(); 
3

La réponse est que tous les fournisseurs IQueryable ne prennent pas en charge toutes les méthodes Linq. Les fournisseurs IQeuryable avec un backend SQL, tel que LinqToSql ou Entity Framework ne supportent pas Last() car SQL lui-même n'a pas de concept de Last. Il n'y a pas d'instruction SQL pour Last() à traduire.

Dans SQL, l'approche correcte consiste à commander les enregistrements de façon appropriée (par Id Desc, peut-être) puis faire Top 1. Voilà ce que nous devons faire dans LINQ:

CalendarDetail lastCd = cds.OrderByDescending(cd => cd.CalendarDetailId).First(); 
0

si votre CalendarDetailID est en cours d'exécution numérique, vous pouvez utiliser Max()