3

性別が一致するすべてのデフォルトの顧客アドレスを返そうとする方法があります。System.Func メソッドを where 句に渡すことで、フィルタリング クエリを少しずつ構築できるようにしたいと考えています。

    var emailAddresses = new List<string>();

        // get all customers.
        IQueryable<Customer> customersQ = base.GetAllQueryable(appContext).Where(o => o.Deleted == false);

        // for each customer filter, filter the query.

        var genders = new List<string>() { "C" };

        Func<Customer, bool> customerGender = (o => genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender));
        customersQ = customersQ.Where(customerGender).AsQueryable();


        emailAddresses = (from c in customersQ
                          select c.Email).Distinct().ToList();

        return emailAddresses;

しかし、このメソッドはすべてのアドレス (8000) 回に対してデータベースを呼び出すため、非常に低速です。

ただし、2行を置き換えると

    Func<Customer, bool> customerGender = (o => genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender));
        customersQ = customersQ.Where(customerGender).AsQueryable();

1行で

    customersQ = customersQ.Where(o => genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender)).AsQueryable();

次に、クエリはデータベースに対して 1 回の呼び出しを行うだけで、非常に高速です。

私の質問は、なぜこれが違いを生むのですか? データベースを 1 回呼び出すだけで最初のメソッドを機能させるにはどうすればよいですか?

4

1 に答える 1

10

Funcの代わりに式を使う:

Expression<Func<Customer, bool>> customerGender = (o => 
   genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender));
customersQ = customersQ.Where(customerGender).AsQueryable();

単純なFuncデリゲートを使用している場合は、Where拡張機能Enumerableが呼び出されます。したがって、すべてのデータがメモリに格納され、そこで列挙され、エンティティごとにラムダが実行されます。また、データベースへの呼び出しが多数あります。

一方、式を使用している場合は、Where拡張の ofQueryableが呼び出され、式が SQL クエリに変換されます。そのため、2 番目のケースでは単一のクエリが使用されます (インプレース ラムダを使用すると、式に変換されます)。

于 2013-03-05T13:39:50.667 に答える