LINQ-NHibernate - 仅为复杂对象选择几个字段(包括Collection)

Gui*_*ais 5 c# linq nhibernate asp.net-mvc linq-to-nhibernate

我在我的一个项目(和ASP.NET MVC应用程序)中使用Fluent NHibernate,使用LINQ查询数据(使用LINQ到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; }
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常标准的课程.我们可以看到一个Foo对象将有一个条形和bazes列表.

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

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

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

IList<Foo> listFoos = foos.ToList();
Run Code Online (Sandbox Code Playgroud)

这将返回一个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();
Run Code Online (Sandbox Code Playgroud)

我得到的错误是数组索引超出范围异常的行.堆栈跟踪显示了一些与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
Run Code Online (Sandbox Code Playgroud)

Tom*_*ell 6

一些建议......

首先,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();
         }
Run Code Online (Sandbox Code Playgroud)

  • 我相信这个例子是针对Query API而不是NHibernate Linq提供者使用LinqToObjects. (5认同)