LINQ-NHibernate -为复杂对象只选择几个字段(包括集合)

hgncfbus  于 2023-03-27  发布在  其他
关注(0)|答案(4)|浏览(142)

我在我的一个项目(和ASP .NETMVC应用程序)中使用FluentNHibernate,使用LINQ查询数据(使用LINQtoNHibernate库)。
为了保护无辜的人,物品的名字被改变了。
假设我在数据库(MySQL)中有以下类Foo、Bar、Baz以及它们相应的表。
Foo与Bar(表“FooBar”)和Baz(表“FooBaz”)都有多对多关系,在FluentMap中定义。因此,类接口定义如下:

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和bazes的列表。
当尝试执行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)。log 4 net显示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端的集合处理相关的方法名称,但没有其他内容。log 4 net报告的查询没有显示对条形图的查询,这意味着在执行查询以选择条形图之前捕获了错误。
以前有没有人遇到过这样的问题?你是怎么解决的?我不想每次在我的应用程序中打开一个网页时都要选择所有的对象!
谢谢大家!

编辑:这里是stacktrace,根据要求。

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
jaql4c8m

jaql4c8m1#

几个建议...
首先,NHibernate默认为延迟加载,因此可能甚至不需要执行您正在尝试的更“有效”的查询。
第二,下面的代码并没有完全做到你的示例代码所做的事情-它创建了一个名为miniFoos的匿名类型列表,其中包含Foo中字段的子集。但实际效果与你似乎正在尝试的类似。它还使用了我认为称为LINQ方法语法的东西。
我在我自己的应用程序中的一个实体上测试了这一点,它也有一个IList属性,它确实工作(尽管我应该注意我目前使用的是Eager loading)。

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();
}
toe95027

toe950272#

我已经试着回答这个问题了,但我不满意。
我对你的问题想得越多,就越觉得你是在尝试自己版本的延迟加载,根据我的经验,NHibernate自己就能很好地处理这个问题。
我知道(从评论中)你切换到Eager Loading是因为你没有保持会话打开。我认为从长远来看,如果你专注于这个Angular ,它会对你更有用。
如果我误解了,也许你可以编辑一下这个问题,解释一下为什么你认为你需要自己处理这个问题?

368yc8dk

368yc8dk3#

我可以设法投射出不是全部的集合,而是它的领域

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

CategoryName是Forum Entity中的计算字段,与DB没有任何Map

结果,我得到了一个巨大的论坛实体的小投影,并有可能在该领域进行排序。
希望它能帮助到别人。

n7taea2i

n7taea2i4#

我认为你可以通过定义结果转换器和结果集名称来解决你的问题,如果你可以以某种方式访问正在生成的Criteria,你就可以避免匿名对象。
问题是属性名和列名是不一样的,当使用nhibernate/linq到nhibernate的投影时,不会检查列名和属性名是什么。所以如果你在执行查询之前,在criteria中的result properties(我认为它被称为result set)中自己定义它们,它应该可以工作。
这很简单,给予看

相关问题