Adding optional where parameters with Nhibernate and QueryOver - nhibernate

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.

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

QueryOver, GroupBy and Subselect

how can I do this with queryover and a subquery?
FakturaObjektGutschrift fog = null;
int[] idS = Session.QueryOver(() => fog)
.SelectList( list => list
.SelectMax(() => fog.Id)
.SelectGroup(() => fog.SammelKontierung)
).List<object[]>().Select(x => (int)x[0]).ToArray();
And using the ids in another query
IQueryOver<Beleg, Beleg> sdfsd = Session.QueryOver(() => bGut)
.AndRestrictionOn(() => fog.Id).IsIn(idS); //iDs is a list of int.
I would like do this with a subquery because there is a limitation on the number of parameters for a SQL query. How do I do this?
How do I write the first query, but without selecting the SelectGroup()? This is exactly where I got stuck.
Group by without projection in QueryOver API is currently not supported link.
You can use LINQ to create the projection in a subquery:
var subquery = Session.Query<FakturaObjektGutschrift>()
.GroupBy(x => x.SammelKontierung)
.Select(x => x.Max(y => y.Id));
var query = Session.Query<Beleg>()
.Where(x => subquery.Contains(x.Id));
If you really needs QueryOver to create more complex queries check this solution link.

QueryOver fails with could not resolve property:

I am using NHibernate 3.0 and was comparing Query and QueryOver
var p = _prepo.Query<Party>()
.Where(c => c.Person.LastName == "Bobby")
.FirstOrDefault();
The above works, I get proxy class for p.Person if I view the object graph.
var p = _prepo.QueryOver<Party>()
.Where(c => c.Person.LastName == "Bobby")
.FirstOrDefault();
This one fails with error ==> could not resolve property: Person.LastName of:
Why?
I'm not familiar with the Linq provider but when using QueryOver you have to use a join to do a query like that:
Example 1
IQueryOver<Cat,Kitten> catQuery =
session.QueryOver<Cat>()
.JoinQueryOver(c => c.Kittens)
.Where(k => k.Name == "Tiddles");
Example 2
Cat catAlias = null;
Kitten kittenAlias = null;
IQueryOver<Cat,Cat> catQuery =
session.QueryOver<Cat>(() => catAlias)
.JoinAlias(() => catAlias.Kittens, () => kittenAlias)
.Where(() => catAlias.Age > 5)
.And(() => kittenAlias.Name == "Tiddles");
It works when you use Linq in this case because the filtering is being done on the client, not in the database. So it is actually the IEnumerable version of Where that is running which is not related to NHibernate.
The QueryOver uses Expression<Func<T,object>> which NHibernate tries to translate to SQL but fails. For reasons unknown to me you must explicitly join using JoinQueryOver or JoinAlias.
Some more info on QueryOver here:
http://nhibernate.info/blog/2009/12/17/queryover-in-nh-3-0.html

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);