简体   繁体   中英

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, eg 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>>>

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM