我在IQueryable上有一个扩展,它允许传入带分隔的属性名称字符串,当使用时会导致查询不构造JOIN,并有效地导致SELECT N + 1问题。
我注意到的是,如果我调用本机EF扩展。包含("属性")直接从 DbSet 一切正常。但是如果我使用我的扩展(我什至将其简化为仅调用.包含("属性")选择 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;
}
我认为这里的根本问题在于您使用 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,因为 Include 被忽略了,因此相关集合仍然使用延迟加载加载。