1

私のC# クラス ライブラリ プロジェクトには、いくつかの統計を計算する必要があるメソッドがありますGetFaultRate。これは、 が与えられたdate場合、生産された製品の数に対して障害のある製品の数を計算します。

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

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

    return rate;
}

どちらのメソッドも、リポジトリクラスからデータを取得しGetProductsます。 GetFaultyProducts_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>ます。

障害率を取得する前に、アプリケーションが長時間停止します。取得するオブジェクトの数が多いためだと思います。実際に 2 つの関数GetProductsを削除して、GetFaultyProducts内部にロジックを実装できGetFaultRateます。ただし、GetProductsandを使用する他の関数があるためGetFaultyProducts、後者のソリューションでは、データベースへのアクセスは 1 回だけですが、多くの重複コードがあります。

良い妥協点は何ですか?

4

3 に答える 3

4

まず、をリストに変換しないでくださいIQueryable。クエリを直接呼び出すだけでなく、データセット全体を一度にメモリに取り込むように強制しWhereます。これにより、データが入ってくるときにデータをフィルタリングできます。これにより、メモリフットプリントが大幅に減少し、(非常に)わずかに増加します。実行時の速度。をデータベースで実行されないように変換IQueryableする必要がある場合は、を使用してください。IEnumerableWhereAsEnumerable

次に、すべてのデータを取得することは、特に複数回、可能な限り避ける必要があります。日付関数が何をするかを示す必要がありますが、それはデータベースで実行できるものである可能性があります。データベースで実行できるフィルタリングは、パフォーマンスを大幅に向上させます。

次に、ここでは2つのクエリは必要ありません。2番目のクエリは最初のクエリのサブセットにすぎないため、常に両方のクエリを使用することがわかっToListている場合は、最初のクエリを実行し、結果をメモリに(つまり、保存したものを使用して)取得してから使用する必要があります。そのWhere上で、結果をさらにフィルタリングします。これにより、別のデータベーストリップとすべてのデータ処理/フィルタリングを回避できます。

Faulty常に両方のクエリを使用するわけではなく、どちらか一方だけを使用する場合もある場合は、すべてのアイテムを取得する前に除外することで、2番目のクエリを改善できます。呼び出すWhere(p => p.Faulty) 前に追加し、呼び出した後AsEnumerableに日付情報をフィルタリングします(これは、日付フィルタリングをデータベースで実行できるフィルタリングに変換できない場合です)。AsEnumerable

最終的には、合計と比較した場合の障害のあるアイテムの比率を計算するだけでよいようです。これは、2つではなく1つのクエリで簡単に実行できます。

あなたはそれ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

データベースリクエストの数を減らす必要があります。、、、、、およびクエリをデータベースToListで実行するようFirstに強制します。Servyが指摘したように、クエリをからに変換します。サブセットを見つける必要がある場合は、を使用できます。FirstOrDefaultAnyTakeCountAsEnumerableIQueryableIEnumerableWhere

于 2012-10-10T16:03:00.270 に答える
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 に答える