Querying an Uninitialized Collection with NHibernate
I have talked before about the problem of accessing an uninitialized collection and presented a solution that allows us to see if the collection contains elements on the DB, and how many there are, without actually loading them. Now I have a general purpose solution for querying the collection on the DB.
Here it is:
1: public static IQueryable<T> Query<T>(this IEnumerable<T> collection)
2: {
3: if (collection is AbstractPersistentCollection)
4: {
5: IPersistentCollection col = collection as IPersistentCollection;
6:
7: if (col.WasInitialized == false)
8: {
9: String role = col.Role;
10: Object owner = col.Owner;
11: Object key = col.Key;
12: ISessionImplementor sessionImpl = typeof(AbstractPersistentCollection).GetField("session", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(collection) as ISessionImplementor;
13: ISession session = sessionImpl as ISession;
14: ISessionFactory sessionFactory = session.SessionFactory;
15: String ownerEntityName = sessionImpl.BestGuessEntityName(owner);
16: Type ownerType = Type.GetType(ownerEntityName);
17: IClassMetadata metadata = sessionFactory.GetClassMetadata(ownerEntityName);
18: String idPropertyName = metadata.IdentifierPropertyName;
19: MethodInfo ownerIdGetMethod = ownerType.GetProperty(idPropertyName).GetGetMethod();
20: String childPropertyName = role.Split('.').Last();
21: MethodInfo ownerChildGetMethod = ownerType.GetProperty(childPropertyName).GetGetMethod();
22: Type childType = typeof(T);
23: ParameterExpression a = null;
24: IQueryable<T> details = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "SelectMany" && x.GetParameters().Length == 2).First().MakeGenericMethod(ownerType, childType).Invoke
25: (
26: null,
27: new Object[]
28: {
29: typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "Where").First().MakeGenericMethod(ownerType).Invoke
30: (
31: null,
32: new Object[]
33: {
34: typeof(LinqExtensionMethods).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "Query" && x.GetParameters()[0].ParameterType == typeof(ISession)).Single().MakeGenericMethod(ownerType).Invoke
35: (
36: null,
37: new Object [] { session }
38: ),
39: Expression.Lambda
40: (
41: typeof(Func<,>).MakeGenericType(ownerType, typeof(Boolean)),
42: Expression.Equal(Expression.Property(a = Expression.Parameter(ownerType, "x"), ownerIdGetMethod), Expression.Constant(key, ownerIdGetMethod.ReturnType)), new ParameterExpression[] { a }
43: )
44: }
45: ),
46: Expression.Lambda
47: (
48: typeof(Func<,>).MakeGenericType(ownerType, typeof(IEnumerable<>).MakeGenericType(childType)),
49: Expression.Property(a = Expression.Parameter(ownerType, "x"), ownerChildGetMethod), new ParameterExpression[] { a }
50: )
51: }
52: ) as IQueryable<T>;
53:
54: return (details);
55: }
56: }
57:
58: return (collection.AsQueryable());
59: }
As you can see, I first check to see the collection is an NHibernate one, if not, I fall back to LINQ to Objects; also, if the collection is already initialized, I also use LINQ to Objects.
Hope this helps!