简体   繁体   中英

NHibernate 3 or 4 Equivalent to Entity Framework Include

Does NHibernate 3 or 4 have an equivalent to Entity Framework's "Include" method that takes a string argument as opposed to a llambda? I'd like to do something like this in NHibernate:

Contact contact =
        context.Contacts.Include("SalesOrderHeaders.SalesOrderDetails")
        .FirstOrDefault();

I've come across this code from this post which uses "Fetch" in a loop which is cool, but this only handles objects that are first-level children of the main object whereas the above EF code goes down 2 levels without needing strongly-typed llambdas.

public IQueryable<T> All<T>(params Expression<Func<T, Object>> [] fetchPaths)
{
    var queryable = this.session.Query<T>();

    foreach (var fetchPath in fetchPaths)
    {
        queryable = queryable.Fetch(fetchPath);
    }

    return queryable;
}

NHibernate has a second method called ThenFetch. You will have to write

this.session.Query<T>()
            .Fetch(x => x.Property)
            .ThenFetch(x => x.SubProperty);

It seems that there a couple options to solve my problem.

Option 1: Dynamically build HQL in the form of:

from Contacts t0
left join fetch t0.SalesOrderHeaders t1
left join fetch t1.SalesOrderDetails t2

Option 2: Use NHibernate's ICriteria.SetFetchMode. This can handle string-based joins which is very helpful, and is easier than the HQL approach for my purposes. Here's the code I ended up writing (adapted from this post ). I hope it's helpful to someone else. The "GetPathsToUse" method is my attempt at having Entity Framework-style include paths. So if I wanted to supply "SalesOrderHeaders.SalesOrderDetails" as the only path, that method would add "SalesOrderHeaders" first before "SalesOrderHeaders.SalesOrderDetails" so that NH would be happy.

// This also uses ICriteria, but adds some logic to auto-handle paths to reduce the number of paths needed to pass in to match how 
        // EntityFramework works e.g. if one path is passed in for OrderInputs.Input, this method will call SetFetchMode twice, once for OrderInputs and again for OrderInputs.Input
        public T GetByIdSetFetchModeEFStyle(Guid id, ISession session, params string[] includePaths)
        {   
            ICriteria criteria = session.CreateCriteria<T>();

            if (includePaths != null && includePaths.Length > 0)
            {
                // NHibernate requires paths to be supplied in order.  So if a path "OrderInputs.Input" is supplied, we must make 
                // 2 separate calls to SetFetchMode, the first with "OrderInputs", and the second with "OrderInputs.Input".
                // EntityFramework handles this for us, but NHibernate requires things to be different.
                List<string> pathsToUse = this.GetPathsToUse(includePaths);

                foreach (string path in pathsToUse)
                {
                    criteria.SetFetchMode(path, FetchMode.Eager);
                }
            }

            return criteria
                // prevent duplicates in the results
                .SetResultTransformer(Transformers.DistinctRootEntity)
                .Add(Restrictions.Eq(typeof(T).Name + "Id", id)).UniqueResult<T>();
        }

        private List<string> GetPathsToUse(string[] includePaths)
        {
            var nhPaths = new List<string>();
            foreach (string path in includePaths)
            {
                if (!path.Contains(".") && !nhPaths.Contains(path))
                {
                    // There is no dot in the path - just add it if it hasn't been added already
                    nhPaths.Add(path);
                }
                else
                {
                    // We have a dot e.g. OrderInputs.Input.  We need to add "OrderInputs" before "OrderInputs.Input"
                    string[] pathParts = path.Split(".".ToCharArray());

                    // Add any missing ancestors of the current path prior to adding the path
                    for (int p = 1; p <= pathParts.Length; p++)
                    {
                        string requiredPath = string.Join(".", pathParts.Take(p).ToArray());
                        if (!nhPaths.Contains(requiredPath))
                        {
                            nhPaths.Add(requiredPath);
                        }
                    }
                }
            }
            return nhPaths;
        }

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