Linq to SQL计算生成超时的分组元素

fre*_*hie 14 .net c# linq linq-to-sql

我有一个看起来像这样的表:

FruitID | FruitType
  23    |    2
  215   |    2
  256   |    1
  643   |    3
Run Code Online (Sandbox Code Playgroud)

我希望通过FruitType给出一个FruitIDs被调用的列表来获得计数TheFruitIDs.这就是我所拥有的:

var TheCounter = (from f in MyDC.Fruits    
                  where TheFruitIDs.Contains(f.FruitID) 
                  group f by 0 into TheFruits
                  select new MyCounterMode()
                  {
                     CountType1 = (int?) TheFruits.Where(f => f.FruitType == 1).Count() ?? 0,
                     CountType2 = (int?) TheFruits.Where(f => f.FruitType == 2).Count() ?? 0,
                     .... all the way to CountType6      
                  }).Single();
Run Code Online (Sandbox Code Playgroud)

此代码有效,但问题是有时我会收到超时错误,因为查询运行时间过长.如何更改此代码以避免超时问题?

Mar*_*age 6

查询的最简单方法是分组FruitType,然后计算行数:

var countsDictionary = MyDC
  .Fruits
  .Where(f => TheFruitIDs.Contains(f.FruitID))
  .GroupBy(
    f => f.FruitType,
    (fruitType, fruits) => new { FruitType = fruitType, Count = fruits.Count() }
  )
  .ToDictionary(c => c.FruitType, c => c.Count);
Run Code Online (Sandbox Code Playgroud)

这将有效地创建以下字典(假设部件不排除任何数据where):

FruitType | Count
----------+------
1         | 1
2         | 2
3         | 1

如果您真的想将其折叠为具有特定水果类型计数的单个对象,则必须创建此对象:

var TheCounter = new {
  CountType1 = countsDictionary.ContainsKey(1) ? countsDictionary[1] : 0,
  CountType2 = countsDictionary.ContainsKey(2) ? countsDictionary[2] : 0,
  CountType3 = countsDictionary.ContainsKey(3) ? countsDictionary[3] : 0
};
Run Code Online (Sandbox Code Playgroud)

您的查询中还有另一件事可能导致性能问题,可能导致超时:部件中的水果ID列表where包含在查询中,如果该列表非常大,则可能会降低查询速度.除非您从先前的查询到数据库创建此列表,否则您无能为力.在这种情况下,您应该尽量避免将水果ID列表拉到客户端.相反,您应该将选择ID的查询与计算类型的查询相结合.这将确保整个查询在服务器端执行.

您似乎关心代码的结构变化.只要您创建匿名对象,就很难编写可重用的代码.您可以考虑使用带有计数或类似内容的字典.另一种选择是使用计数创建动态对象.就个人而言,我不喜欢这个解决方案,但你可能会发现它很有用.

要简化代码,需要一个存储计数的类:

class TypeCount {

  public TypeCount(Int32 type, Int32 count) {
    Type = type;
    Count = count;
  }

  public Int32 Type { get; private set; }

  public Int32 Count { get; private set; }

}
Run Code Online (Sandbox Code Playgroud)

具有属性的动态对象CountType0,CountType1,CountType2基于元组序列等:

class CountsDictionary : DynamicObject {

  readonly IDictionary<Int32, Int32> counts;

  public CountsDictionary(IEnumerable<TypeCount> typeCounts) {
    if (typeCounts== null)
      throw new ArgumentNullException("typeCounts");
    this.counts = typeCounts.ToDictionary(c => c.Type, c => c.Count);
  }

  public override Boolean TryGetMember(GetMemberBinder binder, out Object result) {
    Int32 value;
    if (binder.Name.StartsWith("CountType") && Int32.TryParse(binder.Name.Substring(9), NumberStyles.None, CultureInfo.InvariantCulture, out value) && value >= 0) {
      result = this.counts.ContainsKey(value) ? this.counts[value] : 0;
      return true;
    }
    result = 0;
    return false;
  }

}
Run Code Online (Sandbox Code Playgroud)

一种创建动态对象的扩展方法:

static class CountExtensions {

  public static dynamic ToCounts(this IEnumerable<TypeCount> typeCounts) {
    return new CountsDictionary(typeCounts);
  }

}
Run Code Online (Sandbox Code Playgroud)

把它们放在一起:

var counts = MyDC
  .Fruits
  .Where(f => TheFruitIDs.Contains(f.FruitID))
  .GroupBy(
    f => f.FruitType,
    (fruitType, fruits) => new TypeCount(fruitType, fruits.Count())
  )
  .ToCounts();
Run Code Online (Sandbox Code Playgroud)

然后counts.CountType1,您可以检索属性,counts.CountType2counts.CountType3.其他count.CountType#属性将返回0.但是,由于counts动态,您将无法获得任何智能感知.