public static Expression CreateSelection(
        this QueryableProjectionScope scope,
        Expression source,
        Type sourceType)
    {
        MethodCallExpression selection = Expression.Call(
            typeof(Enumerable),
            nameof(Enumerable.Select),
            new[]
        {
            scope.RuntimeType,
            scope.RuntimeType
        },
            source,
            scope.CreateMemberInitLambda());

        if (sourceType.IsArray)
        {
            return(ToArray(scope, selection));
        }

        if (TryGetSetType(sourceType, out Type? setType))
        {
            return(ToSet(selection, setType));
        }

        return(ToList(scope, selection));
    }
    public static Expression CreateMemberInit(this QueryableProjectionScope scope)
    {
        if (scope.HasAbstractTypes())
        {
            Expression lastValue = Expression.Default(scope.RuntimeType);

            foreach (KeyValuePair <Type, Queue <MemberAssignment> > val in
                     scope.GetAbstractTypes())
            {
                NewExpression ctor       = Expression.New(val.Key);
                Expression    memberInit = Expression.MemberInit(ctor, val.Value);

                lastValue = Expression.Condition(
                    Expression.TypeIs(scope.Instance.Peek(), val.Key),
                    Expression.Convert(memberInit, scope.RuntimeType),
                    lastValue);
            }

            return(lastValue);
        }
        else
        {
            NewExpression ctor = Expression.New(scope.RuntimeType);
            return(Expression.MemberInit(ctor, scope.Level.Peek()));
        }
    }
 private static Expression ToList(QueryableProjectionScope scope, Expression source)
 {
     return(Expression.Call(
                typeof(Enumerable),
                nameof(Enumerable.ToList),
                new[]
     {
         scope.RuntimeType
     },
                source));
 }
    public static QueryableProjectionScope AddScope(
        this QueryableProjectionContext context,
        Type runtimeType)
    {
        var parameterName = "p" + context.Scopes.Count;
        var closure       =
            new QueryableProjectionScope(runtimeType, parameterName);

        context.Scopes.Push(closure);
        return(closure);
    }
 public static Expression CreateMemberInitLambda(this QueryableProjectionScope scope)
 {
     return(Expression.Lambda(scope.CreateMemberInit(), scope.Parameter));
 }
 public static Expression <Func <T, T> > Project <T>(this QueryableProjectionScope scope)
 {
     return((Expression <Func <T, T> >)scope.CreateMemberInitLambda());
 }