问题
Is it possible that I can project collection in NHibernate? For Example:
User
{
UserGuid,
IList<Groups> UserGroups,
}
User userEntity = null;
_session
.StatefulSession.QueryOver(() => userEntity)
.SelectList(list => list
.Select(x => x.UserGuid).WithAlias(() => userEntity.UserGuid)
//How can I project group collection here?
.Select(x => x.Groups).WithAlias(() => userEntity.Groups)
)
.TransformUsing(Transformers.AliasToBean<UserEntity>())
.List();
回答1:
We have to thing about projection as a single SELECT clause. What we would be able to do with it on a DB side, we can mimic with QueryOver
. So, in this case, we can join User
and Group
, and project few properties from User and few from Group. The best target would be some DTO...
The syntax could be like this:
ResultDTO resultDTO = null; // the target to be returned
Group groupEntity = null;
User userEntity = null;
_session.StatefulSession
.QueryOver(() => userEntity)
.JoinAlias(() => userEntity.Groups, () => groupEntity)
.SelectList(list => list
.Select(x => x.UserGuid).WithAlias(() => resultDTO.UserGuid)
// more about User - targeting the ResultDTO
...
// almost the same for Group properties
.Select(x => groupEntity.Name).WithAlias(() => resultDTO.GroupName)
..
)
.TransformUsing(Transformers.AliasToBean<ResultDTO >())
.List();
But there are some issues arround, which in fact effectively disallow this approach. We will reach the Cartesian product
, because each row of a User will be multiplied as many times, as many Groups it does have.
Row 1 - User A - Group A
Row 2 - User A - Group B // two rows for a user A
Row 3 - User B - Group B
Row 4 - User B - Group C
One way to solve it, is to skip the SelectList()
(do not use projections) and ask NHiberante to use different transformation:
_session.StatefulSession
.QueryOver(() => userEntity)
.JoinAlias(() => userEntity.Groups, () => groupEntity)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
Nice, one would say, but it is wrong again. No effective paging is possible, because the effect of more rows for each User still remains. So, if we would say Take(2)
, we will get only User A. The reason is hidden in fact, that the narrowing is done expost, in memory, on .NET/Application tier side - not in DB
Suggestion: Do not use any joins among one-to-many relations. Use the separated queries with conjunction of powerful NHibernate feature: 19.1.5. Using batch fetching
Please, see also:
- NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
- Is this the right way to eager load child collections in NHibernate
- https://stackoverflow.com/questions/18419988/
来源:https://stackoverflow.com/questions/24517403/project-collection-in-nhibernate