我有一个看起来像这样的表:
FruitID | FruitType
23 | 2
215 | 2
256 | 1
643 | 3
我希望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();
此代码有效,但问题是有时我会收到超时错误,因为查询运行时间过长。如何更改此代码以避免超时问题?
答案 0 :(得分: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);
这将有效地创建以下字典(假设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
};
您的查询中还有另一件事可能导致性能问题,可能导致超时:where
部分中的水果ID列表包含在查询中,如果该列表非常大它可能会减慢您的查询速度。除非您从先前的查询到数据库创建此列表,否则您无能为力。在这种情况下,您应该尽量避免将水果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; }
}
基于元组序列具有属性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;
}
}
创建动态对象的扩展方法:
static class CountExtensions {
public static dynamic ToCounts(this IEnumerable<TypeCount> typeCounts) {
return new CountsDictionary(typeCounts);
}
}
全部放在一起:
var counts = MyDC
.Fruits
.Where(f => TheFruitIDs.Contains(f.FruitID))
.GroupBy(
f => f.FruitType,
(fruitType, fruits) => new TypeCount(fruitType, fruits.Count())
)
.ToCounts();
然后,您可以检索属性counts.CountType1
,counts.CountType2
和counts.CountType3
。其他count.CountType#
属性将返回0.但是,由于counts
是动态的,因此您无法获得任何智能感知。
答案 1 :(得分:1)
你的LINQ为每个计数生成saperate sql,所以你需要使用TheFruits计算你的项目
试试这个
var TheCounter = (from f in MyDC.Fruits
where TheFruitIDs.Contains(f.FruitID)
group new {f.FruitType} by f.FruitType into TheFruits
select new MyCounterMode()
{
CountType1 = TheFruits.Count(f => f.FruitType == 1),
CountType2 = TheFruits.Count(f => f.FruitType == 2),
.... all the way to CountType6
}).Single();
答案 2 :(得分:1)
您可以在内存中执行group by
。将group by与multuple计数组合将生成大量子查询,这些子查询的执行情况非常糟糕。
var tempResult = (from f in MyDC.Fruits where TheFruitIDs.Contains(f.FruitID)).ToList();
var TheCounter = (from f in tempResult
group f by f.FruitType into TheFruits
select new MyCounterMode()
{
CountType1 = (int?) TheFruits.Count(f => f.FruitType == 1),
CountType2 = (int?) TheFruits.Count(f => f.FruitType == 2),
.... all the way to CountType6
}).Single();
答案 3 :(得分:0)
您需要记住,每次迭代都会执行select
!!
所以尝试类似:
'var TheCounter = (from f in MyDC.Fruits
group f by f.FruitID into TheFruits
select new KeyValuePair<int, int>(TheFruits.Key,TheFruits.Count())).ToDictionary(r=>r.Key,r=>r.Value);'
这将为您提供一个字典:Key-FruitId,Value-Count
答案 4 :(得分:0)
以下是我总是如何实现这一点(我构建了一个简单的控制台程序来演示):
<强> Fruit.cs 强>
public class Fruit
{
public Fruit(int fruitId, int fruitType)
{
FruitId = fruitId;
FruitType = fruitType;
}
public int FruitId { get; set; }
public int FruitType { get; set; }
}
<强> Program.cs的强>
class Program
{
static void Main(string[] args)
{
// Data
var fruits = new List<Fruit>
{
new Fruit(23, 2),
new Fruit(215, 2),
new Fruit(256, 1),
new Fruit(643, 3)
};
// Query
var query = fruits
.GroupBy(x => x.FruitType)
.Select(x => new {Name = x.Key, Total = x.Count()});
// Output
foreach (var item in query)
{
Console.WriteLine(item.Name + ": " + item.Total);
}
Console.ReadLine();
}
}
您需要关注的是query
。使用GroupBy
后,您将获得一个组列表。对于每个组,Key
是要分组的条件(此处为FruitType
)。然后,我们调用Count()
来获取该组中元素的数量。
答案 5 :(得分:0)
这是一种动态的方式,你不受CountType#的限制:
int typesOfCounts = 6;
IEnumerable<Fruit> theCounter = fruitList.Where(x => theFruitIDs.Contains(x.FruitID));
Dictionary<string, int> myCounterMode = new Dictionary<string, int>();
for (var i = 1; i < typesOfCounts + 1; i++)
{
string counterType = "CountTypeX";
counterType = counterType.Replace("X", i.ToString());
myCounterMode.Add(counterType, theCounter.Count(x => x.FruitType == i));
}
return myCounterMode;