5

プロパティ名の区切り文字列を渡すことができる IQueryable の拡張機能があります。これを使用すると、クエリが JOIN を構築せず、実質的に SELECT N+1 の問題が発生します。

私が気付いたのは、ネイティブ EF 拡張機能 .Include("property") を DbSet から直接呼び出すと、すべてが正常に機能することです。しかし、拡張機能を使用すると (単純化して .Include("property") SELECT N+1 を呼び出すだけにしました...

私の質問はなぜですか?私は何を間違っていますか?

メソッドの呼び出しは次のとおりです(サービスから)

public MyModel[] GetAll(int page, out int total, int pageSize, string sort, string filter)
{
    return _myModelRepository
        .Get(page, out total, pageSize, sort, filter, "PropertyOnMyModelToInclude")
        .ToArray();
}

拡張機能を使用したリポジトリ メソッドは次のとおりです。

public virtual IQueryable<T> Get(int page, out int total, int pageSize, string sort, string filter = null, string includes = null)
{
    IQueryable<T> query = DatabaseSet;
    if (!String.IsNullOrWhiteSpace(includes))
    {
        //query.IncludeMany(includes); // BAD: SELECT N+1
        //query.Include(includes); // BAD: SELECT N+1
    }
    if (!String.IsNullOrWhiteSpace(filter))
    {
        query.Where(filter);
    }
    total = query.Count(); // needed for pagination
    var order = String.IsNullOrWhiteSpace(sort) ? DefaultOrderBy : sort;
    var perPage = pageSize < 1 ? DefaultPageSize : pageSize;

    //return query.OrderBy(order).Paginate(page, total, perPage); // BAD: SELECT N+1 (in both variations above)
    //return query.IncludeMany(includes).OrderBy(order).Paginate(page, total, perPage); // BAD: SELECT N+1
    return query.Include(includes).OrderBy(order).Paginate(page, total, perPage);     // WORKS!
}

これが拡張機能です(問題を説明するために Include() を呼び出すためだけに縮小されています)

public static IQueryable<T> IncludeMany<T>(this IQueryable<T> query, string includes, char delimiter = ',') where T : class
{
    // OPTION 1
    //var propertiesToInclude = String.IsNullOrWhiteSpace(includes)
    //                              ? new string[0]
    //                              : includes.Split(new[] {delimiter}, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray();
    //foreach (var includeProperty in propertiesToInclude)
    //{
    //    query.Include(includeProperty);
    //}
    // OPTION 2
    //if (!String.IsNullOrWhiteSpace(includes))
    //{
    //    var propertiesToInclude = includes.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable(); //.Select(p => p.Trim());
    //    propertiesToInclude.Aggregate(query, (current, include) => current.Include(include));
    //}

    // OPTION 3 - for testing
    query.Include(includes);

    return query;
}
4

1 に答える 1

8

ここでの根本的な問題は、Include メソッドの使用方法と、ついでに Where メソッドの使用方法にあると思います。これらのメソッドは、LINQ 拡張メソッドでよくあることですが、呼び出されたオブジェクトを変更しません。代わりに、演算子が適用された後のクエリを表す新しいオブジェクトを返します。たとえば、次のコードでは次のようになります。

var query = SomeQuery();
query.Include(q => q.Bing);
return query;

Include によって返された新しいクエリは破棄されるため、Include メソッドは基本的に何もしません。一方、これは:

var query = SomeQuery();
query = query.Include(q => q.Bing);
return query;

Include をクエリに適用し、Include から返された新しいクエリ オブジェクトでクエリ変数を更新します。

投稿したコードにはありませんが、インクルードが無視され、関連するコレクションがまだ遅延読み込みを使用して読み込まれているため、コードで N+1 がまだ表示されていると思います。

于 2012-06-07T01:21:24.470 に答える