예제 #1
0
        /// <summary>
        ///     Use this to select only specific fields, instead fetching the whole entity.
        /// </summary>
        /// <example>
        ///     <code>
        /// var query =
        ///     YOUR_QUERYABLE_OBJECT
        ///     .SelectDynamicFields(new List<string />(){
        ///         "Property1",
        ///         "Property2",
        ///     });
        ///
        /// dynamic FirstObj = query.FirstOrDefault();
        /// Console.WriteLine(FirstObj.Property1); //Name of the member will validated in runtime!
        /// </code>
        /// </example>
        /// <typeparam name="T">Type of Source IQueryable</typeparam>
        /// <param name="source">Source IQueryable</param>
        /// <param name="propertyNames">List of Property-Names you want to Select</param>
        /// <returns>A dynamic IQueryable Object. The object includes all Property-Names you have given as Fields.</returns>
        public static IQueryable <dynamic> SelectPartially <T>(
            this IQueryable <T> source,
            IEnumerable <string> propertyNames)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            // Here we do something similar to
            //
            //  Select(source => new {
            //      property1 = source.property1,
            //      property2 = source.property2,
            //      [...]
            //  })
            //
            // We build here firstly the Expression needed by the Select-Method dynamicly.
            // Beyond this we build even the class dynamicly. The class includes only
            // the Properties we want to project. The difference is, that the class is
            // not an anonymous type. Its a "Type built in Runtime" using Reflection.Emit.

            // Prepare ParameterExpression refering to the source object
            var sourceItem = Expression.Parameter(source.ElementType, "t");

            // Get PropertyInfos from Source Object (Filter all Misspelled Property-Names)
            var sourceProperties =
                propertyNames.Where(name => source.ElementType.GetProperty(name) != null)
                .ToDictionary(name => name, name => source.ElementType.GetProperty(name));

            // Build dynamic a Class that includes the Fields (no inheritance, no interfaces)
            var dynamicType =
                DynamicTypeBuilder.GetDynamicType(
                    sourceProperties.Values.ToDictionary(f => f.Name, f => f.PropertyType),
                    typeof(object),
                    Type.EmptyTypes);

            // Create the Binding Expressions
            var bindings =
                dynamicType.GetFields()
                .Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name])))
                .OfType <MemberBinding>()
                .ToList();

            // Create the Projection
            var selector =
                Expression.Lambda <Func <T, dynamic> >(
                    Expression.MemberInit(Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings),
                    sourceItem);

            // Now Select and return the IQueryable object
            return(source.Select(selector));
        }
예제 #2
0
        /// <summary>
        ///     <para>
        ///         Selecting and including related entities, instead of using the "Include"-Method from EF.
        ///         With the "Include"-Method it is not possible to specify conditions what entities to include.
        ///         With this method you can specify exactly what entities to load, and how they will be filtered
        ///         or ordered.
        ///     </para>
        ///     <para>
        ///         Attention! This selection will use AsEnumerable(). This means, that the database will be
        ///         queried at this point of the chain!
        ///     </para>
        /// </summary>
        /// <typeparam name="T">Type of Source IQueryable</typeparam>
        /// <param name="source">Source IQueryable</param>
        /// <param name="includeExpessions">Lamda Expressions, defining what entities to include</param>
        /// <returns></returns>
        public static IQueryable <dynamic> SelectIncluding <T>(
            this IQueryable <T> source,
            IEnumerable <Expression <Func <T, object> > > includeExpessions)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            // Here we do something similar to this:

            // First, select into a anonymous type with all the related entity-collections you need.
            // The relation can be defined by every query you want.

            // var query = _dbSet
            //    .Select(mainEntity => new
            //    {
            //
            //        //The main object. We need this field to unwrap later.
            //        mainEntity,
            //
            //        //Example how to retrieve only the newest history entry
            //        newestHistoryEntry = mainEntity.HistoryEntries.OrderByDescending(x => x.Timestamp).Take(1),
            //
            //        //Example how to order related entities
            //        itemSpecMSRPrices = mainEntity.OtherEntities.OrderBy(y => y.Something).ThenBy(y => y.SomeOtherThing),
            //
            //        //Example how to retrieve entities one level deeper
            //        secondLevel = mainEntity.CollectionWithRelations.Select(x => x.EntityCollectionOnSecondLevel),
            //
            //        //Of course you can order or subquery the deeper level
            //        //Here you should use SelectMany, to flatten the query
            //        secondLevelOrdered = mainEntity.CollectionWithRelations.SelectMany(x => x.EntityCollectionOnSecondLevel.OrderBy(y => y.Something).ThenBy(y => y.SomeOtherThing)),
            //
            //    });

            // Now we fire up the query (AsEnumerable) and then unwrap the SupplierItem out
            // of the anonymous type (Select).

            // return query.AsEnumerable().Select(mainEntity => mainEntity.mainEntity);

            // Because the ObjectContext have collected all the related entities, it have also linked each other correctly over
            // navigation-properties and reference-properties. Please read this Tip, too:
            // http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx

            // We build here firstly the Expression needed by the Select-Method dynamicly.
            // Beyond this we build even the class dynamicly. The class includes only
            // the Properties we want to project. The difference is, that the class is
            // not an anonymous type. Its a "Type built in Runtime" using Reflection.Emit.

            // Remark to the fields within the dynamic generated class:

            // All of them will be declared with the type really needed. So even the dynamicly
            // generated class will be "strong-type-safe".

            // Remark to the paramter "includeExpressions":

            // The method expect a collection of LambdaExpression-Objects. In fact we do not need
            // the LamdaExpression, but only the body of them. The LambdaExpression is only a
            // pleasant way to enable the user to define expression in a strong-type way.

            // Prepare ParameterExpression refering to the source object
            var sourceItem = Expression.Parameter(source.ElementType, "t");

            // Prepare helper class to replace the user-parameter of the LambdExpression with ours
            var paramRewriter = new PredicateRewriterVisitor(sourceItem);

            // Loop all expression and:
            //  1.) Determine returned type.
            //  2.) Get Body and replace the Parameter used by the user with ours
            //  2.) Give all of them a name and save them in a Dictionary
            var dynamicFields        = new Dictionary <string, Tuple <Expression, Type> >();
            var dynamicFieldsCounter = 0;

            foreach (var includeExpession in includeExpessions)
            {
                // Detect Type
                Type typeDetected;
                if ((includeExpession.Body.NodeType == ExpressionType.Convert) ||
                    (includeExpession.Body.NodeType == ExpressionType.ConvertChecked))
                {
                    var unary = includeExpession.Body as UnaryExpression;
                    if (unary != null)
                    {
                        typeDetected = unary.Operand.Type;
                    }
                }

                typeDetected = includeExpession.Body.Type;

                // Save into List
                dynamicFields.Add(
                    "f" + dynamicFieldsCounter,
                    new Tuple <Expression, Type>(
                        paramRewriter.ReplaceParameter(includeExpession.Body, includeExpession.Parameters[0]),
                        typeDetected));

                // Count
                dynamicFieldsCounter++;
            }

            // Add a field in which the source object will be saved
            dynamicFields.Add(
                "sourceObject",
                new Tuple <Expression, Type>(
                    sourceItem,
                    source.ElementType));

            // Build dynamic a Class that includes the Fields (no inheritance, no interfaces)
            var dynamicType =
                DynamicTypeBuilder.GetDynamicType(dynamicFields.ToDictionary(x => x.Key, x => x.Value.Item2),
                                                  typeof(object), Type.EmptyTypes);

            // reate the Binding Expressions
            var bindings =
                dynamicType.GetFields()
                .Select(p => Expression.Bind(p, dynamicFields[p.Name].Item1))
                .OfType <MemberBinding>()
                .ToList();

            // Create the Projection
            var selector =
                Expression.Lambda <Func <T, dynamic> >(
                    Expression.MemberInit(Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings),
                    sourceItem);

            return(source.Select(selector));
        }