Improving ObjectQuery<T>.Include


Having spent some time using the sample from my previous post on ObjectQuery.Include, I’ve encountered a bug! It turns out that the code generates the wrong include string for


context.Customers.Include(c => c.Order.SubInclude(o=>o.OrderDetail))


The fix for this is a small change to the BuildString method to recurse up the MemberExpression if necessary. The updated code is below  – usual disclaimers apply!


    public static class ObjectQueryExtensions


    {


        public static ObjectQuery<TSource> Include<TSource,


                               TPropType>(this ObjectQuery<TSource> source,


                               Expression<Func<TSource,


                               TPropType>> propertySelector)


        {


            string includeString = BuildString(propertySelector);


            return source.Include(includeString);


        }


        private static string BuildString(Expression propertySelector)


        {


            switch (propertySelector.NodeType)


            {


                case ExpressionType.Lambda:


                    LambdaExpression lambdaExpression = (LambdaExpression)propertySelector;


                    return BuildString(lambdaExpression.Body);


 


                case ExpressionType.Quote:


                    UnaryExpression unaryExpression = (UnaryExpression)propertySelector;


                    return BuildString(unaryExpression.Operand);


 


                case ExpressionType.MemberAccess:


 


                    MemberExpression memberExpression = (MemberExpression)propertySelector;


                    MemberInfo propertyInfo = memberExpression.Member;


 


                    if (memberExpression.Expression is ParameterExpression)


                    {


                        return propertyInfo.Name;


                    }


                    else


                    {


                        // we’ve got a nested property (e.g. MyType.SomeProperty.SomeNestedProperty)


                        return BuildString(memberExpression.Expression) + “.” + propertyInfo.Name;


                    }


 


                case ExpressionType.Call:


                    MethodCallExpression methodCallExpression =


                            (MethodCallExpression)propertySelector;


                   // check that it’s a SubInclude cal


                    if (IsSubInclude(methodCallExpression.Method))l


                    {


                        // argument 0 is the expression to which the SubInclude is applied (this could


                        // be member access or another SubInclude)


                        // argument 1 is the expression to apply to get the included property


                        // Pass both to BuildString to get the full expression


                        return BuildString(methodCallExpression.Arguments[0]) + “.” +


                               BuildString(methodCallExpression.Arguments[1]);


                    }


                    // else drop out and throw


                    break;


            }


            throw new InvalidOperationException(“Expression must be a member expression or


                    an SubInclude call: ” + propertySelector.ToString());


 


        }


 


        private static readonly MethodInfo[] SubIncludeMethods;


        static ObjectQueryExtensions()


        {


            Type type = typeof(ObjectQueryExtensions);


            SubIncludeMethods =


                    type.GetMethods().Where(mi => mi.Name == “SubInclude”).ToArray();


        }


        private static bool IsSubInclude(MethodInfo methodInfo)


        {


            if (methodInfo.IsGenericMethod)


            {


                if (!methodInfo.IsGenericMethodDefinition)


                {


                    methodInfo = methodInfo.GetGenericMethodDefinition();


                }


            }


            return SubIncludeMethods.Contains(methodInfo);


        }


 


        public static TPropType SubInclude<TSource,


                    TPropType>(this EntityCollection<TSource> source,


                    Expression<Func<TSource, TPropType>> propertySelector)


            where TSource : class, IEntityWithRelationships


            where TPropType : class


        {


            throw new InvalidOperationException(“This method is only intended for use with


                    ObjectQueryExtensions.Include to generate expressions trees”);


                    // no actually using this – just want the expression!


        }


        public static TPropType SubInclude<TSource,


               TPropType>(this TSource source,


               Expression<Func<TSource, TPropType>> propertySelector)


            where TSource : class, IEntityWithRelationships


            where TPropType : class


        {


            throw new InvalidOperationException(“This method is only intended for use with


                    ObjectQueryExtensions.Include to generate expressions trees”);


                    // no actually using this – just want the expression!


        }


    }


Originally posted by Stuart Leeks on April 24th 2009 here.


 

Comments (0)