【问题标题】:LINQ-NHibernate - Selecting only a few fields (including a Collection) for a complex objectLINQ-NHibernate - 为复杂对象仅选择几个字段(包括集合)
【发布时间】:2011-01-01 16:45:02
【问题描述】:

我在我的一个项目(和 ASP.NET MVC 应用程序)中使用 Fluent NHibernate,使用 LINQ 查询数据(使用 LINQ to NHibernate 库)。

更改对象名称以保护无辜者。

假设我在数据库 (MySQL) 中有以下类 Foo、Bar、Baz 及其相应的表。

Foo 与在 Fluent 映射中定义的 Bar(表“FooBar”)和 Baz(表“FooBaz”)具有多对多关系。因此,类接口定义如下:

public class Foo {
    public virtual int id { get; set; }
    public virtual string name { get; set; }
    public virtual string email { get; set; }
    public virtual IList<Bar> bars { get; set; }
    public virtual IList<Baz> bazes { get; set; }
}

这是一个非常标准的课程。我们可以看到一个 Foo 对象会有一个 bar 和 baze 的列表。

在尝试执行 LINQ 查询时出现问题。

如果我做一个这样的简单查询,它工作正常(where 子句不重要):

var foos = from foo in session.Linq<Foo>()
           where email.equals("foo@bar.com")
           select foo;

IList<Foo> listFoos = foos.ToList();

这将返回一个 Foos 列表,其中填充了所有字段(id、name、email、bars、bazes)。 log4net 显示 NHibernate 对集合执行单独的查询。

当我只想加载某些字段时,就会出现问题。例如,我可能只想加载查询中的条形图,而不是 bazes。

此查询编译,但在运行时产生错误:

var foos = from foo in session.Linq<Foo>()
           where email.equals("foo@bar.com")
           select new Foo() 
           {
               id = foo.id,
               name = foo.name,
               email = foo.email,
               bars = foo.bars
           };

IList<Foo> listFoos = foos.ToList();

我得到的错误是数组索引越界异常。堆栈跟踪显示了一些与 LINQ-NHibernate 方面的集合处理相关的方法名称,但仅此而已。 log4net 报告的查询在柱上没有显示查询的迹象,这意味着在它有时间执行查询以选择柱之前就发现了一个错误。

以前有没有人遇到过这种问题?你是怎么解决的?我不想每次在我的应用程序中打开网页时都必须选择所有对象!

谢谢!

编辑:这里是堆栈跟踪,根据要求。

System.IndexOutOfRangeException: L'index se trouve en dehors des limites du tableau. (read: "Index is out of bounds for the array.")

[IndexOutOfRangeException: L'index se trouve en dehors des limites du tableau.]
   NHibernate.Transform.TypeSafeConstructorMemberInitResultTransformer.InvokeMemberInitExpression(MemberInitExpression expression, Object[] args, Int32& argumentCount) +404
   NHibernate.Transform.TypeSafeConstructorMemberInitResultTransformer.TransformTuple(Object[] tuple, String[] aliases) +150

[QueryException: could not instantiate: Foo]
   NHibernate.Transform.TypeSafeConstructorMemberInitResultTransformer.TransformTuple(Object[] tuple, String[] aliases) +265
   NHibernate.Loader.Criteria.CriteriaLoader.GetResultColumnOrRow(Object[] row, IResultTransformer resultTransformer, IDataReader rs, ISessionImplementor session) +171
   NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) +330
   NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +704
   NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +70
   NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) +111
   NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) +18
   NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes) +79
   NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) +407
   NHibernate.Impl.CriteriaImpl.List(IList results) +41
   NHibernate.Impl.CriteriaImpl.List() +35
   NHibernate.Linq.<GetEnumerator>d__0.MoveNext() +71
   System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) +7665172
   System.Linq.Enumerable.ToList(IEnumerable`1 source) +61
   FooRepository.List(Int32 count) in C:\...\FooRepository.cs:38
   FooController.List() in C:\...\FooController.cs:30
   lambda_method(ExecutionScope , ControllerBase , Object[] ) +39
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +178
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +24
   System.Web.Mvc.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7() +52
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +254
   System.Web.Mvc.<>c__DisplayClassc.<InvokeActionMethodWithFilters>b__9() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +192
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +399
   System.Web.Mvc.Controller.ExecuteCore() +126
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
   System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +151
   System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +57
   System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

【问题讨论】:

  • 请用堆栈跟踪发布完整的异常

标签: c# asp.net-mvc linq nhibernate linq-to-nhibernate


【解决方案1】:

一些建议...

首先,NHibernate 默认为延迟加载,因此甚至可能不需要执行您正在尝试的更“高效”的查询。

其次,以下代码与您的示例代码完全不同——它创建了一个名为 miniFoos 的匿名类型列表,其中包含 Foo 中字段的子集。但最终效果与您似乎正在尝试的类似。它还使用了我认为所谓的 LINQ 方法语法。

我在我自己的应用程序中的一个实体上对此进行了测试,该实体也有一个 IList 属性,并且它确实有效(尽管我应该注意我目前正在使用 Eager 加载)。我只是剪切并粘贴了工作代码并替换了您的示例名称。

        using( var session = sessionFactory.OpenSession() )
        {
            session.BeginTransaction();

            var foos =
                session.CreateCriteria(typeof(Foo))
                       .List<Foo>();

            var miniFoos = 
               foos.Select(f => new { f.email, f.bars })
                   .Where(f => f.email.Equals("foo@bar.com)
                   .ToList();

            session.Close();
         }

【讨论】:

  • 我是 NHibernate 世界的新手,但是延迟加载存在一些问题,因为 Fluent NHibernate 默认激活它。这样做的效果是我可以查询对象并操作它们,但最终,当我在我的视图中并需要访问这些对象时,由于 Session 已关闭(在视图中),我得到了错误,所以我把它关掉了。不过,我没有详细说明这个问题,所以我可能在延迟加载方面做错了。
  • 好的,我测试了你的代码,它似乎可以工作,至少在调试器中是这样。你知道我如何将它转换为具体的 IList (因为现在它只是根据编译器的匿名类型列表)。
  • 我对 NH 也很陌生,并且出于类似的原因在延迟加载上来回走动。你能保持会话一直打开吗?我通过添加我在启动程序时设置的“会话”变量解决了这个问题,并且只在程序退出时关闭会话。这样一来,NH 会为您处理所有脏活,这是首先使用 NH 的主要原因之一 ;-)
  • 我相信这个例子是针对查询 API 使用 LinqToObjects,而不是 NHibernate Linq 提供程序。
  • @Michael - 是的,经过反思,我认为你是对的。我应该记下我的答案吗?
【解决方案2】:

我认为您可以通过定义结果转换器和结果集名称来解决您的问题,如果您可以通过某种方式访问​​生成的 Criteria,则可以避免匿名对象。

问题是属性名和列名不一样,并且使用投影时nhibernate/linq to nhibernate不检查列名和属性名是什么。因此,如果您在执行查询之前在条件中的结果属性(我认为它称为结果集)中自己定义它们,它应该可以工作。

很简单,试试看

【讨论】:

  • Chen,如何在 IQueryable 上定义结果转换器?我知道你可以在 IQuery 上做到这一点,但我认为这不是我们所拥有的。我有同样的问题,所以如果你能详细说明你的建议,我会很高兴。另外,LINQ to NHibernate 和使用 Criteria 不一样吗?
【解决方案3】:

我已经尝试过回答这个问题,但对此并不满意。

我对您的问题思考得越多,您似乎就越想尝试自己的延迟加载版本,根据我的经验,NHibernate 自己可以很好地处理它。

我知道(从评论中)您切换到 Eager Loading 是因为您没有保持会话打开。我认为从长远来看,如果你专注于那个角度,它对你会更有用。

如果我误解了,也许您可​​以编辑问题以解释为什么您认为需要自己处理?

【讨论】:

  • 我认为你的反应是有道理的。但是,我知道我可以在我的 Fluent 映射中定义哪些字段将被延迟加载。但是,更进一步,我是否可以在我的 LINQ 查询中指定我想要专门加载的内容(例如,如果我的 bazes 集合很大并且我的索引页面上不需要它们,但我需要 bar 集合,我可以指定延迟加载 bazes,但完全加载栏吗?这有意义吗?)。假设延迟加载有效,我必须设计一种方法来保持会话在整个 ASP.NET MVC 应用程序中保持打开和可访问,但是另一个主题......
  • 延迟加载有效 - 相信我!如果您的程序直接读取对象,NH 只会加载对象中单个成员变量的数据。这意味着您可以只加载 Foo 并向您的用户显示 Bazes 集合。 Bazes 会被加载,而 Bar 不会,因为你没有在你的代码中显式地引用它,即使你引用的是 Foo。相信原力,卢克!查看stackoverflow.com/questions/2013467/… 获取有关会话的一些好的建议。
【解决方案4】:

我可以设法投影的不是整个集合,而是它的字段

   ... 
   Select(a => new Forum {
   Id = a.Id, 
   Name = a.Name, 
   CategoryName = a.Categories.Select(b => b.Name).First() 
   })

CategoryName 是论坛实体中的计算字段 与 DB 没有任何映射关系

结果我得到了巨大论坛实体的一个小投影,并有可能在现场进行排序。

希望它对某人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多