public EFUQueryable <T> Include(IncludeExecuter include) { _includes.Add(include); return(this); }
/// <summary> /// Loads a child collection in a more efficent way than the standard Include. Will run all involved queries as NoTracking /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="TChild"></typeparam> /// <param name="query"></param> /// <param name="context"></param> /// <param name="collectionSelector">The navigation property. It can be filtered and sorted with the methods Where,OrderBy(Descending),ThenBy(Descending) </param> /// <returns></returns> public static EFUQueryable <T> IncludeEFU <T, TChild>(this IQueryable <T> query, DbContext context, Expression <Func <T, IEnumerable <TChild> > > collectionSelector) where T : class where TChild : class { var octx = (context as IObjectContextAdapter).ObjectContext; var cSpaceTables = octx.MetadataWorkspace.GetItems <EntityType>(DataSpace.CSpace); var cSpaceType = cSpaceTables.Single(t => t.Name == typeof(T).Name); //Use single to avoid any problems with multiple tables using the same type var keys = cSpaceType.KeyProperties; if (keys.Count > 1) { throw new InvalidOperationException("The include method only works on single key entities"); } var fkGetter = GetForeignKeyGetter <T, TChild>(cSpaceTables); var pkInfo = typeof(T).GetProperty(keys.First().Name); var pkGetter = MakeGetterDelegate <T>(pkInfo); var childCollectionModifiers = new List <MethodCallExpression>(); var childProp = SetCollectionModifiersAndGetChildProperty(collectionSelector, childCollectionModifiers); var setter = MakeSetterDelegate <T>(childProp); var e = new IncludeExecuter { ElementType = typeof(TChild), SingleItemLoader = parent => { if (parent == null) { return; } var children = octx.CreateObjectSet <TChild>(); GetRootEntityToChildCollectionSelector <T, TChild>(cSpaceType); var q = ApplyChildCollectionModifiers(children, childCollectionModifiers); var rootPk = pkGetter((T)parent); var param = Expression.Parameter(typeof(TChild), "x"); var fk = GetFkProperty <T, TChild>(cSpaceTables); var body = Expression.Equal(Expression.Property(param, fk), Expression.Constant(rootPk)); var where = Expression.Lambda <Func <TChild, bool> >(body, param); q = q.AsNoTracking().Where(where); setter((T)parent, q.ToList()); }, Loader = (rootFilters, parents) => { var baseType = typeof(T).BaseType != typeof(object) ? typeof(T).BaseType : typeof(T); dynamic dynamicSet = octx.GetType() .GetMethod("CreateObjectSet", new Type[] { }) ?.MakeGenericMethod(baseType) .Invoke(octx, new Object[] { }); if (!(dynamicSet?.OfType <T>() is ObjectQuery <T> set)) { return; } IQueryable <T> q = set; foreach (var item in rootFilters) { var newSource = Expression.Constant(q); var arguments = Enumerable.Repeat(newSource, 1).Concat(item.Arguments.Skip(1)).ToArray(); var newMethods = Expression.Call(item.Method, arguments); q = q.Provider.CreateQuery <T>(newMethods); } var lambdaExpression = GetRootEntityToChildCollectionSelector <T, TChild>(cSpaceType); var childQ = q.SelectMany(lambdaExpression); childQ = ApplyChildCollectionModifiers(childQ, childCollectionModifiers); var dict = childQ.AsNoTracking().ToLookup(fkGetter); var list = parents.Cast <T>().ToList(); foreach (var parent in list) { var prop = pkGetter(parent); var childs = dict.Contains(prop) ? dict[prop].ToList() : new List <TChild>(); setter(parent, childs); } } }; return(new EFUQueryable <T>(query.AsNoTracking()).Include(e)); }