nHibernate Dynamic Joins with QueryOver
I'm wanting to create a generic method for List() with nHibernate and QueryOver. I've got it to a point where I wanted to add joins but I don't think I can without specifying the generic type that I'm joining... Which doesn't make it so dynamic because every generic must be declared. Is there anywhere to have a dynamic list of joins? Code below:
public static IList<T> QueryOver<T>(
Dictionary<Expression<Func<T, object>>, JoinType> joins,
List<Expression<Func<T, bool>>> predicates,
Dictionary<Expression<Func<T, object>>, System.Web.UI.WebControls.SortDirection> sortList,
int? maxResults
) where T : class
{
IList<T> results;
IQueryOver<T, T> query;
results = null;
// open the session
using (ISession session = OpenSession())
{
// begin a transaction
using (ITransaction transaction = session.BeginTransaction())
{
try
{
// declare the query
query = session.QueryOver<T>();
开发者_开发知识库 // joins
if (joins != null && joins.Count > 0)
{
foreach (KeyValuePair<Expression<Func<T, object>>, JoinType> join in joins)
{
// required to specify the type in the format query.JoinQueryOver<SubType>(join.Key, join.Value)
// BUT this means that it's not so dynamic because each SubType would have to be specified in the method call, yes?
query = query.JoinQueryOver(join.Key, join.Value);
}
}
// apply the where clauses
if (predicates != null && predicates.Count > 0)
{
foreach (Expression<Func<T, bool>> predicate in predicates)
{
query = query.Where(predicate);
}
}
// apply the sorting
if (sortList != null && sortList.Count > 0)
{
foreach (KeyValuePair<Expression<Func<T, object>>, System.Web.UI.WebControls.SortDirection> sort in sortList)
{
if (sort.Value == System.Web.UI.WebControls.SortDirection.Ascending)
{
query = query.OrderBy(sort.Key).Asc;
}
else
{
query = query.OrderBy(sort.Key).Desc;
}
}
}
// max results
if (maxResults.HasValue && maxResults.Value > 0)
{
query = (IQueryOver<T, T>)query.Take(maxResults.Value);
}
results = query.List();
// no errors, commit the transaction
transaction.Commit();
}
catch (Exception ex)
{
// error, rollback
transaction.Rollback();
// throw the exception and let the business logic deal with it
throw ex;
}
}
}
return results;
}
Look into using JoinAlias instead of JoinQueryOver... that should help you.
There are two kind of joins: direct joins on the entity of type T, and indirect joins (two or more steps in). These require different approaches.
(1) For direct joins, your method could receive an IEnumerable of type (ready for it):
Tuple<Expression<Func<T, object>>, Expression<Func<object>>>
an example of which might look like this in the calling code:
JoiningEntity joiningEntity = null;
new Tuple<Expression<Func<YourEntityType, object>>, Expression<Func<object>>>(entity => entity.JoiningEntity, () => joiningEntity)
The null object is just an alias so that QueryOver can resolve it. You may get annoyed with the warnings in Visual Studio telling you that it's null so I would use a helper method to create the null objects, e.g. Null.Get<HolidayOccupancyPrice>()
(see bottom for Null helper method).
(2) For indirect joins, you need to pass in an IEnumerable of type:
Tuple<Expression<Func<object>>, Expression<Func<object>>>
i.e. as above but without the Entity type. And then your calling code might send a join like this:
JoiningEntity joiningEntity = null;
new Tuple<Expression<Func<object>>, Expression<Func<object>>>(() => joiningEntity.IndirectJoiningEntity, () => joiningEntity)
Putting it together in your query method, you'd want something like this:
IEnumerable<Tuple<Expression<Func<T, object>>, Expression<Func<object>>>> directJoins;
IEnumerable<Tuple<Expression<Func<object>>, Expression<Func<object>>>> indirectJoins;
// ....
foreach (var join in directJoins)
query = queryable.Left.JoinAlias(dependency.Item1, dependency.Item2);
foreach (var join in indirectJoins)
query = queryable.Left.JoinAlias(dependency.Item1, dependency.Item2);
(Notice I've specified Left join explicitly -- if you wanted this controllable you'd have to add it in as an additional parameter in the Tuple)
Now all that looks quite complex, but its fairly straight forward once you put it together. You can of course create helper methods to reduce the amount of 'Func' gunk in your code.
Hope that helps!
Null helper method:
public static class Null
{
public static T Get<T>()
{
return default(T);
}
}
精彩评论