NHibernate QueryOver projection on many-to-one - nhibernate

I am trying to get a QueryOver working using a Projection on a many-to-one.
The class "Post" has a property many-to-one "Creator".
Using
session.QueryOver(Of Post).
Select(Projections.
Property(of Post)(Function(x) x.Creator).
WithAlias(Function() postAlias.Creator)).
TransformUsing(Transformers.AliasToBean(Of Post)()).
List()
works BUT each creator is retrieved by a single query rather than using a join like it is done when not using a select/projection. So if there are 5 posts with 5 different creators, 6 queries will be run 1 for the list of posts and 5 for the creators.
I tried to get it working using a JoinAlias but nothing really did the job.
I already searched for a solution, but all solutions I found did use the Linq-Provider which does not really fit since the actual "field list" is passed via a parameter.
Does anyone know if there is a solution to this other than the linq provider?

There is a solution, we can use projections for many-to-one and then custom result transformer.
DISCLAIMER: I can read VB syntax but do not have enough courage to write... I expect that you can read C# and convert it into VB....
So we can have projection like this:
// aliases
Post root = null;
Creator creator = null;
// projection list
var columns = Projections.ProjectionList();
// root properties
columns.Add(Projections.Property(() => root.ID).As("ID"));
columns.Add(Projections.Property(() => root.Text).As("Text"));
// reference properties
columns.Add(Projections.Property(() => creator.ID).As("Creator.ID"));
columns.Add(Projections.Property(() => creator.FirstName).As("Creator.FirstName"));
// so our projections now do have proper ALIAS
// alias which is related to domain model
// (because "Creator.FirstName" will be use in reflection)
var query = session.QueryOver<Post>(() => root)
.JoinAlias(() => root.Creator, () => creator)
.Select(columns)
Now we would need smart Transformer, our own custome one (plugability is power of NHibernate). Here you can find one:
public class DeepTransformer
And we can continue like this
var list = query
.TransformUsing(new DeepTransformer<Post>())
.List<Post>()
Check also this:
Fluent NHibernate - ProjectionList - ICriteria is returning null values
NHibernate AliasToBean transformer associations

Related

Override lazy loading behavior 'lazy=false'

I would like to override the default lazy loading behavior which is set in mappings as 'lazy=false'. Can't change it as many parts of existing application depend on this setting.
After spent some hours on it I didn't find a solution so I'm asking here.
How to do this?
What I want to achieve is to fine tune my query to load only what is needed.
This is what I've tried already:
Using QueryOver api:
var properties = session.QueryOver<Property>()
.Fetch(prop => prop.Transactions).Eager
.Fetch(prop => prop.Districts).Eager
//I dont want to load those entities below so I mark
//them as lazy - IT DOESN'T WORK
//I can see in SQL that they are getting loaded in separate queries
.Fetch(prop => prop.Districts.First().Aliases).Lazy
.Fetch(prop => prop.Districts.First().PolygonPoints).Lazy
.Skip(i * pageSize)
.Take(pageSize)
.List();
Using Criteria api:
var criteria = session.CreateCriteria<Property>();
criteria.SetFetchMode("Transactions", NHibernate.FetchMode.Join);
criteria.SetFetchMode("Districts", NHibernate.FetchMode.Join);
criteria.SetFetchMode("Districts.Aliases", NHibernate.FetchMode.Select); // tried Lazy too
criteria.SetFetchMode("Districts.PolygonPoints", NHibernate.FetchMode.Select); // tried Lazy too
criteria.AddOrder(NHibernate.Criterion.Order.Asc("Id"));
criteria.SetFirstResult(i * pageSize);
criteria.SetMaxResults(pageSize);
var properties = criteria.List<Property>();
Using any of the above methods 'Aliases' and 'PolygonPoints' are always being loaded when calling List<>(). I don't need them in my process.
I'm using Nhibernate 4.0.
Any ideas?
We cannot override mapping in this case. We can do it opposite way - have lazy in place - and use eager fetching for querying.
The decision process (of reference loading) is done outside of the query, ex post. So it could be pre-loaded, but cannot be avoided.
Solution here could be of two types.
The first is preferred (by me) - do your best and make laziness the default: Ayende -
NHibernate is lazy, just live with it
Use projections. Instruct NHibernate to create just one query, use transformer to get expected object graph - without any proxies in it
There is pretty clear example how to (properly) use projection list even for references:
Fluent NHibernate - ProjectionList - ICriteria is returning null values
And we would also need Custom result transformer, which will ex-post create all the references from the returned data:
Custom DeepResultTransfomer

FluentNhibernate - query with ReferencesAny

When using ReferencesAny<> there is .EntityTypeColumn("MyType") which is not visible to application.
Is there option to query by that column using HQL or something and how to integrate into my LINQ to HQL queries?
As always, NHibernate has solution for (almost) everything.
Let's assume mapping like this (ala documentation):
ReferencesAny(x => x.AnyEntity) // the property name is AnyEntity... used below
// some explicit mapping
.AddMetaValue<Household>(typeof(Employee).Name)
.AddMetaValue<Client>(typeof(Contact).Name)
// the essence of <any>
.EntityTypeColumn("MyType")
.EntityIdentifierColumn("TheId")
.IdentityType<int>();
A cite from doc: 14.7. The where clause:
Likewise, the special property class accesses the discriminator value of an instance in the case of polymorphic persistence. A .Net class name embedded in the where clause will be translated to its discriminator value.
So, now we've informed NHibernate, that the colunn MyType contains the type, the equiv of the C# type. Because we can access EntityTypeColumn via the .class - this query will give us what we want:
// the 'Contact' represents the value contained in DB...
var hql = "from MyEntity where AnyEntity.class = 'Contact' ";
...
session.CreateQuery(hql)
.List<MyEntity>();
this will generate the WHERE ... MyType = 'Contact'
The same in the QueryOver:
var query = session.QueryOver<MyEntity>()
.Where(x => x.AnyEntity is Contact)
.List<MyEntity>();
NOTE: the bad news, that with built in LINQ provider, similar query is failing. I would guess it is a bug. I.e. this is not working session.Query<MyEntity>().Where(x => x.AnyEntity is Contact), generating the wrong WHERE clause
EXTENDED as asked in the comment
We can also filter by the <any> elements ID, the 'EntityIdentifierColumn'
The syntax would look like this (see the magical ".id" selector")
query
...
.Where(Restrictions.In("AnyEntity.id", new[] {1, 2, 3, 4, 5, 6, 7, 8}))
...
How about simply mapping the column in the entity you reference. Have a base class for all kind of types for example?

Is it possible to intelligently map a DTO in NHibernate?

If I want to create a DTO from properties on classes, all of which are mapped, can I do this without writing a regular SQL statement?
I've seen a lot of documentation on making the DTO class, as well as using it, but not much about how it gets created. I've seen SQL queries used with the Transformer, but that requires the use of magic strings.
Edit:
Thanks for the comments. I am aware there are multiple methods to retrieve DTOs, but I have been unable to find examples of methods that don't use SQL/HQL. Is there a reference somewhere, or is this one of those sparsely documented areas of NHibernate?
From your latest comment here are some examples of projecting a DTO using QueryOver and Linq
Using QueryOver:-
var schoolList = Session.QueryOver<lmschool>()
.SelectList(i => i
.Select(p => p.Name).WithAlias(() => dto.Name)
.Select(p => p.Lat).WithAlias(() => dto.Lat)
.Select(p => p.Lng).WithAlias(() => dto.Lng)
)
.Where(w => w.Lat != null && w.Lng != null)
.TransformUsing(Transformers.AliasToBean<MarkerDto>())
.List<MarkerDto>();
Using the NH Linq provider:-
var schoolList = (from school in Session.Query<lmschool>()
.Where(w => w.Lat != null && w.Lng != null)
select new LmSchoolMarkerDto {
Name = school.Name,
Lat = school.Lat,
Lng = school.Lng,
}).ToList();
Source is from my blog
Also if you need to see how multiple joins are used the see this blog post.
There are also lots of S.O. posts that should help you, see this search.
You could load all necessary entities and use AutoMapper to map them to DTOs more easily ?

Mapping to custom types in NHibernate 3.2

I have just started to use NHibernate 3.2 with its new Conformist API, having used previous versions with Fluent a while back. The basic stuff seems fine but I am currently struggling with trying to map a string to a custom type.
In this specific case, I have a string which is a semi-colon separated list of roles in a column on one of my tables. When I get it out, I want it to be mapped into a "RoleSet" custom object that I have created by passing the string value from the database into its constructor.
I have created a IUserType but I cannot see how to tell it to use it.
Previously with Fluent I would have done this in my map class:
Map(x => x.Roles).CustomType<RoleSetType>();
Is there an equivalent way to do this is in the new API?
Give this a try...
Property(x => x.Roles, x => x.Type(typeof(RoleSetType), null));

Unable to figure out how to do Joins within IQueryable

Here is what I am trying:
IQueryable query = this.MyRepository.GetShippingCollection();
IList<SomeListType> myList = query.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
Produces this error:
could not resolve property: Settings.ID
If I do it this way it works, but causes over 3,000 queries on my SQL Server:
IList<SomeListType> myList = this.MyRepository.GetShippingCollection().ToList();
myList = myList.Where(x => x.Settings
.Where(y => y.SelectorID.Equals(5))
.Count() > 0)
.OrderBy(x => x.Order)
.ToList();
I know the solution resides within using a "Join".
I have been looking at examples for the last couple hours and can only find Join examples within the Mapping file. I am also finding examples for "ICriteria".
I don't want to have to create seporate entries for all my complex queries in the mapping file so the join within that file will not work.
Since I am using Fluent NHibernate, I am not using "ICriteria". I am using "IQueryable". How do you join multiple tables within "IQueryable"?
Any help with this would be greatly appreciated.
Thanks in advance.
If the second query is executing 3,000 queries, it is almost certainly lazy-loading the Settings collection. As you iterate over the list, you access this collection, and each time NHibernate goes back to the database to fetch it. Try setting the fetch mode for the Settings property to eager load in the mapping.
Beyond that, the LINQ provider could be an issue. What version of NHibernate are you using? The 2.x LINQ provider has some real limitations. It has been reimplemented in the 3.0 trunk, but you'll have to download and compile it from the source.
By the way, ICriteria vs IQueryable is not related to Fluent NHibernate. Criteria API and LINQ are two providers through which you can create queries. Fluent NHibernate is an alternative way to perform configuration.