1

在我的C# 类库项目中,我有一个需要计算一些统计数据的方法GetFaultRate,给定 a date,计算有故障的产品数量超过生产的产品数量。

float GetFaultRate(DateTime date)
{
    var products = GetProducts(date);
    var faultyProducts = GetFaultyProducts(date);

    var rate = (float) (faultyProducts.Count() / products.Count());

    return rate;
}

两种方法,GetProductsGetFaultyProductsRepository类中获取数据_productRepository

IEnumerable<Product> GetProducts(DateTime date)
{
    var products = _productRepository.GetAll().ToList();

    var periodProducts = products.Where(p => CustomFunction(p.productionDate) == date);

    return periodProducts;
}

IEnumerable<Product> GetFaultyProducts(DateTime date)
{
    var products = _productRepository.GetAll().ToList();

    var periodFaultyProducts = products.Where(p => CustomFunction(p.ProductionDate) == date && p.Faulty == true);

    return periodFaultyProducts;
}

哪里GetAll有签名:

IQueryable<Product> GetAll();

数据库中的产品很多,检索和转换需要很长时间ToList()。我需要枚举该集合,因为任何自定义函数(例如CustomFunction、 )都无法在IQueryable<T>.

我的应用程序在获得故障率之前卡了很长时间。我想这是因为要检索的对象数量很大。我确实可以删除这两个函数GetProductsGetFaultyProducts实现里面的逻辑GetFaultRate。但是,由于我还有其他使用GetProductsand的函数,因此使用GetFaultyProducts后一种解决方案,我只能访问数据库,但有很多重复的代码。

什么是好的妥协?

4

3 回答 3

4

首先,不要将其转换IQueryable为列表。它强制将整个数据集一次全部放入内存,而不是直接调用Where查询,这将允许您在数据进入时对其进行过滤。这将大大减少您的内存占用,并且(非常)略微增加运行速度。如果您需要将 an 转换IQueryable为 anIEnumerable以便Where数据库不执行,只需使用AsEnumerable.

接下来,尽可能避免获取所有数据,尤其是多次。您需要向我们展示您的日期函数的作用,但它可能是可以在数据库上完成的。您可以在数据库中执行的任何过滤都将大大提高性能。

接下来,您在这里真的不需要两个查询。第二个查询只是第一个查询的子集,所以如果您知道您将始终使用两个查询,那么您应该只执行第一个查询,将结果放入内存(即使用ToList您存储的 a)然后使用aWhere以进一步过滤结果。这将避免另一个数据库旅行以及所有数据处理/过滤。

如果您并不总是同时使用这两个查询,但有时只使用其中一个,那么您可以通过Faulty在获取所有项目之前过滤掉来改进第二个查询。在调用Where(p => p.Faulty) 之前添加并在调用之后AsEnumerable过滤日期信息(如果您无法将任何日期过滤转换为可以在数据库中完成的过滤)。AsEnumerable

看来,最终您只需要计算有缺陷的项目与总数相比的比率。这可以通过一个查询轻松完成,而不是两个。

您已经说过Count在您的代码中运行非常缓慢,但这不是真的。 Count只是实际枚举查询的方法,而所有其他方法只是构建查询,而不是执行它。但是,您可以通过完全组合查询来大幅降低性能成本。

var lookup = _productRepository.GetAll()
.AsEnumerable()//if at all possible, try to re-write the `Where` 
               //to be a valid SQL query so that you don't need this call here
.Where(p => CustomFunction(p.productionDate) == date)
.ToLookup(product => product.Faulty);

int totalCount = lookup[true].Count() + lookup[false].Count();
double rate = lookup[true].Count() / (double) totalCount;
于 2012-10-10T14:08:42.153 回答
0
var products = GetProducts(date);
var periodFaultyProducts = (from p in products.AsParallel()
                            where p.Faulty == true
                            select p).AsEnumerable();
于 2012-10-10T14:10:17.240 回答
0

您需要减少数据库请求的数量。ToList, First, FirstOrDefault, Any,TakeCount强制您的查询在数据库中运行。正如 Servy 指出的那样,AsEnumerable将您的查询从 转换IQueryableIEnumerable. 如果您必须找到子集,您可以使用Where.

于 2012-10-10T16:03:00.270 回答