Я использую Быстрый NHibernate в одном из моих проектов (и ASP.NET приложение MVC) с LINQ для запросов к данным (использующий LINQ для библиотек NHibernate).
Имена objet изменились для защиты невинного.
Скажем, у меня есть следующие классы Нечто, Панель, Baz и их соответствующие таблицы в базе данных (MySQL).
Нечто имеет many-many отношения и с Панелью (таблица "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 будет иметь список панелей и оснований.
Проблема возникает при попытке сделать запрос LINQ.
Если я делаю простой запрос как это, он хорошо работает (где пункт неважен):
var foos = from foo in session.Linq<Foo>()
where email.equals("foo@bar.com")
select foo;
IList<Foo> listFoos = foos.ToList();
Это возвратит список Foos, со всеми заполненными полями (идентификатор, имя, электронная почта, панели, основания). log4net показывает, что NHibernate выполняет отдельные запросы для наборов.
Проблема возникает, когда я хочу загрузить только некоторые поля. Например, я мог бы хотеть загрузить только панели в запросе, но не основания.
Этот запрос компилирует, но производит ошибку во времени выполнения:
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, не показывает знака запроса на панелях, что означает, чем ошибка была зафиксирована, прежде чем это имело время для выполнения запроса для выбора панелей.
Кто-либо еще имел этот вид проблемы прежде? Как Вы решали его? Я не хочу должным быть выбирать все объекты каждый раз, я открываю веб-страницу в своем приложении!
Спасибо!
Править: вот 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
Пара предложений...
Во-первых, по умолчанию NHibernate - это ленивая загрузка, так что, возможно, даже не нужно делать более "эффективный" запрос.
Во-вторых, следующий код не совсем делает то, что делает ваш пример - он создает список анонимных типов, называемых miniFoos, которые содержат подмножество полей в Foo. Но сетевой эффект похож на то, что Вы, кажется, пытаетесь сделать. Он также использует то, что, как мне кажется, называется синтаксисом метода LINQ.
Я протестировал это на сущности в моем собственном приложении, которое также имеет свойство IList, и оно действительно работает (хотя я должен заметить, что в настоящее время я использую Eager load). Я просто вырезал и вставил рабочий код и подставил имена вашего примера.
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();
}
Я уже пытался ответить на этот вопрос, но меня это не устраивало.
Чем больше я думаю о вашем вопросе, тем больше мне кажется, что вы пытаетесь сделать свою собственную версию Lazy Loading, с которой NHibernate справляется довольно хорошо самостоятельно, по моему опыту.
Я знаю (из комментария), что вы переключились на Eager Loading, потому что не держали сессию открытой. Я думаю, что было бы более полезно для вас в долгосрочной перспективе, если бы вы сосредоточились на этом аспекте.
Если я неправильно понял, возможно, вы могли бы отредактировать вопрос, чтобы объяснить, почему вы думаете, что вам нужно справиться с этим самостоятельно?
.Я думаю, вы можете решить свою проблему, определив преобразователь результатов и имена наборов результатов, если каким-то образом вы можете получить доступ к генерируемым критериям, вы можете избежать анонимного объекта.
проблема в том, что имена свойств и имена столбцов не совпадают, и при использовании проекции nhibernate / linq для nhibernate не проверяется, что такое имя столбца и имя свойства. поэтому, если вы определите их самостоятельно в свойствах результата (я думаю, что это называется набором результатов) в критериях, прежде чем выполнять запрос, он должен работать.
это довольно просто, попробуйте