NHibernate & LINQ: Using Fetch() with custom ToPagedList method
I have a problem with using the nHibernate Fetch() (or FetchMany()) method with my paging method which uses Futures to get the information it needs. And I'm not sure how to fix it but I know exactly what it's doing. Let me explain what I have so far.
I'm using an extension method found here to let me get a row count as a Future value. it looks like this.
public static IFutureValue<TResult> ToFutureValue<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<IQueryable<TSource>, TResult>> selector) where TResult : struct
{
var provider = (NhQueryProvider)source.Provider;
var method = ((MethodCallExpression)selector.Body).Method;
var expression = Expression.Call(null, method, source.Expression);
return (IFutureValue<TResult>)provider.ExecuteFuture(expression);
}
It works great, using this I can then combine getting a row count and getting my result set into a signle paging method that only generates a single database hit. My Paging method looks like this.
public static IList<T> ToPagedList<T>(this IQueryable<T> query, int pageIndex, int pageSize, out int count)
{
var futureCount = query.ToFutureValue(x => x.Count());
var futureResults = pageIndex == 0 && pageSize == 0 ? query.ToFuture() : query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToFuture();
count = futureCount.Value;
return futureResults.ToList();
}
With this in place I can ma开发者_运维技巧ke queries like the following.
repositoryInstance.Query().Where(x=>x.SomeTextField.Contains("lake")).ToPagedList(pageIndex, pageSize, out count);
This much works like a charm. However now I need to be able to define certain relationships as eager loaded, in this case it might look like this.
repositoryInstance.Query().Where(x=>x.SomeTextField.Contains("lake")).FetchMany(x=>x.Details).ToPagedList(pageIndex, pageSize, out count);
This blows up with a QueryException with the description "Query specified join fetching, but the owner of the fetched association was not present in the select list". The reason for this is because of my paging method, it's trying to apply an aggregate row count on the query which has the Fetch() on it, and you can't do that, because the error is right. The owner of the fetched association wasn't in the select list (at least it isn't in one of them), my select is essentially for the first query is for a single integer. I thought maybe I could rip out the Expression nodes once in the ToPagedList that are for any fetching but I have no idea if it's possible or how ugly it would be. I'm looking for some alternatives that might let be get the behavior I want while bypassing the limitation on how Fetching works. Any thoughts?
You could modify ToPagedList as follows:
public static IList<T> ToPagedList<T>(this IQueryable<T> query, int pageIndex,
int pageSize, out int count,
Func<IQueryable<T>, IQueryable<T>> filterResult)
{
var futureCount = query.ToFutureValue(x => x.Count());
//change only the results query, not the count one
if (customAction != null)
query = filterResult(query);
var futureResults = pageIndex == 0 && pageSize == 0
? query.ToFuture()
: query.Skip((pageIndex - 1) * pageSize)
.Take(pageSize).ToFuture();
count = futureCount.Value;
return futureResults.ToList();
}
And use it like this:
repositoryInstance.Query().Where(x => x.SomeTextField.Contains("lake"))
.ToPagedList(pageIndex, pageSize, out count,
q => q.FetchMany(x => x.Details));
I know it's slightly less elegant, but it will work, and it's way easier than modifying an existing expression.
精彩评论