Question

Je suis en train de convertir certaines requêtes SQL en Linq pour éviter plusieurs voyages à la base de données.

L'ancien SQL que je suis en train de convertir fait:

SELECT
    AVG(CAST(DATEDIFF(ms, A.CreatedDate, B.CompletedDate) AS decimal(15,4))),
    AVG(CAST(DATEDIFF(ms, B.CreatedDate, B.CompletedDate) AS decimal(15,4)))
FROM
    dbo.A
INNER JOIN
    dbo.B ON B.ParentId = A.Id

J'ai donc créé deux classes C #:

class B
{
    public Guid Id { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime CompletedDate { get; set; }
}

class A
{
    public Guid Id { get; set; }
    public DateTime CreatedDate { get; set; }
    public List<B> BList { get; set; }
}

Et j'ai un objet que je veux List<A> interroger. Il est peuplé de la base de données, chaque A dans la liste a une sous-liste avec une charge d'hôtes. Je veux utiliser des objets LINQ-to-interroger cette liste.

Je dois donc utiliser LINQ pour obtenir le temps moyen entre un début de A et l'achèvement de son enfant B, et le temps moyen entre le début et la fin de chaque B. Je n'ai pas écrit le SQL d'origine donc je ne suis pas tout à fait sûr qu'il fait ce qu'il est censé!

J'ai plusieurs de ces moyennes pour calculer, donc je voudrais les faire tout à l'intérieur d'une requête Linq magique. Est-ce possible?

Était-ce utile?

La solution

La question la plus intéressante serait de savoir comment faire une telle chose sur le serveur, par exemple pour la requête suivante pour traduire LINQ to SQL.

        var q = from single in Enumerable.Range(1, 1)
                let xs = sourceSequence
                select new
                {
                    Aggregate1 = xs.Sum(),
                    Aggregate2 = xs.Average(),
                    // etc
                };

Mais il n'y a pas d'essayer de le caser dans une requête si vous utilisez LINQ aux objets. Il suffit d'écrire les agrégats séparément:

var aggregate1 = xs.Sum();
var aggregate2 = xs.Average();
// etc

En d'autres termes:

var xs = from a in listOfAs
         from b in a.Bs
         select new { A=a, B=b };

var average1 = xs.Average(x => (x.B.Completed - x.A.Created).TotalSeconds);
var average2 = xs.Average(x => (x.B.Completed - x.B.Created).TotalSeconds);

Ai-je comprends bien?

Edit: David B ont répondu de même. J'ai oublié de convertir le TimeSpan à un type numérique simple supporté par la méthode de la moyenne. Utilisez TotalMilliseconds / secondes / minutes ou quelle que soit l'échelle est appropriée.

Autres conseils

Sql et LINQ diffèrent sur certains points. Sql a groupement implicite - prendre toutes ces choses et faire un groupe. Dans LINQ, on peut faux regroupement implicite en introduisant une constante groupe sur.

var query = 
  from i in Enumerable.Repeat(0, 1)
  from a in AList
  from b in a.BList
  select new {i, a, b} into x
  group x by x.i into g
  select new
  {
    Avg1 = g.Average(x => (x.b.CompletedDate - x.a.CreatedDate)
       .TotalMilliseconds ),
    Avg2 = g.Average(x => (x.b.CompletedDate - x.b.CreatedDate)
       .TotalMilliseconds )
  };

permet de démarrer en obtenant le temps moyen entre le début de B et la finition:

1) Vous devez d'abord la différence de temps entre le début et la fin de chaque objet B de sorte que vous feriez ceci:

List<TimeSpan> timeSpans = new List<TimeSpan>();
foreach (B myB in BList)
{
  TimeSpan myTimeSpan = myB.CompletedDate.Subtract(myB.CreatedDate);
  timeSpans.Add(myTimeSpan);
}

2) Vous devez maintenant obtenir la moyenne de cela. Vous pouvez le faire en utilisant des expressions lambda:

//This will give you the average time it took to complete a task in minutes
Double averageTimeOfB = timeSpans.average(timeSpan => timeSpan.TotalMinutes);

Vous pouvez maintenant faire la même chose pour obtenir le temps moyen entre le début de A et la finition de B comme suit:

1) Obtenez la différence de temps pour la date de début de A et chacun date d'achèvement de B:

 List<TimeSpan> timeSpans_2 = new List<TimeSpan>();
 foreach (B myB in BList)
 {
    TimeSpan myTimeSpan = myB.CompletedDate.Subtract(objectA.CreatedDate);
    timeSpans_2.Add(myTimeSpan);
 }

2) Obtenez la moyenne de ces differnces temps exactement comme celui ci-dessus:

//This will give you the average time it took to complete a task in minutes
Double averageTimeOfAandB = timeSpans_2.average(timeSpan => timeSpan.TotalMinutes);

Et vous avez terminé. J'espère que ca aide. S'il vous plaît noter que ce code n'a pas été testé.

EDIT: Souvenez-vous que LINQ utilise des expressions Lamda, mais est plus de sucre syntaxique (ne sais pas beaucoup sur la mise en œuvre et j'espère que vous ne le tenez pas contre moi). Et vous pouvez aussi envelopper les implementaions que je propose des méthodes pour que vous puissiez les appeler plusieurs fois pour une liste de objets.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top