NHibernate QueryOver over many tables - nhibernate

There is an entity Effort that has a property List and a property AdType
We have several adTypes enum objects and specialLists enum objects to select IList<Effort>
I do it in this way:
return NHibernateSession.QueryOver<Effort>()
.JoinQueryOver(effort => effort.AdType)
.WhereRestrictionOn(adType => adType.Id)
.IsIn(adTypes.Select(adt => (long)adt).ToList())
.Clone()
.JoinQueryOver(effort => effort.List)
.WhereRestrictionOn(list => list.Id)
.IsIn(specialLists.Select(sl => (long)sl).ToList())
.List<Effort>();
as u can see I use a strange Clone() method that doesn't have any description. It works great.
In what way do you use QueryOver for such queries?

.JoinQueryOver(effort => effort.AdType) will return a QueryOver with a subtype, here AdType IQueryOver<Effort, Adtype> instead of the original IQueryOver<Effort, Effort>. The first generic argument is the queryType and the second the type on which the methods are operating on. If you clone in between the whole query is copyied and returned as the basequery IQueryOver<Effort, Effort>.
To prevent the QueryOver to switch to the subtype there is JoinAlias which creates an alias instead of descending.
AdType adAlias = null;
ListType listAlias = null;
return NHibernateSession.QueryOver<Effort>()
.JoinAlias(effort => effort.AdType, () => adAlias)
.JoinAlias(effort => effort.List, () => listAlias)
.WhereRestrictionOn(() => adAlias.Id).IsIn(adTypes.Cast<long>().ToList())
.WhereRestrictionOn(() => listAlias.Id).IsIn(specialLists.Cast<long>().ToList())
.List<Effort>();
not that if you only Restrict on the Id of adtype and listtype then
return NHibernateSession.QueryOver<Effort>()
.WhereRestrictionOn(effort => effort.Adtype.Id).IsIn(adTypes.Cast<long>().ToList())
.WhereRestrictionOn(effort => effort.List.Id).IsIn(specialLists.Cast<long>().ToList())
.List<Effort>();

Related

Nhibernate Restrictions.In Error

Finally tracked down my error which is a result of the query. I have an nhibernate query using a Restrictions.In. Problem is once query executes if no results returned query throws error immediately. Is there another restriction that I can use. I know if I was writing a linq query I could use the .Any to return bool value and go from there is there something similar I can do in this instance?
carMake is passed in
myQuery.JoinQueryOver(x => x.Car)
.Where(Restrictions.In("VIN",
Trades.Where(x => x.Car.Make.ToLower() == carMake.ToLower())
.Select(x => x.Car.PrimaryVIN)
.ToList()));
Assuming that Trades is a list of objects you can use .WhereRestrictionOn() instead. Try this (I split the code for better readability):
var vinsWithCarMake = Trades
.Where(x => x.Car.Make.ToLower() == carMake.ToLower())
.Select(x => x.Car.PrimaryVIN)
.ToList<string>();
var carAlias = null;
var result = myQuery.JoinAlias(x => x.Car, () => carAlias)
.WhereRestrictionOn(() => carAlias.VIN).IsInG<string>(vinsWithCarMake)
.List();

Adding optional where parameters with Nhibernate and QueryOver

How do I add optional where clauses with QueryOver?
TL;DR
I am trying to implement a search form for an application and use QueryOver.
Some of the search parameters are optional.
var query =
myDatabase.QueryOver(() => customerAlias)
.JoinAlias(() => customerAlias.Projects, () => projectAlias)
.Where(() => projectAlias.IsClosed >= 1)
... possibly add more stuff
QueryOver is deferred in execution as for usual Linq statements. It will only execute when you force it to by calling a finalising method such as .List<T>().
var query =
myDatabase.QueryOver(() => customerAlias)
.JoinAlias(() => customerAlias.Projects, () => projectAlias)
.Where(() => projectAlias.IsClosed >= 1);
if (myCondition) {
query = query.Where(...);
}
var result = query.List<T>(); //Or however else you want to make it execute.
You should still be able to access the in-line aliases too this way.

How do I target an alias from a restriction, with the QueryOver api?

As far as I know, the QueryOver api does not allow you reference an alias by name, but rather you use a typed object. How can I add a restriction to my query that targets the alias?
For example, I would like to accomplish something similar to the following:
var query = session.QueryOver<Person>().JoinQueryOver(x => x.Dogs, () => dogAlias);
return query.Where(Restrictions.Disjunction()
.Add(Restrictions.Like("Name", searchQuery, MatchMode.Anywhere))
.Add(Restrictions.Like("dogAlias.Name", searchQuery, MatchMode.Anywhere)));
instead of:
Restrictions.Like("dogAlias.Name", searchQuery, MatchMode.Anywhere)
use:
Restrictions.On(() => dogAlias.Name).IsLike(searchQuery, MatchMode.Anywhere)
So, the complete query would become:
var query = session.QueryOver<Person>()
.JoinQueryOver(x => x.Dogs, () => dogAlias);
return query.Where(Restrictions.Disjunction()
.Add(Restrictions.On<Person>(p => p.Name).IsLike(searchQuery, MatchMode.Anywhere))
.Add(Restrictions.On(() => dogAlias.Name).IsLike(searchQuery, MatchMode.Anywhere)));

NHibernate 3. Alternatives to "ThenFetch" in QueryOver

I'm using NHibernate 3.0 with both the LINQ provider and QueryOver. Sometimes I want to eager load related data, and there comes the method "Fetch" to the rescue, both in LINQ and QueryOver. Now I have a special scenario where I want to eager load a property not directly on the second level, like:
Foo f = ...;
f.A.B.C
with LINQ there's no problem, as you can "chain" fetches with the method "ThenFetch", like:
var result = Session.Query<Foo>().Fetch(a => a.A).ThenFetch(b => b.B).ThenFetch(c => c.C).ToList();
In QueryOver there's no such method, so how can I achieve the same result?
Thanks in advance.
I actually managed to solve this problem using two different approaches:
Approach one:
Session.QueryOver<Foo>().Fetch(x => x.A).Fetch(x => x.A.B).Fetch(x => x.A.B.C)
Approach two:
A a = null;
B b = null;
C c = null;
Session.QueryOver<Foo>()
.JoinAlias(x => x.A, () => a)
.JoinAlias(() => a.B, () => b)
.JoinAlias(() => b.C, () => c)
Both work (altough I'm not exactly sure if one of them generated "inner" and the other one "outer" joins).
Just as a curiosity, I'll post the reply they gave me on the NHibernate Jira:
query
.Fetch(p => p.B)
.Fetch(p => p.B.C) // if B is not a collection ... or
.Fetch(p => p.B[0].C) // if B is a collection ... or
.Fetch(p => p.B.First().C) // if B is an IEnumerable (using .First() extension method)
I think you can do that with JoinQueryOver
IQueryOver<Relation> actual =
CreateTestQueryOver<Relation>()
.Inner.JoinQueryOver(r => r.Related1)
.Left.JoinQueryOver(r => r.Related2)
.Right.JoinQueryOver(r => r.Related3)
.Full.JoinQueryOver(r => r.Related4)
.JoinQueryOver(r => r.Collection1, () => collection1Alias)
.Left.JoinQueryOver(r => r.Collection2, () => collection2Alias)
.Right.JoinQueryOver(r => r.Collection3)
.Full.JoinQueryOver(r => r.People, () => personAlias);

Is this the right way of using ThenFetch() to load multiple collections?

I'm trying to load all the collections eagerly, using NHibernate 3 alpha 1. I'm wondering if this the right way of using ThenFetch()?
Properties with plural names are collections. The others are just a single object.
IQueryable<T> milestoneInstances = Db.Find<T, IQueryable<T>>(db =>
from mi in db
where mi.RunDate == runDate
select mi).Fetch(mi => mi.Milestone)
.ThenFetch(m => m.PrimaryOwners)
.Fetch(mi => mi.Milestone)
.ThenFetch(m => m.SecondaryOwners)
.Fetch(mi => mi.Milestone)
.ThenFetch(m => m.Predecessors)
.Fetch(mi => mi.Milestone)
.ThenFetch(m => m.Function)
.Fetch(mi => mi.Milestone)
.ThenFetchMany(m => m.Jobs)
.ThenFetch(j => j.Source)
;
I thought of asking this in the NHibernate forums but unfortunately access to google groups is forbidden from where I am. I know Fabio is here, so maybe the guys from the NHibernate team can shed some light on this?
Thanks
Apparently, there's no "right" way to use ThenFetch in such a case. Your example works fine but SQL produced contains many joins to Milestone, which isn't that right.
Using IQueryOver instead of IQueryable allows you to use complex syntax in Fetch:
Fetch(p => p.B)
Fetch(p => p.B.C) // if B is not a collection ... or
Fetch(p => p.B[0].C) // if B is a collection ... or
Fetch(p => p.B.First().C) // if B is an IEnumerable (using .First() extension method)
So in your case it would be:
query // = session.QueryOver<X>()
.Fetch(mi => mi.Milestone).Eager
.Fetch(mi => mi.Milestone.PrimaryOwners).Eager
.Fetch(mi => mi.Milestone.SecondaryOwners).Eager
.Fetch(mi => mi.Milestone.Predecessors).Eager
.Fetch(mi => mi.Milestone.Function).Eager
.Fetch(mi => mi.Milestone.Jobs).Eager
.Fetch(mi => mi.Milestone.Jobs.First().Source).Eager
The one thing you are missing is that you should use FetchMany() and ThenFetchMany() is the child property is a collection.
IQueryable<T> milestoneInstances = Db.Find<T, IQueryable<T>>(db =>
from mi in db
where mi.RunDate == runDate
select mi);
var fetch = milestoneInstances.Fetch(f => f.Milestone);
fetch.ThenFetch(f => f.PrimaryOwners);
fetch.ThenFetch(f => f.SecondaryOwners);
//...
As leora said, make sure when fetching children collections that you use
FetchMany()
ThenFetchMany()
A new Fetch, should pick up from the root, but this does not always happen. Sometimes you need to create them as separate queries or use Criteria Futures to batch up a multiple fetch.