Grouper par semaines dans LINQ aux entités

c# entity-framework linq linq-to-entities

Question

J'ai une application qui permet aux utilisateurs de saisir le temps qu'ils consacrent au travail et j'essaie de créer de bons rapports conçus pour cela, qui exploitent LINQ to Entities. Parce que chaque TrackedTime a une TargetDate qui n'est que la partie "Date" d'un DateTime , il est relativement simple de regrouper les heures par utilisateur et par date (je laisse de côté les clauses "Where" pour des raisons de simplicité):

var userTimes = from t in context.TrackedTimes
                group t by new {t.User.UserName, t.TargetDate} into ut
                select new
                {
                    UserName = ut.Key.UserName,
                    TargetDate = ut.Key.TargetDate,
                    Minutes = ut.Sum(t => t.Minutes)
                };

Grâce à la propriété DateTime.Month , le regroupement par utilisateur et par mois n’est que légèrement plus compliqué:

var userTimes = from t in context.TrackedTimes
                group t by new {t.User.UserName, t.TargetDate.Month} into ut
                select new
                {
                    UserName = ut.Key.UserName,
                    MonthNumber = ut.Key.Month,
                    Minutes = ut.Sum(t => t.Minutes)
                };

Maintenant vient la partie la plus délicate. Existe-t-il un moyen fiable de regrouper par semaine? J'ai essayé ce qui suit sur la base de cette réponse à une question LINQ to SQL similaire:

DateTime firstDay = GetFirstDayOfFirstWeekOfYear();
var userTimes = from t in context.TrackedTimes
                group t by new {t.User.UserName, WeekNumber = (t.TargetDate - firstDay).Days / 7} into ut
                select new
                {
                    UserName = ut.Key.UserName,
                    WeekNumber = ut.Key.WeekNumber,
                    Minutes = ut.Sum(t => t.Minutes)
                };

Mais LINQ to Entities ne semble pas prendre en charge les opérations arithmétiques sur les objets DateTime, il ne sait donc pas comment procéder (t.TargetDate - firstDay).Days / 7 .

J'ai envisagé de créer une vue dans la base de données qui mappe simplement des jours ou des semaines, puis de l'ajouter à mon contexte Entity Framework et de la joindre à ma requête LINQ, mais cela semble demander beaucoup de travail. Existe-t-il une bonne solution pour l’approche arithmétique? Quelque chose qui fonctionne avec Linq to Entities, que je peux simplement incorporer dans l'instruction LINQ sans avoir à toucher à la base de données? Une façon de dire à Linq aux entités comment soustraire une date à une autre?

Résumé

Je voudrais remercier tout le monde pour leurs réponses réfléchies et créatives. Après tout ce va-et-vient, il semble que la vraie réponse à cette question est "attend jusqu'à ce que .NET 4.0." Je vais donner la prime à Noldorin pour la réponse la plus pratique qui exploite toujours LINQ, avec une mention spéciale à Jacob Proffitt pour avoir fourni une réponse qui utilise Entity Framework sans qu'il soit nécessaire de modifier la base de données. Il y avait aussi d'autres bonnes réponses, et si vous examinez la question pour la première fois, je vous recommande fortement de lire toutes les réponses qui ont été votées, ainsi que leurs commentaires. Cela a vraiment été un superbe exemple de la puissance de StackOverflow. Merci à tous!

Réponse acceptée

Vous devriez pouvoir forcer la requête à utiliser LINQ to Objects plutôt que LINQ to Entities pour le regroupement, à l'aide d'un appel à la méthode d'extension AsEnumerable .

Essayez ce qui suit:

DateTime firstDay = GetFirstDayOfFirstWeekOfYear();
var userTimes = 
    from t in context.TrackedTimes.Where(myPredicateHere).AsEnumerable()
    group t by new {t.User.UserName, WeekNumber = (t.TargetDate - firstDay).Days / 7} into ut
    select new
    {
        UserName = ut.Key.UserName,
        WeekNumber = ut.Key.WeekNumber,
        Minutes = ut.Sum(t => t.Minutes)
    };

Cela signifierait au moins que la clause where est exécutée par LINQ to Entities, mais la clause group , qui est trop complexe pour être gérée par les entités, est exécutée par LINQ to Objects.

Faites-moi savoir si vous avez de la chance avec ça.

Mettre à jour

Voici une autre suggestion, qui pourrait vous permettre d’utiliser LINQ to Entities pour l’ensemble.

(t.TargetDate.Days - firstDay.Days) / 7

Cela élargit simplement l'opération de sorte que seule la soustraction d'entiers soit effectuée plutôt que la soustraction DateTime .

Il est actuellement non testé, donc cela peut ou peut ne pas fonctionner ...


Réponse populaire

Vous pouvez également utiliser des fonctions SQL dans le LINQ tant que vous utilisez SQLServer:

using System.Data.Objects.SqlClient;

var codes = (from c in _twsEntities.PromoHistory
                         where (c.UserId == userId)
                         group c by SqlFunctions.DatePart("wk", c.DateAdded) into g
                         let MaxDate = g.Max(c => SqlFunctions.DatePart("wk",c.DateAdded))
                         let Count = g.Count()
                         orderby MaxDate
                         select new { g.Key, MaxDate, Count }).ToList();

Cet échantillon a été adapté du MVP Zeeshan Hirani dans ce fil de discussion: un fichier MSDN



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi