File: SqlClient\Query\TypeSource.cs
Project: ndp\fx\src\DLinq\Dlinq\System.Data.Linq.csproj (System.Data.Linq)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Linq;
using System.Reflection;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data.Linq.Provider;
using System.Diagnostics.CodeAnalysis;
 
namespace System.Data.Linq.SqlClient {
 
    /// <summary>
    /// Method used for dealing with dynamic types. The ClrType of SqlNode is the 
    /// statically known type originating in the source expression tree. For methods
    /// like GetType(), we need to know the dynamic type that will be constructed.
    /// </summary>
    internal static class TypeSource {
        private class Visitor : SqlVisitor {
            class UnwrapStack {
                public UnwrapStack(UnwrapStack last, bool unwrap) {
                    Last = last;
                    Unwrap = unwrap;
                }
                public UnwrapStack Last { get; private set; }
                public bool Unwrap { get; private set; }
            }
            UnwrapStack UnwrapSequences;
            internal SqlExpression sourceExpression;
            internal Type sourceType;
            [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification="These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
            internal override SqlNode Visit(SqlNode node) {
                if (node == null)
                    return null;
 
                sourceExpression = node as SqlExpression;
                if (sourceExpression != null) {
                    Type type = sourceExpression.ClrType;
                    UnwrapStack unwrap = this.UnwrapSequences;
                    while (unwrap != null) {
                        if (unwrap.Unwrap) {
                            type = TypeSystem.GetElementType(type);
                        }
                        unwrap = unwrap.Last;
                    }
                    sourceType = type;
                }
                if (sourceType != null && TypeSystem.GetNonNullableType(sourceType).IsValueType) {
                    return node; // Value types can't also have a dynamic type.
                }
                if (sourceType != null && TypeSystem.HasIEnumerable(sourceType)) {
                    return node; // Sequences can't be polymorphic.
                }
 
                switch (node.NodeType) {
                    case SqlNodeType.ScalarSubSelect:
                    case SqlNodeType.Multiset:
                    case SqlNodeType.Element:
                    case SqlNodeType.SearchedCase:
                    case SqlNodeType.ClientCase:
                    case SqlNodeType.SimpleCase:
                    case SqlNodeType.Member:
                    case SqlNodeType.DiscriminatedType:
                    case SqlNodeType.New:
                    case SqlNodeType.FunctionCall:
                    case SqlNodeType.MethodCall:
                    case SqlNodeType.Convert: // Object identity does not survive convert. It does survive Cast.
                        // Dig no further.
                        return node;
                    case SqlNodeType.TypeCase:
                        sourceType = ((SqlTypeCase)node).RowType.Type;
                        return node;
                    case SqlNodeType.Link:
                        sourceType = ((SqlLink)node).RowType.Type;
                        return node;
                    case SqlNodeType.Table:
                        sourceType = ((SqlTable)node).RowType.Type;
                        return node;
                    case SqlNodeType.Value:
                        SqlValue val = (SqlValue)node;
                        if (val.Value != null) {
                            // In some cases the ClrType of a Value node may
                            // differ from the actual runtime type of the value.
                            // Therefore, we ensure here that the correct type is set.
                            sourceType = val.Value.GetType();
                        }
                        return node;
                }
                return base.Visit(node);
            }
            internal override SqlSelect VisitSelect(SqlSelect select) {
                /*
                 * We're travelling through <expression> of something like:
                 * 
                 *  SELECT <expression>
                 *  FROM <alias>
                 *
                 * Inside the expression there may be a reference to <alias> that 
                 * represents the dynamic type that we're trying to discover.
                 * 
                 * In this case, the type relationship between AliasRef and Alias is
                 * T to IEnumerable<T>.
                 * 
                 * We need to remember to 'unpivot' the type of IEnumerable<T> to 
                 * get the correct dynamic type.
                 * 
                 * Since SELECTs may be nested, we use a stack of pivots.
                 * 
                 */
                this.UnwrapSequences = new UnwrapStack(this.UnwrapSequences, true);
                VisitExpression(select.Selection);
                this.UnwrapSequences = this.UnwrapSequences.Last;
                return select;
            }
            internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
                if (this.UnwrapSequences != null && this.UnwrapSequences.Unwrap) {
                    this.UnwrapSequences = new UnwrapStack(this.UnwrapSequences, false);
                    this.VisitAlias(aref.Alias);
                    this.UnwrapSequences = this.UnwrapSequences.Last;
                } else {
                    this.VisitAlias(aref.Alias);
                }
                return aref;
            }
            internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
                this.VisitColumn(cref.Column); // Travel through column references
                return cref;
            }
        }
 
        /// <summary>
        /// Get a MetaType that represents the dynamic type of the given node.
        /// </summary>
        internal static MetaType GetSourceMetaType(SqlNode node, MetaModel model) {
            Visitor v = new Visitor();
            v.Visit(node);
            Type type = v.sourceType;
            type = TypeSystem.GetNonNullableType(type); // Emulate CLR's behavior: strip nullability from type.
            return model.GetMetaType(type);
        }
 
        /// <summary>
        /// Retrieve the expression that will represent the _dynamic_ type of the
        /// given expression. This is either a SqlDiscriminatedType or a SqlValue
        /// of type Type.
        /// </summary>
        internal static SqlExpression GetTypeSource(SqlExpression expr) {
            Visitor v = new Visitor();
            v.Visit(expr);
            return v.sourceExpression;
        }
    }
}