5

我正在尝试运行最简单的查询:

string queryStr = "select b " +
                  "from Blog b " +
                  "left outer join fetch b.BlogComments bc";

IList<Blog> blogs = Session.CreateQuery(queryStr)
    .SetMaxResults(10)
    .List<Blog>();

但它会引发以下错误:

System.ArgumentNullException: Value cannot be null.
Parameter name: source

但是,如果我从 HQL 中删除“获取”,它就可以正常工作。此外,如果我保留 fetch 但删除 SetMaxResults 它也可以正常工作。这与 fetch + SetMaxResults 组合有关。

我正在尝试加载子集合以优化查询并防止 SELECT N+1 问题。我将 NHibernate 3.3.1.4000 与 MySQL 数据库一起使用。


我的映射:

public class BlogMap : ClassMapping<Blog>
{
    public BlogMap ()
    {
        // other properties (snip)....

        Set(x => x.BlogComments, x =>
        {
            x.Inverse(true);
            x.Cascade(Cascade.All | Cascade.DeleteOrphans);
            x.Lazy(CollectionLazy.Extra);
            x.Key(k => { k.Column("BlogId"); });
        }, x => x.OneToMany());
    }
}


public class BlogCommentMap : ClassMapping<BlogComment>
{
    public BlogCommentMap ()
    {
        // other properties (snip)....

        ManyToOne(x => x.Blog, x =>
        {
            x.Column("BlogId");
            x.NotNullable(true);
        });
    }
}

根据要求进行堆栈跟踪:

[ArgumentNullException: Value cannot be null.
Parameter name: source]
   System.Linq.Enumerable.ToList(IEnumerable`1 source) +4206743
   NHibernate.Engine.QueryParameters.CreateCopyUsing(RowSelection selection) +178
   NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.List(ISessionImplementor session, QueryParameters queryParameters) +210
   NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results) +369
   NHibernate.Impl.SessionImpl.List(String query, QueryParameters queryParameters, IList results) +301

[GenericADOException: Could not execute query[SQL: SQL not available]]
   NHibernate.Impl.SessionImpl.List(String query, QueryParameters queryParameters, IList results) +351
   NHibernate.Impl.SessionImpl.List(String query, QueryParameters parameters) +282
   NHibernate.Impl.QueryImpl.List() +162
   WebApp.Repositories.BlogRepository.SearchBlogs(SearchBlogs search) in C:\...path...\BlogRepository.cs:43
   WebApp.Controllers.HomepageController.Index(Int32 page) in C:\...path...\Controllers\HomepageController.cs:54
   lambda_method(Closure , ControllerBase , Object[] ) +101
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +208
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
   System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
   System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8970061
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
4

2 回答 2

3

您遇到的是一个错误......但在这种情况下,这可能是一个好兆头!;) 因为在获取父对象及其集合的查询上的分页 ( SetFirstResult(), ) 将返回意外结果。SetMaxResults()比方说,

数据库表包含以下内容:

  • 博客A
    • 博客评论A1
    • 博客评论A2
    • 博客评论A3
  • 博客B
    • 博客评论B1
    • 博客评论B2

QueryOver语法(有效)与上述 HQL 相同:

var blogs = session.QueryOver<Blog>()
  .Fetch(c => c.BlogComment).Eager // collection in one SQL
  .Skip(0).Take(2) // paging, but weird...
  .List<Blog>();

.Skip(0).Take(2)- 第一页,选择导致这两行但一个BlogA

| 博客A | 博客评论A1
| 博客A | 博客评论A2

.Skip(2).Take(2)- 下一页,奇怪...再次BlogA

| 博客A | 博客评论A3
| 博客B | 博客评论B1

这很可能不是我们想要的。

建议:选择 1+1

在这种情况下,最可靠的是只对父对象 ( Blog) 进行分页。然后,当我们在 NHibernate 会话中拥有所有博客 ( .Skip(2).Take(2)) 时,我们只会为他们的所有孩子 ( BlogComments) 调用一次 select。

最简单的方法是设置batch-size="x",其中x是一个接近通常页面大小的数字(例如 25 或 50)。

.BatchSize(25)

NHibernate 文档19.1.5。使用批量获取解释细节

于 2012-12-15T17:09:16.123 回答
2

这看起来像是 QueryParameters.CreateCopyUsing() 中的错误,或者其他东西没有正确创建原始 QueryParameters。无论如何,将 SetMaxResults() 与集合连接一起使用将强制在客户端应用分页,而不是在数据库中,这可能不是您想要的。

您可能希望重写它以在产生 10 个博客 ID 并在外部查询中获取连接的子查询上使用 IN。

于 2012-12-15T15:08:53.077 回答