// end mike

        private Expression ExecuteProjection(ProjectionExpression projection, bool okayToDefer)
        {
            okayToDefer &= (receivingMember != null && policy.IsDeferLoaded(receivingMember));

            // parameterize query
            projection = (ProjectionExpression)policy.Mapping.Language.Parameterize(projection);

            if (scope != null)
            {
                // also convert references to outer alias to named values!  these become SQL parameters too
                projection = (ProjectionExpression)OuterParameterizer.Parameterize(scope.Alias, projection);
            }

            var saveScope = scope;
            ParameterExpression reader = Expression.Parameter(typeof(DbDataReader), "r" + nReaders++);

            scope = new Scope(scope, reader, projection.Source.Alias, projection.Source.Columns);
            LambdaExpression projector = Expression.Lambda(Visit(projection.Projector), reader);

            scope = saveScope;
            List <string> columnNames = ColumnNamedGatherer.Gather(projector.Body);           //mike
            string        commandText = policy.Mapping.Language.Format(projection.Source);
            var           namedValues = NamedValueGatherer.Gather(projection.Source);
            var           names       = namedValues.Select(v => v.Name).ToArray();

            Expression[] values = namedValues.Select(v => Expression.Convert(
                                                         v.ParameterBindingAction != null ? v.ParameterBindingAction((ConstantExpression)v.Value) : Visit(v.Value),
                                                         typeof(object))).ToArray();

            string methExecute = okayToDefer
                                     ? "ExecuteDeferred"
                                     : "Execute";

            if (okayToDefer)
            {
            }

            // call low-level execute directly on supplied DbQueryProvider
            Expression result = Expression.Call(provider, methExecute, new[] { projector.Body.Type },
                                                Expression.New(
                                                    typeof(QueryCommand <>).MakeGenericType(projector.Body.Type).
                                                    GetConstructors()[0],
                                                    Expression.Constant(commandText),
                                                    Expression.Constant(names),
                                                    projector, Expression.Constant(columnNames)
                                                    ),
                                                Expression.NewArrayInit(typeof(object), values)
                                                );

            if (projection.Aggregator != null)
            {
                // apply aggregator
                result = DbExpressionReplacer.Replace(projection.Aggregator.Body, projection.Aggregator.Parameters[0],
                                                      result);
            }
            return(result);
        }
        protected override Expression VisitClientJoin(ClientJoinExpression join)
        {
            // convert client join into a up-front lookup table builder and replace client-join in tree with lookup accessor

            // 1) lookup = query.Select(e => new KVP(key: inner, value: e)).ToLookup(kvp => kvp.Key, kvp => kvp.Value)
            Expression innerKey = MakeJoinKey(join.InnerKey);
            Expression outerKey = MakeJoinKey(join.OuterKey);

            ConstructorInfo kvpConstructor =
                typeof(KeyValuePair <,>).MakeGenericType(innerKey.Type, join.Projection.Projector.Type).GetConstructor(
                    new[] { innerKey.Type, join.Projection.Projector.Type });
            Expression           constructKVPair = Expression.New(kvpConstructor, innerKey, join.Projection.Projector);
            ProjectionExpression newProjection   = new ProjectionExpression(join.Projection.Source, constructKVPair);

            int        iLookup   = ++nLookup;
            Expression execution = ExecuteProjection(newProjection, false);

            ParameterExpression kvp = Expression.Parameter(constructKVPair.Type, "kvp");

            // filter out nulls
            if (join.Projection.Projector.NodeType == (ExpressionType)DbExpressionType.OuterJoined)
            {
                LambdaExpression pred = Expression.Lambda(
                    Expression.NotEqual(
                        Expression.PropertyOrField(kvp, "Value"),
                        Expression.Constant(null, join.Projection.Projector.Type)
                        ),
                    kvp
                    );
                execution = Expression.Call(typeof(Enumerable), "Where", new[] { kvp.Type }, execution, pred);
            }

            // make lookup
            LambdaExpression keySelector     = Expression.Lambda(Expression.PropertyOrField(kvp, "Key"), kvp);
            LambdaExpression elementSelector = Expression.Lambda(Expression.PropertyOrField(kvp, "Value"), kvp);
            Expression       toLookup        = Expression.Call(typeof(Enumerable), "ToLookup",
                                                               new[] { kvp.Type, outerKey.Type, join.Projection.Projector.Type },
                                                               execution, keySelector, elementSelector);

            // 2) agg(lookup[outer])
            ParameterExpression lookup   = Expression.Parameter(toLookup.Type, "lookup" + iLookup);
            PropertyInfo        property = lookup.Type.GetProperty("Item");
            Expression          access   = Expression.Call(lookup, property.GetGetMethod(), Visit(outerKey));

            if (join.Projection.Aggregator != null)
            {
                // apply aggregator
                access = DbExpressionReplacer.Replace(join.Projection.Aggregator.Body,
                                                      join.Projection.Aggregator.Parameters[0], access);
            }

            variables.Add(lookup);
            initializers.Add(toLookup);

            return(access);
        }