Exemplo n.º 1
0
        /// <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);

            PropertyInfo 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);

                    var methodInfo = octx.GetType()
                                     .GetMethod("CreateObjectSet", new Type[] { });
                    if (methodInfo == null)
                    {
                        return;
                    }
                    dynamic dynamicSet = methodInfo
                                         .MakeGenericMethod(baseType)
                                         .Invoke(octx, new Object[] { });

                    var set = dynamicSet.OfType <T>() as ObjectQuery <T>;

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

                    if (q == null)
                    {
                        return;
                    }
                    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));
        }
 public EfuQueryable <T> Include(IncludeExecuter include)
 {
     _includes.Add(include);
     return(this);
 }