Multiple AND/OR Statements with NHibernate
I’m sure you’ve encountered the scenario where you need to add multiple AND or OR statements to an NHibernate query. The string parsing and manipulation required to do it in HQL would be enough to send you straight to programmer hell. So it turns out the easiest (in my opinion) option is to use the good ol’ Criteria API. But it’s not as obvious as it first seems. In order to accomplish the task, you actually need to use a pair of constructs that may be new to you: Conjunction which represents a grouping of AND statements (A and B and C…); and Disjunction, a grouping of OR statements (A or B or C…). Here’s an admittedly contrived example of how you could query for Persons by FirstName that are not archived (IsArchived), ordering by FirstName:
Contrived Disjunction Example
- string[] namesToCheck = { "Nick", "Dave", "Bryan", "James" };
- using (ISession session = SessionManager.CreateSession())
- {
- var criteria = session.CreateCriteria<Person>();
- var disjunction = Restrictions.Disjunction();
- criteria.Add(Expression.Eq("IsArchived", false));
- foreach (var name in namesToCheck)
- {
- disjunction.Add(Restrictions.Eq("FirstName", name));
- }
- criteria.Add(disjunction);
- criteria.AddOrder(Order.Asc("FirstName"));
- return criteria.List<Person>();
- }
This would generate something similar to the following SQL-statement:
Disjunction SQL Results
- SELECT * FROM Person
- WHERE IsArchived = 0 AND
- (FirstName = "Nick" OR
- FirstName = "Dave" OR
- FirstName = "Bryan" OR
- FirstName = "James")
- ORDER BY FirstName
If you needed ANDs instead of ORs you would use Conjunction instead of Disjunction.
A more powerful example would be if you needed multiple sets of statements. Consider the following scenario:
Select all Persons where IsArchived is false and the FirstName is “Nick”, or all Persons where IsArchived is true and the FirstName is “Bryan”
Here’s how you would do it using Conjunctions and Disjunctions:
Less Contrived Example
- using (ISession session = SessionManager.CreateSession())
- {
- var criteria = session.CreateCriteria<Person>()
- .AddOrder(Order.Asc("FirstName"));
- // IsArchived is FALSE and FirstName = "Nick"
- var conjunction1 = Restrictions.Conjunction();
- conjunction1.Add(Restrictions.Eq("IsArchived", false));
- conjunction1.Add(Restrictions.Eq("FirstName", "Nick"));
- // IsArchived is TRUE and FirstName = "Bryan"
- var conjunction2 = Restrictions.Conjunction();
- conjunction2.Add(Restrictions.Eq("IsArchived", true));
- conjunction2.Add(Restrictions.Eq("FirstName", "Bryan"));
- // This combines the two statements into an OR
- var disjunction = Restrictions.Disjunction();
- disjunction.Add(conjunction1);
- disjunction.Add(conjunction2);
- criteria.Add(disjunction);
- return criteria.List<Person>();
- }
Which would produce the following:
SQL Results
- SELECT * FROM Person
- WHERE
- (IsArchived = 0 AND FirstName = "Nick") OR
- (IsArchived = 1 AND FirstName = "Bryan")
- ORDER BY FirstName