File: SqlClient\Reader\ObjectReaderCompiler.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.Reflection.Emit;
using System.Data;
using System.Data.Common;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data.Linq.Provider;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
 
namespace System.Data.Linq.SqlClient {
    using System.Data.Linq.SqlClient.Implementation;
 
    using System.Diagnostics.CodeAnalysis;
    using System.Diagnostics;
#if ILGEN || DEBUG
    namespace Implementation {
        /// <summary>
        /// Internal interface type defining the operations dynamic materialization functions need to perform when
        /// materializing objects, without reflecting/invoking privates.
        /// <remarks>This interface is required because our anonymously hosted materialization delegates 
        /// run under partial trust and cannot access non-public members of types in the fully trusted 
        /// framework assemblies.</remarks>
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Materializer", Justification = "Spelling is correct.")]
        [SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors", Justification = "Unknown reason.")]
        public abstract class ObjectMaterializer<TDataReader> where TDataReader : DbDataReader {
            // These are public fields rather than properties for access speed
            [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
            public int[] Ordinals;
            [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Globals", Justification = "Spelling is correct.")]
            [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
            public object[] Globals;
            [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
            public object[] Locals;
            [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
            public object[] Arguments;
            [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
            public TDataReader DataReader;
            [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
            public DbDataReader BufferReader;
 
            public ObjectMaterializer() {
                DataReader = default(TDataReader);
            }
 
            public abstract object InsertLookup(int globalMetaType, object instance);
            public abstract void SendEntityMaterialized(int globalMetaType, object instance);
            public abstract IEnumerable ExecuteSubQuery(int iSubQuery, object[] args);
 
            [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
            public abstract IEnumerable<T> GetLinkSource<T>(int globalLink, int localFactory, object[] keyValues);
 
            [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
            public abstract IEnumerable<T> GetNestedLinkSource<T>(int globalLink, int localFactory, object instance);
            public abstract bool Read();
            public abstract bool CanDeferLoad { get; }
 
            [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
            [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
            public static IEnumerable<TOutput> Convert<TOutput>(IEnumerable source) {
                foreach (object value in source) {
                    yield return DBConvert.ChangeType<TOutput>(value);
                }
            }
 
            [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
            public static IGrouping<TKey, TElement> CreateGroup<TKey, TElement>(TKey key, IEnumerable<TElement> items) {
                return new ObjectReaderCompiler.Group<TKey, TElement>(key, items);
            }
 
            [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
            public static IOrderedEnumerable<TElement> CreateOrderedEnumerable<TElement>(IEnumerable<TElement> items) {
                return new ObjectReaderCompiler.OrderedResults<TElement>(items);
            }
 
            [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
            public static Exception ErrorAssignmentToNull(Type type) {
                return Error.CannotAssignNull(type);
            }
        }
    }
 
    internal class ObjectReaderCompiler : IObjectReaderCompiler {
        Type dataReaderType;
        IDataServices services;
 
        MethodInfo miDRisDBNull;
        MethodInfo miBRisDBNull;
        FieldInfo readerField;
        FieldInfo bufferReaderField;
 
        FieldInfo ordinalsField;
        FieldInfo globalsField;
        FieldInfo argsField;
 
#if DEBUG
        static AssemblyBuilder captureAssembly;
        static ModuleBuilder captureModule;
        static string captureAssemblyFilename;
        static int iCaptureId;
 
        internal static int GetNextId() {
            return iCaptureId++;
        }
 
        internal static ModuleBuilder CaptureModule {
            get { return captureModule; }
        }
 
        [ResourceExposure(ResourceScope.Machine)] // filename parameter later used by other methods.
        internal static void StartCaptureToFile(string filename) {
            if (captureAssembly == null) {
                string dir = System.IO.Path.GetDirectoryName(filename);
                if (dir.Length == 0) dir = null;
                string name = System.IO.Path.GetFileName(filename);
                AssemblyName assemblyName = new AssemblyName(System.IO.Path.GetFileNameWithoutExtension(name));
                captureAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, dir);
                captureModule = captureAssembly.DefineDynamicModule(name);
                captureAssemblyFilename = filename;
            }
        }
 
        [ResourceExposure(ResourceScope.None)] // Exposure is via StartCaptureToFile method.
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // Assembly.Save method call.
        internal static void StopCapture() {
            if (captureAssembly != null) {
                captureAssembly.Save(captureAssemblyFilename);
                captureAssembly = null;
            }
        }
 
        internal static void SetMaxReaderCacheSize(int size) {
            if (size <= 1) {
                throw Error.ArgumentOutOfRange("size");
            }
            maxReaderCacheSize = size;
        }
#endif
        static LocalDataStoreSlot cacheSlot = Thread.AllocateDataSlot();
        static int maxReaderCacheSize = 10;
 
        static ObjectReaderCompiler() {
        }
 
        internal ObjectReaderCompiler(Type dataReaderType, IDataServices services) {
            this.dataReaderType = dataReaderType;
            this.services = services;
 
            this.miDRisDBNull = dataReaderType.GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            this.miBRisDBNull = typeof(DbDataReader).GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
 
            Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
            this.ordinalsField = orbType.GetField("Ordinals", BindingFlags.Instance | BindingFlags.Public);
            this.globalsField = orbType.GetField("Globals", BindingFlags.Instance | BindingFlags.Public);
            this.argsField = orbType.GetField("Arguments", BindingFlags.Instance | BindingFlags.Public);
            this.readerField = orbType.GetField("DataReader", BindingFlags.Instance | BindingFlags.Public);
            this.bufferReaderField = orbType.GetField("BufferReader", BindingFlags.Instance | BindingFlags.Public);
 
            System.Diagnostics.Debug.Assert(
                this.miDRisDBNull != null &&
                this.miBRisDBNull != null &&
                this.readerField != null &&
                this.bufferReaderField != null &&
                this.ordinalsField != null &&
                this.globalsField != null &&
                this.argsField != null
            );
        }
 
        [ResourceExposure(ResourceScope.None)] // Consumed by Thread.AllocateDataSource result being unique.
        [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)] // Thread.GetData method call.
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
        public IObjectReaderFactory Compile(SqlExpression expression, Type elementType) {
            object mapping = this.services.Context.Mapping.Identity;
            DataLoadOptions options = this.services.Context.LoadOptions;
            IObjectReaderFactory factory = null;
            ReaderFactoryCache cache = null;
            bool canBeCompared = SqlProjectionComparer.CanBeCompared(expression);
            if (canBeCompared) {
                cache = (ReaderFactoryCache)Thread.GetData(cacheSlot);
                if (cache == null) {
                    cache = new ReaderFactoryCache(maxReaderCacheSize);
                    Thread.SetData(cacheSlot, cache);
                }
                factory = cache.GetFactory(elementType, this.dataReaderType, mapping, options, expression);
            }
            if (factory == null) {
                Generator gen = new Generator(this, elementType);
#if DEBUG
                if (ObjectReaderCompiler.CaptureModule != null) {
                    this.CompileCapturedMethod(gen, expression, elementType);
                }
#endif
                DynamicMethod dm = this.CompileDynamicMethod(gen, expression, elementType);
                Type fnMatType = typeof(Func<,>).MakeGenericType(typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType), elementType);
                var fnMaterialize = (Delegate)dm.CreateDelegate(fnMatType);
 
                Type factoryType = typeof(ObjectReaderFactory<,>).MakeGenericType(this.dataReaderType, elementType);
                factory = (IObjectReaderFactory)Activator.CreateInstance(
                    factoryType, BindingFlags.Instance | BindingFlags.NonPublic, null,
                    new object[] { fnMaterialize, gen.NamedColumns, gen.Globals, gen.Locals }, null
                    );
 
                if (canBeCompared) {
                    expression = new SourceExpressionRemover().VisitExpression(expression);
                    cache.AddFactory(elementType, this.dataReaderType, mapping, options, expression, factory);
                }
            }
            return factory;
        }
 
        private class SourceExpressionRemover : SqlDuplicator.DuplicatingVisitor {
            internal SourceExpressionRemover()
                : base(true) {
            }
            internal override SqlNode Visit(SqlNode node) {
                node = base.Visit(node);
                if (node != null) {
                    node.ClearSourceExpression();
                }
                return node;
            }
            internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
                SqlExpression result = base.VisitColumnRef(cref);
                if (result != null && result == cref) {
                    // reference to outer scope, don't propogate references to expressions or aliases
                    SqlColumn col = cref.Column;
                    SqlColumn newcol = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression);
                    newcol.Ordinal = col.Ordinal;
                    result = new SqlColumnRef(newcol);
                    newcol.ClearSourceExpression();
                }
                return result;
            }
            internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
                SqlExpression result = base.VisitAliasRef(aref);
                if (result != null && result == aref) {
                    // reference to outer scope, don't propogate references to expressions or aliases
                    SqlAlias alias = aref.Alias;
                    SqlAlias newalias = new SqlAlias(new SqlNop(aref.ClrType, aref.SqlType, null));
                    return new SqlAliasRef(newalias);
                }
                return result;
            }
        }
 
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
        public IObjectReaderSession CreateSession(DbDataReader reader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
            Type sessionType = typeof(ObjectReaderSession<>).MakeGenericType(this.dataReaderType);
            return (IObjectReaderSession)Activator.CreateInstance(sessionType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
                new object[] { reader, provider, parentArgs, userArgs, subQueries }, null);
        }
 
#if DEBUG
        private void CompileCapturedMethod(Generator gen, SqlExpression expression, Type elementType) {
            TypeBuilder tb = ObjectReaderCompiler.CaptureModule.DefineType("reader_type_" + ObjectReaderCompiler.GetNextId());
            MethodBuilder mb = tb.DefineMethod(
                "Read_" + elementType.Name,
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard,
                elementType,
                new Type[] { typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType) }
                );
            gen.GenerateBody(mb.GetILGenerator(), (SqlExpression)SqlDuplicator.Copy(expression));
            tb.CreateType();
        }
#endif
 
        private DynamicMethod CompileDynamicMethod(Generator gen, SqlExpression expression, Type elementType) {
            Type objectReaderType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
            DynamicMethod dm = new DynamicMethod(
                "Read_" + elementType.Name,
                elementType,
                new Type[] { objectReaderType },
                true
                );
            gen.GenerateBody(dm.GetILGenerator(), expression);
            return dm;
        }
 
        class ReaderFactoryCache {
            int maxCacheSize;
            LinkedList<CacheInfo> list;
 
            class CacheInfo {
                internal Type elementType;
                internal Type dataReaderType;
                internal object mapping;
                internal DataLoadOptions options;
                internal SqlExpression projection;
                internal IObjectReaderFactory factory;
                public CacheInfo(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
                    this.elementType = elementType;
                    this.dataReaderType = dataReaderType;
                    this.options = options;
                    this.mapping = mapping;
                    this.projection = projection;
                    this.factory = factory;
                }
            }
 
            internal ReaderFactoryCache(int maxCacheSize) {
                this.maxCacheSize = maxCacheSize;
                this.list = new LinkedList<CacheInfo>();
            }
 
            internal IObjectReaderFactory GetFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection) {
                for (LinkedListNode<CacheInfo> info = this.list.First; info != null; info = info.Next) {
                    if (elementType == info.Value.elementType &&
                        dataReaderType == info.Value.dataReaderType &&
                        mapping == info.Value.mapping &&
                        DataLoadOptions.ShapesAreEquivalent(options, info.Value.options) &&
                        SqlProjectionComparer.AreSimilar(projection, info.Value.projection)
                        ) {
                        // move matching item to head of list to reset its lifetime
                        this.list.Remove(info);
                        this.list.AddFirst(info);
                        return info.Value.factory;
                    }
                }
                return null;
            }
 
            internal void AddFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
                this.list.AddFirst(new LinkedListNode<CacheInfo>(new CacheInfo(elementType, dataReaderType, mapping, options, projection, factory)));
                if (this.list.Count > this.maxCacheSize) {
                    this.list.RemoveLast();
                }
            }
        }
 
        internal class SqlProjectionComparer {
            [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 static bool CanBeCompared(SqlExpression node) {
                if (node == null) {
                    return true;
                }
                switch (node.NodeType) {
                    case SqlNodeType.New: {
                            SqlNew new1 = (SqlNew)node;
                            for (int i = 0, n = new1.Args.Count; i < n; i++) {
                                if (!CanBeCompared(new1.Args[i])) {
                                    return false;
                                }
                            }
                            for (int i = 0, n = new1.Members.Count; i < n; i++) {
                                if (!CanBeCompared(new1.Members[i].Expression)) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.ColumnRef:
                    case SqlNodeType.Value:
                    case SqlNodeType.UserColumn:
                        return true;
                    case SqlNodeType.Link: {
                            SqlLink l1 = (SqlLink)node;
                            for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
                                if (!CanBeCompared(l1.KeyExpressions[i])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.OptionalValue:
                        return CanBeCompared(((SqlOptionalValue)node).Value);
                    case SqlNodeType.ValueOf:
                    case SqlNodeType.OuterJoinedValue:
                        return CanBeCompared(((SqlUnary)node).Operand);
                    case SqlNodeType.Lift:
                        return CanBeCompared(((SqlLift)node).Expression);
                    case SqlNodeType.Grouping: {
                            SqlGrouping g1 = (SqlGrouping)node;
                            return CanBeCompared(g1.Key) && CanBeCompared(g1.Group);
                        }
                    case SqlNodeType.ClientArray: {
                            if (node.SourceExpression.NodeType != ExpressionType.NewArrayInit &&
                                node.SourceExpression.NodeType != ExpressionType.NewArrayBounds) {
                                    return false;
                            }
                            SqlClientArray a1 = (SqlClientArray)node;
                            for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
                                if (!CanBeCompared(a1.Expressions[i])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.ClientCase: {
                            SqlClientCase c1 = (SqlClientCase)node;
                            for (int i = 0, n = c1.Whens.Count; i < n; i++) {
                                if (!CanBeCompared(c1.Whens[i].Match) ||
                                    !CanBeCompared(c1.Whens[i].Value)) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.SearchedCase: {
                            SqlSearchedCase c1 = (SqlSearchedCase)node;
                            for (int i = 0, n = c1.Whens.Count; i < n; i++) {
                                if (!CanBeCompared(c1.Whens[i].Match) ||
                                    !CanBeCompared(c1.Whens[i].Value)) {
                                    return false;
                                }
                            }
                            return CanBeCompared(c1.Else);
                        }
                    case SqlNodeType.TypeCase: {
                            SqlTypeCase c1 = (SqlTypeCase)node;
                            if (!CanBeCompared(c1.Discriminator)) {
                                return false;
                            }
                            for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
                                if (!CanBeCompared(c1.Whens[i].Match)) {
                                    return false;
                                }
                                if (!CanBeCompared(c1.Whens[i].TypeBinding)) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.DiscriminatedType:
                        return CanBeCompared(((SqlDiscriminatedType)node).Discriminator);
                    case SqlNodeType.JoinedCollection: {
                            SqlJoinedCollection j1 = (SqlJoinedCollection)node;
                            return CanBeCompared(j1.Count) && CanBeCompared(j1.Expression);
                        }
                    case SqlNodeType.Member:
                        return CanBeCompared(((SqlMember)node).Expression);
                    case SqlNodeType.MethodCall: {
                            SqlMethodCall mc = (SqlMethodCall)node;
                            if (mc.Object != null && !CanBeCompared(mc.Object)) {
                                return false;
                            }
                            for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
                                if (!CanBeCompared(mc.Arguments[0])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.ClientQuery:
                        return true;
                    case SqlNodeType.ClientParameter:
                    default:
                        return false;
                }
            }
 
            [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", 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.")]
            [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 static bool AreSimilar(SqlExpression node1, SqlExpression node2) {
                if (node1 == node2) {
                    return true;
                }
                if (node1 == null || node2 == null) {
                    return false;
                }
                if (node1.NodeType != node2.NodeType ||
                    node1.ClrType != node2.ClrType ||
                    node1.SqlType != node2.SqlType) {
                    return false;
                }
                switch (node1.NodeType) {
                    case SqlNodeType.New: {
                            SqlNew new1 = (SqlNew)node1;
                            SqlNew new2 = (SqlNew)node2;
                            if (new1.Args.Count != new2.Args.Count ||
                                new1.Members.Count != new2.Members.Count) {
                                return false;
                            }
                            for (int i = 0, n = new1.Args.Count; i < n; i++) {
                                if (!AreSimilar(new1.Args[i], new2.Args[i])) {
                                    return false;
                                }
                            }
                            for (int i = 0, n = new1.Members.Count; i < n; i++) {
                                if (!MetaPosition.AreSameMember(new1.Members[i].Member, new2.Members[i].Member) ||
                                    !AreSimilar(new1.Members[i].Expression, new2.Members[i].Expression)) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.ColumnRef: {
                            SqlColumnRef cref1 = (SqlColumnRef)node1;
                            SqlColumnRef cref2 = (SqlColumnRef)node2;
                            return cref1.Column.Ordinal == cref2.Column.Ordinal;
                        }
                    case SqlNodeType.Link: {
                            SqlLink l1 = (SqlLink)node1;
                            SqlLink l2 = (SqlLink)node2;
                            if (!MetaPosition.AreSameMember(l1.Member.Member, l2.Member.Member)) {
                                return false;
                            }
                            if (l1.KeyExpressions.Count != l2.KeyExpressions.Count) {
                                return false;
                            }
                            for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
                                if (!AreSimilar(l1.KeyExpressions[i], l2.KeyExpressions[i])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.Value:
                        return Object.Equals(((SqlValue)node1).Value, ((SqlValue)node2).Value);
                    case SqlNodeType.OptionalValue: {
                            SqlOptionalValue ov1 = (SqlOptionalValue)node1;
                            SqlOptionalValue ov2 = (SqlOptionalValue)node2;
                            return AreSimilar(ov1.Value, ov2.Value);
                        }
                    case SqlNodeType.ValueOf:
                    case SqlNodeType.OuterJoinedValue:
                        return AreSimilar(((SqlUnary)node1).Operand, ((SqlUnary)node2).Operand);
                    case SqlNodeType.Lift:
                        return AreSimilar(((SqlLift)node1).Expression, ((SqlLift)node2).Expression);
                    case SqlNodeType.Grouping: {
                            SqlGrouping g1 = (SqlGrouping)node1;
                            SqlGrouping g2 = (SqlGrouping)node2;
                            return AreSimilar(g1.Key, g2.Key) && AreSimilar(g1.Group, g2.Group);
                        }
                    case SqlNodeType.ClientArray: {
                            SqlClientArray a1 = (SqlClientArray)node1;
                            SqlClientArray a2 = (SqlClientArray)node2;
                            if (a1.Expressions.Count != a2.Expressions.Count) {
                                return false;
                            }
                            for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
                                if (!AreSimilar(a1.Expressions[i], a2.Expressions[i])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.UserColumn:
                        return ((SqlUserColumn)node1).Name == ((SqlUserColumn)node2).Name;
                    case SqlNodeType.ClientCase: {
                            SqlClientCase c1 = (SqlClientCase)node1;
                            SqlClientCase c2 = (SqlClientCase)node2;
                            if (c1.Whens.Count != c2.Whens.Count) {
                                return false;
                            }
                            for (int i = 0, n = c1.Whens.Count; i < n; i++) {
                                if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
                                    !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value)) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.SearchedCase: {
                            SqlSearchedCase c1 = (SqlSearchedCase)node1;
                            SqlSearchedCase c2 = (SqlSearchedCase)node2;
                            if (c1.Whens.Count != c2.Whens.Count) {
                                return false;
                            }
                            for (int i = 0, n = c1.Whens.Count; i < n; i++) {
                                if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
                                    !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value))
                                    return false;
                            }
                            return AreSimilar(c1.Else, c2.Else);
                        }
                    case SqlNodeType.TypeCase: {
                            SqlTypeCase c1 = (SqlTypeCase)node1;
                            SqlTypeCase c2 = (SqlTypeCase)node2;
                            if (!AreSimilar(c1.Discriminator, c2.Discriminator)) {
                                return false;
                            }
                            if (c1.Whens.Count != c2.Whens.Count) {
                                return false;
                            }
                            for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
                                if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match)) {
                                    return false;
                                }
                                if (!AreSimilar(c1.Whens[i].TypeBinding, c2.Whens[i].TypeBinding)) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.DiscriminatedType: {
                            SqlDiscriminatedType dt1 = (SqlDiscriminatedType)node1;
                            SqlDiscriminatedType dt2 = (SqlDiscriminatedType)node2;
                            return AreSimilar(dt1.Discriminator, dt2.Discriminator);
                        }
                    case SqlNodeType.JoinedCollection: {
                            SqlJoinedCollection j1 = (SqlJoinedCollection)node1;
                            SqlJoinedCollection j2 = (SqlJoinedCollection)node2;
                            return AreSimilar(j1.Count, j2.Count) && AreSimilar(j1.Expression, j2.Expression);
                        }
                    case SqlNodeType.Member: {
                            SqlMember m1 = (SqlMember)node1;
                            SqlMember m2 = (SqlMember)node2;
                            return m1.Member == m2.Member && AreSimilar(m1.Expression, m2.Expression);
                        }
                    case SqlNodeType.ClientQuery: {
                            SqlClientQuery cq1 = (SqlClientQuery)node1;
                            SqlClientQuery cq2 = (SqlClientQuery)node2;
                            if (cq1.Arguments.Count != cq2.Arguments.Count) {
                                return false;
                            }
                            for (int i = 0, n = cq1.Arguments.Count; i < n; i++) {
                                if (!AreSimilar(cq1.Arguments[i], cq2.Arguments[i])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.MethodCall: {
                            SqlMethodCall mc1 = (SqlMethodCall)node1;
                            SqlMethodCall mc2 = (SqlMethodCall)node2;
                            if (mc1.Method != mc2.Method || !AreSimilar(mc1.Object, mc2.Object)) {
                                return false;
                            }
                            if (mc1.Arguments.Count != mc2.Arguments.Count) {
                                return false;
                            }
                            for (int i = 0, n = mc1.Arguments.Count; i < n; i++) {
                                if (!AreSimilar(mc1.Arguments[i], mc2.Arguments[i])) {
                                    return false;
                                }
                            }
                            return true;
                        }
                    case SqlNodeType.ClientParameter:
                    default:
                        return false;
                }
            }
        }
 
        class SideEffectChecker : SqlVisitor {
            bool hasSideEffect;
 
            internal bool HasSideEffect(SqlNode node) {
                this.hasSideEffect = false;
                this.Visit(node);
                return this.hasSideEffect;
            }
 
            internal override SqlExpression VisitJoinedCollection(SqlJoinedCollection jc) {
                this.hasSideEffect = true;
                return jc;
            }
 
            internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
                return cq;
            }
        }
 
        [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unknown reason.")]
        class Generator {
            ObjectReaderCompiler compiler;
            ILGenerator gen;
            List<object> globals;
            List<NamedColumn> namedColumns;
            LocalBuilder locDataReader;
            Type elementType;
            int nLocals;
            Dictionary<MetaAssociation, int> associationSubQueries;
            SideEffectChecker sideEffectChecker = new SideEffectChecker();
 
            internal Generator(ObjectReaderCompiler compiler, Type elementType) {
                this.compiler = compiler;
                this.elementType = elementType;
                this.associationSubQueries = new Dictionary<MetaAssociation,int>();
            }
 
            internal void GenerateBody(ILGenerator generator, SqlExpression expression) {
                this.gen = generator;
                this.globals = new List<object>();
                this.namedColumns = new List<NamedColumn>();
                // prepare locDataReader
                this.locDataReader = generator.DeclareLocal(this.compiler.dataReaderType);
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldfld, this.compiler.readerField);
                generator.Emit(OpCodes.Stloc, this.locDataReader);
 
                this.GenerateExpressionForType(expression, this.elementType);
 
                generator.Emit(OpCodes.Ret);
            }
 
            internal object[] Globals {
                get { return this.globals.ToArray(); }
            }
 
            internal NamedColumn[] NamedColumns {
                get { return this.namedColumns.ToArray(); }
            }
 
            internal int Locals {
                get { return this.nLocals; }
            }
 
#if DEBUG
            private int stackDepth;
#endif
 
            private Type Generate(SqlNode node) {
                return this.Generate(node, null);
            }
 
            [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
            [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.")]
            private Type Generate(SqlNode node, LocalBuilder locInstance) {
#if DEBUG
                try {
                    stackDepth++;
                    System.Diagnostics.Debug.Assert(stackDepth < 500);
#endif
                    switch (node.NodeType) {
                        case SqlNodeType.New:
                            return this.GenerateNew((SqlNew)node);
                        case SqlNodeType.ColumnRef:
                            return this.GenerateColumnReference((SqlColumnRef)node);
                        case SqlNodeType.ClientQuery:
                            return this.GenerateClientQuery((SqlClientQuery)node, locInstance);
                        case SqlNodeType.JoinedCollection:
                            return this.GenerateJoinedCollection((SqlJoinedCollection)node);
                        case SqlNodeType.Link:
                            return this.GenerateLink((SqlLink)node, locInstance);
                        case SqlNodeType.Value:
                            return this.GenerateValue((SqlValue)node);
                        case SqlNodeType.ClientParameter:
                            return this.GenerateClientParameter((SqlClientParameter)node);
                        case SqlNodeType.ValueOf:
                            return this.GenerateValueOf((SqlUnary)node);
                        case SqlNodeType.OptionalValue:
                            return this.GenerateOptionalValue((SqlOptionalValue)node);
                        case SqlNodeType.OuterJoinedValue:
                            return this.Generate(((SqlUnary)node).Operand);
                        case SqlNodeType.Lift:
                            return this.GenerateLift((SqlLift)node);
                        case SqlNodeType.Grouping:
                            return this.GenerateGrouping((SqlGrouping)node);
                        case SqlNodeType.ClientArray:
                            return this.GenerateClientArray((SqlClientArray)node);
                        case SqlNodeType.UserColumn:
                            return this.GenerateUserColumn((SqlUserColumn)node);
                        case SqlNodeType.ClientCase:
                            return this.GenerateClientCase((SqlClientCase)node, false, locInstance);
                        case SqlNodeType.SearchedCase:
                            return this.GenerateSearchedCase((SqlSearchedCase)node);
                        case SqlNodeType.TypeCase:
                            return this.GenerateTypeCase((SqlTypeCase)node);
                        case SqlNodeType.DiscriminatedType:
                            return this.GenerateDiscriminatedType((SqlDiscriminatedType)node);
                        case SqlNodeType.Member:
                            return this.GenerateMember((SqlMember)node);
                        case SqlNodeType.MethodCall:
                            return this.GenerateMethodCall((SqlMethodCall)node);
                        default:
                            throw Error.CouldNotTranslateExpressionForReading(node.SourceExpression);
                    }
#if DEBUG
                }
                finally {
                    stackDepth--;
                }
#endif
            }
 
            private void GenerateAccessBufferReader() {
                gen.Emit(OpCodes.Ldarg_0);
                gen.Emit(OpCodes.Ldfld, this.compiler.bufferReaderField);
            }
 
            private void GenerateAccessDataReader() {
                gen.Emit(OpCodes.Ldloc, this.locDataReader);
            }
 
            private void GenerateAccessOrdinals() {
                gen.Emit(OpCodes.Ldarg_0);
                gen.Emit(OpCodes.Ldfld, this.compiler.ordinalsField);
            }
 
            private void GenerateAccessGlobals() {
                gen.Emit(OpCodes.Ldarg_0);
                gen.Emit(OpCodes.Ldfld, this.compiler.globalsField);
            }
 
            private void GenerateAccessArguments() {
                gen.Emit(OpCodes.Ldarg_0);
                gen.Emit(OpCodes.Ldfld, this.compiler.argsField);
            }
 
            private Type GenerateValue(SqlValue value) {
                return this.GenerateConstant(value.ClrType, value.Value);
            }
 
            private Type GenerateClientParameter(SqlClientParameter cp) {
                Delegate d = cp.Accessor.Compile();
                int iGlobal = this.AddGlobal(d.GetType(), d);
                this.GenerateGlobalAccess(iGlobal, d.GetType());
                this.GenerateAccessArguments();
                MethodInfo miInvoke = d.GetType().GetMethod(
                    "Invoke",
                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                    null,
                    new Type[] { typeof(object[]) },
                    null
                    );
                System.Diagnostics.Debug.Assert(miInvoke != null);
                gen.Emit(GetMethodCallOpCode(miInvoke), miInvoke);
                return d.Method.ReturnType;
            }
 
            private Type GenerateValueOf(SqlUnary u) {
                System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(u.Operand.ClrType));
                this.GenerateExpressionForType(u.Operand, u.Operand.ClrType);
                LocalBuilder loc = gen.DeclareLocal(u.Operand.ClrType);
                gen.Emit(OpCodes.Stloc, loc);
                gen.Emit(OpCodes.Ldloca, loc);
                this.GenerateGetValue(u.Operand.ClrType);
                return u.ClrType;
            }
 
            private Type GenerateOptionalValue(SqlOptionalValue opt) {
                System.Diagnostics.Debug.Assert(opt.HasValue.ClrType == typeof(int?));
 
                Label labIsNull = gen.DefineLabel();
                Label labExit = gen.DefineLabel();
 
                Type actualType = this.Generate(opt.HasValue);
                System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(actualType));
                LocalBuilder loc = gen.DeclareLocal(actualType);
                gen.Emit(OpCodes.Stloc, loc);
                gen.Emit(OpCodes.Ldloca, loc);
                this.GenerateHasValue(actualType);
                gen.Emit(OpCodes.Brfalse, labIsNull);
 
                this.GenerateExpressionForType(opt.Value, opt.ClrType);
                gen.Emit(OpCodes.Br_S, labExit);
 
                gen.MarkLabel(labIsNull);
                this.GenerateConstant(opt.ClrType, null);
 
                gen.MarkLabel(labExit);
                return opt.ClrType;
            }
 
            private Type GenerateLift(SqlLift lift) {
                return this.GenerateExpressionForType(lift.Expression, lift.ClrType);
            }
 
            private Type GenerateClientArray(SqlClientArray ca) {
                if (!ca.ClrType.IsArray) {
                    throw Error.CannotMaterializeList(ca.ClrType);
                }
                Type elemType = TypeSystem.GetElementType(ca.ClrType);
                this.GenerateConstInt(ca.Expressions.Count);
                gen.Emit(OpCodes.Newarr, elemType);
                for (int i = 0, n = ca.Expressions.Count; i < n; i++) {
                    gen.Emit(OpCodes.Dup);
                    this.GenerateConstInt(i);
                    this.GenerateExpressionForType(ca.Expressions[i], elemType);
                    this.GenerateArrayAssign(elemType);
                }
                return ca.ClrType;
            }
 
            private Type GenerateMember(SqlMember m) {
                FieldInfo fi = m.Member as FieldInfo;
                if (fi != null) {
                    this.GenerateExpressionForType(m.Expression, m.Expression.ClrType);
                    gen.Emit(OpCodes.Ldfld, fi);
                    return fi.FieldType;
                }
                else {
                    PropertyInfo pi = (PropertyInfo)m.Member;
                    return this.GenerateMethodCall(new SqlMethodCall(m.ClrType, m.SqlType, pi.GetGetMethod(), m.Expression, null, m.SourceExpression));
                }
            }
 
            private Type GenerateMethodCall(SqlMethodCall mc) {
                ParameterInfo[] pis = mc.Method.GetParameters();
                if (mc.Object != null) {
                    Type actualType = this.GenerateExpressionForType(mc.Object, mc.Object.ClrType);
                    if (actualType.IsValueType) {
                        LocalBuilder loc = gen.DeclareLocal(actualType);
                        gen.Emit(OpCodes.Stloc, loc);
                        gen.Emit(OpCodes.Ldloca, loc);
                    }
                }
                for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
                    ParameterInfo pi = pis[i];
                    Type pType = pi.ParameterType;
                    if (pType.IsByRef) {
                        pType = pType.GetElementType();
                        this.GenerateExpressionForType(mc.Arguments[i], pType);
                        LocalBuilder loc = gen.DeclareLocal(pType);
                        gen.Emit(OpCodes.Stloc, loc);
                        gen.Emit(OpCodes.Ldloca, loc);
                    }
                    else {
                        this.GenerateExpressionForType(mc.Arguments[i], pType);
                    }
                }
                OpCode callOpCode = GetMethodCallOpCode(mc.Method);
                if (mc.Object != null && TypeSystem.IsNullableType(mc.Object.ClrType) && callOpCode == OpCodes.Callvirt){
                    gen.Emit(OpCodes.Constrained, mc.Object.ClrType);
                }
                gen.Emit(callOpCode, mc.Method);
 
                return mc.Method.ReturnType;
            }
 
            /// <summary>
            /// Cannot use Call for virtual methods - it results in unverifiable code.  Ensure we're using the correct op code.
            /// </summary>
            private static OpCode GetMethodCallOpCode(MethodInfo mi) {
                return (mi.IsStatic || mi.DeclaringType.IsValueType) ? OpCodes.Call : OpCodes.Callvirt;
            }
 
            private Type GenerateNew(SqlNew sn) {
                LocalBuilder locInstance = gen.DeclareLocal(sn.ClrType);
                LocalBuilder locStoreInMember = null;
                Label labNewExit = gen.DefineLabel();
                Label labAlreadyCached = gen.DefineLabel();
 
                // read all arg values
                if (sn.Args.Count > 0) {
                    ParameterInfo[] pis = sn.Constructor.GetParameters();
                    for (int i = 0, n = sn.Args.Count; i < n; i++) {
                        this.GenerateExpressionForType(sn.Args[i], pis[i].ParameterType);
                    }
                }
 
                // construct the new instance
                if (sn.Constructor != null) {
                    gen.Emit(OpCodes.Newobj, sn.Constructor);
                    gen.Emit(OpCodes.Stloc, locInstance);
                }
                else if (sn.ClrType.IsValueType) {
                    gen.Emit(OpCodes.Ldloca, locInstance);
                    gen.Emit(OpCodes.Initobj, sn.ClrType);
                }
                else {
                    ConstructorInfo ci = sn.ClrType.GetConstructor(System.Type.EmptyTypes);
                    gen.Emit(OpCodes.Newobj, ci);
                    gen.Emit(OpCodes.Stloc, locInstance);
                }
 
                // read/write key bindings if there are any
                foreach (SqlMemberAssign ma in sn.Members.OrderBy(m => sn.MetaType.GetDataMember(m.Member).Ordinal)) {
                    MetaDataMember mm = sn.MetaType.GetDataMember(ma.Member);
                    if (mm.IsPrimaryKey) {
                        this.GenerateMemberAssignment(mm, locInstance, ma.Expression, null);
                    }
                }
 
                int iMeta = 0;
 
                if (sn.MetaType.IsEntity) {
                    LocalBuilder locCached = gen.DeclareLocal(sn.ClrType);
                    locStoreInMember = gen.DeclareLocal(typeof(bool));
                    Label labExit = gen.DefineLabel();
 
                    iMeta = this.AddGlobal(typeof(MetaType), sn.MetaType);
                    Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
 
                    // this.InsertLookup(metaType, locInstance)
                    gen.Emit(OpCodes.Ldarg_0);
                    this.GenerateConstInt(iMeta);
                    gen.Emit(OpCodes.Ldloc, locInstance);
                    MethodInfo miInsertLookup = orbType.GetMethod(
                        "InsertLookup",
                        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                        null,
                        new Type[] { typeof(int), typeof(object) },
                        null
                        );
 
                    System.Diagnostics.Debug.Assert(miInsertLookup != null);
                    gen.Emit(GetMethodCallOpCode(miInsertLookup), miInsertLookup);
                    gen.Emit(OpCodes.Castclass, sn.ClrType);
                    gen.Emit(OpCodes.Stloc, locCached);
 
                    // if cached != instance then already cached
                    gen.Emit(OpCodes.Ldloc, locCached);
                    gen.Emit(OpCodes.Ldloc, locInstance);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brfalse, labAlreadyCached);
 
                    this.GenerateConstInt(1);
                    gen.Emit(OpCodes.Stloc, locStoreInMember);
                    gen.Emit(OpCodes.Br_S, labExit);
 
                    gen.MarkLabel(labAlreadyCached);
                    gen.Emit(OpCodes.Ldloc, locCached);
                    gen.Emit(OpCodes.Stloc, locInstance);
                    // signal to not store loaded values in instance...
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Stloc, locStoreInMember);
 
                    gen.MarkLabel(labExit);
                }
 
                // read/write non-key bindings
                foreach (SqlMemberAssign ma in sn.Members.OrderBy(m => sn.MetaType.GetDataMember(m.Member).Ordinal)) {
                    MetaDataMember mm = sn.MetaType.GetDataMember(ma.Member);
                    if (!mm.IsPrimaryKey) {
                        this.GenerateMemberAssignment(mm, locInstance, ma.Expression, locStoreInMember);
                    }
                }
 
                if (sn.MetaType.IsEntity) {
                    // don't call SendEntityMaterialized if we already had the instance cached
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labNewExit);
 
                    // send entity materialized event
                    gen.Emit(OpCodes.Ldarg_0);
                    this.GenerateConstInt(iMeta);
                    gen.Emit(OpCodes.Ldloc, locInstance);
                    Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
                    MethodInfo miRaiseEvent = orbType.GetMethod(
                        "SendEntityMaterialized",
                        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                        null,
                        new Type[] { typeof(int), typeof(object) },
                        null
                        );
                    System.Diagnostics.Debug.Assert(miRaiseEvent != null);
                    gen.Emit(GetMethodCallOpCode(miRaiseEvent), miRaiseEvent);
                }
 
                gen.MarkLabel(labNewExit);
                gen.Emit(OpCodes.Ldloc, locInstance);
 
                return sn.ClrType;
            }
 
            private void GenerateMemberAssignment(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
                MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
                Type memberType = TypeSystem.GetMemberType(m);
 
                // check for deferrable member & deferred source expression
                if (IsDeferrableExpression(expr) &&
                    (this.compiler.services.Context.LoadOptions == null ||
                     !this.compiler.services.Context.LoadOptions.IsPreloaded(mm.Member))
                   ) {
                    // we can only defer deferrable members 
                    if (mm.IsDeferred) {
                        // determine at runtime if we are allowed to defer load 
                        gen.Emit(OpCodes.Ldarg_0);
                        Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
                        PropertyInfo piCanDeferLoad = orbType.GetProperty("CanDeferLoad");
                        System.Diagnostics.Debug.Assert(piCanDeferLoad != null);
                        MethodInfo miCanDeferLoad = piCanDeferLoad.GetGetMethod();
                        gen.Emit(GetMethodCallOpCode(miCanDeferLoad), miCanDeferLoad);
 
                        // if we can't defer load then jump over the code that does the defer loading
                        Label labEndDeferLoad = gen.DefineLabel();
                        gen.Emit(OpCodes.Brfalse, labEndDeferLoad);
 
                        // execute the defer load operation
                        if (memberType.IsGenericType) {
                            Type genType = memberType.GetGenericTypeDefinition();
                            if (genType == typeof(EntitySet<>)) {
                                this.GenerateAssignDeferredEntitySet(mm, locInstance, expr, locStoreInMember);
                            }
                            else if (genType == typeof(EntityRef<>) || genType == typeof(Link<>)) {
                                this.GenerateAssignDeferredReference(mm, locInstance, expr, locStoreInMember);
                            }
                            else {
                                throw Error.DeferredMemberWrongType();
                            }
                        }
                        else {
                            throw Error.DeferredMemberWrongType();
                        }
                        gen.MarkLabel(labEndDeferLoad);
                    }
                    else {
                        // behavior for non-deferred members w/ deferrable expressions is to load nothing
                    }
                }
                else if (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(EntitySet<>)) {
                    this.GenerateAssignEntitySet(mm, locInstance, expr, locStoreInMember);
                }
                else {
                    this.GenerateAssignValue(mm, locInstance, expr, locStoreInMember);
                }
            }
 
            private void GenerateAssignValue(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
                MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
                if (!IsAssignable(m)) {
                    throw Error.CannotAssignToMember(m.Name);
                }
                Type memberType = TypeSystem.GetMemberType(m);
 
                Label labExit = gen.DefineLabel();
 
                bool hasSideEffect = this.HasSideEffect(expr);
 
                if (locStoreInMember != null && !hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                this.GenerateExpressionForType(expr, memberType, mm.DeclaringType.IsEntity ? locInstance : null);
                LocalBuilder locValue = gen.DeclareLocal(memberType);
 
                gen.Emit(OpCodes.Stloc, locValue);
 
                if (locStoreInMember != null && hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                this.GenerateLoadForMemberAccess(locInstance);
                gen.Emit(OpCodes.Ldloc, locValue);
                this.GenerateStoreMember(m);
 
                gen.MarkLabel(labExit);
            }
 
            private static bool IsAssignable(MemberInfo member) {
                FieldInfo fi = member as FieldInfo;
                if (fi != null) {
                    return true;
                }
                PropertyInfo pi = member as PropertyInfo;
                if (pi != null) {
                    return pi.CanWrite;
                }
                return false;
            }
 
            private void GenerateAssignDeferredEntitySet(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
                MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
                Type memberType = TypeSystem.GetMemberType(m);
                System.Diagnostics.Debug.Assert(memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(EntitySet<>));
                Label labExit = gen.DefineLabel();
                Type argType = typeof(IEnumerable<>).MakeGenericType(memberType.GetGenericArguments());
 
                bool hasSideEffect = this.HasSideEffect(expr);
 
                if (locStoreInMember != null && !hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                Type eType = this.GenerateDeferredSource(expr, locInstance);
                System.Diagnostics.Debug.Assert(argType.IsAssignableFrom(eType));
                LocalBuilder locSource = gen.DeclareLocal(eType);
                gen.Emit(OpCodes.Stloc, locSource);
 
                if (locStoreInMember != null && hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                // if member is directly writeable, check for null entityset
                if (m is FieldInfo || (m is PropertyInfo && ((PropertyInfo)m).CanWrite)) {
                    Label labFetch = gen.DefineLabel();
                    this.GenerateLoadForMemberAccess(locInstance);
                    this.GenerateLoadMember(m);
                    gen.Emit(OpCodes.Ldnull);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brfalse, labFetch);
 
                    // create new entity set
                    this.GenerateLoadForMemberAccess(locInstance);
                    ConstructorInfo ci = memberType.GetConstructor(System.Type.EmptyTypes);
                    System.Diagnostics.Debug.Assert(ci != null);
                    gen.Emit(OpCodes.Newobj, ci);
                    this.GenerateStoreMember(m);
 
                    gen.MarkLabel(labFetch);
                }
 
                // set the source
                this.GenerateLoadForMemberAccess(locInstance);
                this.GenerateLoadMember(m);
                gen.Emit(OpCodes.Ldloc, locSource);
                MethodInfo miSetSource = memberType.GetMethod("SetSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { argType }, null);
                System.Diagnostics.Debug.Assert(miSetSource != null);
                gen.Emit(GetMethodCallOpCode(miSetSource), miSetSource);
 
                gen.MarkLabel(labExit);
            }
 
            private bool HasSideEffect(SqlNode node) {
                return this.sideEffectChecker.HasSideEffect(node);
            }
 
            private void GenerateAssignEntitySet(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
                MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
                Type memberType = TypeSystem.GetMemberType(m);
                System.Diagnostics.Debug.Assert(memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(EntitySet<>));
                Label labExit = gen.DefineLabel();
                Type argType = typeof(IEnumerable<>).MakeGenericType(memberType.GetGenericArguments());
 
                bool hasSideEffect = this.HasSideEffect(expr);
 
                if (locStoreInMember != null && !hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                Type eType = this.Generate(expr, mm.DeclaringType.IsEntity ? locInstance : null);
                System.Diagnostics.Debug.Assert(argType.IsAssignableFrom(eType));
                LocalBuilder locSource = gen.DeclareLocal(eType);
                gen.Emit(OpCodes.Stloc, locSource);
 
                if (locStoreInMember != null && hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                // if member is directly writeable, check for null entityset
                if (m is FieldInfo || (m is PropertyInfo && ((PropertyInfo)m).CanWrite)) {
                    Label labFetch = gen.DefineLabel();
                    this.GenerateLoadForMemberAccess(locInstance);
                    this.GenerateLoadMember(m);
                    gen.Emit(OpCodes.Ldnull);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brfalse, labFetch);
 
                    // create new entity set
                    this.GenerateLoadForMemberAccess(locInstance);
                    ConstructorInfo ci = memberType.GetConstructor(System.Type.EmptyTypes);
                    System.Diagnostics.Debug.Assert(ci != null);
                    gen.Emit(OpCodes.Newobj, ci);
                    this.GenerateStoreMember(m);
 
                    gen.MarkLabel(labFetch);
                }
 
                // set the source
                this.GenerateLoadForMemberAccess(locInstance);
                this.GenerateLoadMember(m);
                gen.Emit(OpCodes.Ldloc, locSource);
                MethodInfo miAssign = memberType.GetMethod("Assign", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { argType }, null);
                System.Diagnostics.Debug.Assert(miAssign != null);
                gen.Emit(GetMethodCallOpCode(miAssign), miAssign);
 
                gen.MarkLabel(labExit);
            }
 
            private void GenerateAssignDeferredReference(MetaDataMember mm, LocalBuilder locInstance, SqlExpression expr, LocalBuilder locStoreInMember) {
                MemberInfo m = mm.StorageMember != null ? mm.StorageMember : mm.Member;
                Type memberType = TypeSystem.GetMemberType(m);
                System.Diagnostics.Debug.Assert(
                    memberType.IsGenericType &&
                    (memberType.GetGenericTypeDefinition() == typeof(EntityRef<>) ||
                     memberType.GetGenericTypeDefinition() == typeof(Link<>))
                   );
                Label labExit = gen.DefineLabel();
                Type argType = typeof(IEnumerable<>).MakeGenericType(memberType.GetGenericArguments());
 
                bool hasSideEffect = this.HasSideEffect(expr);
 
                if (locStoreInMember != null && !hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                Type eType = this.GenerateDeferredSource(expr, locInstance);
                if (!argType.IsAssignableFrom(eType)) {
                    throw Error.CouldNotConvert(argType, eType);
                }
 
                LocalBuilder locSource = gen.DeclareLocal(eType);
                gen.Emit(OpCodes.Stloc, locSource);
 
                if (locStoreInMember != null && hasSideEffect) {
                    gen.Emit(OpCodes.Ldloc, locStoreInMember);
                    this.GenerateConstInt(0);
                    gen.Emit(OpCodes.Ceq);
                    gen.Emit(OpCodes.Brtrue, labExit);
                }
 
                this.GenerateLoadForMemberAccess(locInstance);
                gen.Emit(OpCodes.Ldloc, locSource);
                ConstructorInfo ci = memberType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { argType }, null);
                System.Diagnostics.Debug.Assert(ci != null);
                gen.Emit(OpCodes.Newobj, ci);
                this.GenerateStoreMember(m);
 
                gen.MarkLabel(labExit);
            }
 
            private void GenerateLoadForMemberAccess(LocalBuilder loc) {
                if (loc.LocalType.IsValueType) {
                    gen.Emit(OpCodes.Ldloca, loc);
                }
                else {
                    gen.Emit(OpCodes.Ldloc, loc);
                }
            }
 
            private bool IsDeferrableExpression(SqlExpression expr) {
                if (expr.NodeType == SqlNodeType.Link) {
                    return true;
                }
                else if (expr.NodeType == SqlNodeType.ClientCase) {
                    SqlClientCase c = (SqlClientCase)expr;
                    foreach (SqlClientWhen when in c.Whens) {
                        if (!IsDeferrableExpression(when.Value)) {
                            return false;
                        }
                    }
                    return true;
                }
                return false;
            }
 
            private Type GenerateGrouping(SqlGrouping grp) {
                Type[] typeArgs = grp.ClrType.GetGenericArguments();
 
                this.GenerateExpressionForType(grp.Key, typeArgs[0]);
                this.Generate(grp.Group);
 
                Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
                MethodInfo miCreateGroup = TypeSystem.FindStaticMethod(orbType, "CreateGroup", new Type[] { typeArgs[0], typeof(IEnumerable<>).MakeGenericType(typeArgs[1]) }, typeArgs);
                System.Diagnostics.Debug.Assert(miCreateGroup != null);
                gen.Emit(OpCodes.Call, miCreateGroup);
 
                return miCreateGroup.ReturnType;
            }
 
            private Type GenerateLink(SqlLink link, LocalBuilder locInstance) {
                gen.Emit(OpCodes.Ldarg_0);
 
                // iGlobalLink arg
                int iGlobalLink = this.AddGlobal(typeof(MetaDataMember), link.Member);
                this.GenerateConstInt(iGlobalLink);
 
                // iLocalFactory arg
                int iLocalFactory = this.AllocateLocal();
                this.GenerateConstInt(iLocalFactory);
 
                Type elemType = link.Member.IsAssociation && link.Member.Association.IsMany
                    ? TypeSystem.GetElementType(link.Member.Type)
                    : link.Member.Type;
 
                MethodInfo mi = null;
                if (locInstance != null) {
                    // load instance for 'instance' arg
                    gen.Emit(OpCodes.Ldloc, locInstance);
 
                    // call GetNestedLinkSource on ObjectReaderBase
                    mi = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType).GetMethod("GetNestedLinkSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    System.Diagnostics.Debug.Assert(mi != null);
                    MethodInfo miGLS = mi.MakeGenericMethod(elemType);
                    gen.Emit(GetMethodCallOpCode(miGLS), miGLS);
                }
                else {
                    // create array of key values for 'keyValues' arg
                    this.GenerateConstInt(link.KeyExpressions.Count);
                    gen.Emit(OpCodes.Newarr, typeof(object));
 
                    // intialize key values
                    for (int i = 0, n = link.KeyExpressions.Count; i < n; i++) {
                        gen.Emit(OpCodes.Dup);
                        this.GenerateConstInt(i);
                        this.GenerateExpressionForType(link.KeyExpressions[i], typeof(object));
                        this.GenerateArrayAssign(typeof(object));
                    }
 
                    // call GetLinkSource on ObjectReaderBase
                    mi = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType).GetMethod("GetLinkSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    System.Diagnostics.Debug.Assert(mi != null);
                    MethodInfo miGLS = mi.MakeGenericMethod(elemType);
                    gen.Emit(GetMethodCallOpCode(miGLS), miGLS);
                }
 
                return typeof(IEnumerable<>).MakeGenericType(elemType);
            }
 
            private Type GenerateDeferredSource(SqlExpression expr, LocalBuilder locInstance) {
                if (expr.NodeType == SqlNodeType.ClientCase) {
                    return this.GenerateClientCase((SqlClientCase)expr, true, locInstance);
                }
                else if (expr.NodeType == SqlNodeType.Link) {
                    return this.GenerateLink((SqlLink)expr, locInstance);
                }
                else {
                    throw Error.ExpressionNotDeferredQuerySource();
                }
            }
 
            private Type GenerateClientQuery(SqlClientQuery cq, LocalBuilder locInstance) {
                Type clientElementType = cq.Query.NodeType == SqlNodeType.Multiset ? TypeSystem.GetElementType(cq.ClrType) : cq.ClrType;
 
                gen.Emit(OpCodes.Ldarg_0); // ObjectReaderBase
                this.GenerateConstInt(cq.Ordinal); // iSubQuery
                
                // create array of subquery parent args
                this.GenerateConstInt(cq.Arguments.Count);
                gen.Emit(OpCodes.Newarr, typeof(object));
 
                // intialize arg values
                for (int i = 0, n = cq.Arguments.Count; i < n; i++) {
                    gen.Emit(OpCodes.Dup);
                    this.GenerateConstInt(i);
                    Type clrType = cq.Arguments[i].ClrType;
                    if (cq.Arguments[i].NodeType == SqlNodeType.ColumnRef) {
                        SqlColumnRef cref = (SqlColumnRef)cq.Arguments[i];
                        if (clrType.IsValueType && !TypeSystem.IsNullableType(clrType)) {
                            clrType = typeof(Nullable<>).MakeGenericType(clrType);
                        }
                        this.GenerateColumnAccess(clrType, cref.SqlType, cref.Column.Ordinal, null);
                    }
                    else {
                        this.GenerateExpressionForType(cq.Arguments[i], cq.Arguments[i].ClrType);
                    }
                    if (clrType.IsValueType) {
                        gen.Emit(OpCodes.Box, clrType);
                    }
                    this.GenerateArrayAssign(typeof(object));
                }
 
                MethodInfo miExecute = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType)
                    .GetMethod("ExecuteSubQuery", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                System.Diagnostics.Debug.Assert(miExecute != null);
                gen.Emit(GetMethodCallOpCode(miExecute), miExecute);
 
                Type actualType = typeof(IEnumerable<>).MakeGenericType(clientElementType);
                gen.Emit(OpCodes.Castclass, actualType);
 
                Type resultType = typeof(List<>).MakeGenericType(clientElementType);
                this.GenerateConvertToType(actualType, resultType);
 
                return resultType;
            }
 
            private Type GenerateJoinedCollection(SqlJoinedCollection jc) {
                LocalBuilder locCount = gen.DeclareLocal(typeof(int));
                LocalBuilder locHasRows = gen.DeclareLocal(typeof(bool));
                Type joinElementType = jc.Expression.ClrType;
                Type listType = typeof(List<>).MakeGenericType(joinElementType);
                LocalBuilder locList = gen.DeclareLocal(listType);
 
                // count = xxx
                this.GenerateExpressionForType(jc.Count, typeof(int));
                gen.Emit(OpCodes.Stloc, locCount);
 
                // list = new List<T>(count)
                gen.Emit(OpCodes.Ldloc, locCount);
                ConstructorInfo ci = listType.GetConstructor(new Type[] { typeof(int) });
                System.Diagnostics.Debug.Assert(ci != null);
                gen.Emit(OpCodes.Newobj, ci);
                gen.Emit(OpCodes.Stloc, locList);
 
                // hasRows = true
                gen.Emit(OpCodes.Ldc_I4_1);
                gen.Emit(OpCodes.Stloc, locHasRows);
 
                // start loop
                Label labLoopTest = gen.DefineLabel();
                Label labLoopTop = gen.DefineLabel();
                LocalBuilder locI = gen.DeclareLocal(typeof(int));
                gen.Emit(OpCodes.Ldc_I4_0);
                gen.Emit(OpCodes.Stloc, locI);
                gen.Emit(OpCodes.Br, labLoopTest);
 
                gen.MarkLabel(labLoopTop);
                // loop interior
 
                // if (i > 0 && hasRows) { hasRows = this.Read(); }
                gen.Emit(OpCodes.Ldloc, locI);
                gen.Emit(OpCodes.Ldc_I4_0);
                gen.Emit(OpCodes.Cgt);
                gen.Emit(OpCodes.Ldloc, locHasRows);
                gen.Emit(OpCodes.And);
                Label labNext = gen.DefineLabel();
                gen.Emit(OpCodes.Brfalse, labNext);
 
                // this.Read()
                gen.Emit(OpCodes.Ldarg_0);
                Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
                MethodInfo miRead = orbType.GetMethod("Read", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
                System.Diagnostics.Debug.Assert(miRead != null);
                gen.Emit(GetMethodCallOpCode(miRead), miRead);
                gen.Emit(OpCodes.Stloc, locHasRows);
 
                gen.MarkLabel(labNext);
                // if (hasRows) { list.Add(expr); }
                Label labNext2 = gen.DefineLabel();
                gen.Emit(OpCodes.Ldloc, locHasRows);
                gen.Emit(OpCodes.Brfalse, labNext2);
                gen.Emit(OpCodes.Ldloc, locList);
                this.GenerateExpressionForType(jc.Expression, joinElementType);
                MethodInfo miAdd = listType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { joinElementType }, null);
                System.Diagnostics.Debug.Assert(miAdd != null);
                gen.Emit(GetMethodCallOpCode(miAdd), miAdd);
 
                gen.MarkLabel(labNext2);
                // loop bottom
                // i = i + 1
                gen.Emit(OpCodes.Ldloc, locI);
                gen.Emit(OpCodes.Ldc_I4_1);
                gen.Emit(OpCodes.Add);
                gen.Emit(OpCodes.Stloc, locI);
 
                // loop test
                // i < count && hasRows
                gen.MarkLabel(labLoopTest);
                gen.Emit(OpCodes.Ldloc, locI);
                gen.Emit(OpCodes.Ldloc, locCount);
                gen.Emit(OpCodes.Clt);
                gen.Emit(OpCodes.Ldloc, locHasRows);
                gen.Emit(OpCodes.And);
                gen.Emit(OpCodes.Brtrue, labLoopTop);
 
                // return list;
                gen.Emit(OpCodes.Ldloc, locList);
 
                return listType;
            }
 
            private Type GenerateExpressionForType(SqlExpression expr, Type type) {
                return this.GenerateExpressionForType(expr, type, null);
            }
 
            private Type GenerateExpressionForType(SqlExpression expr, Type type, LocalBuilder locInstance) {
                Type actualType = this.Generate(expr, locInstance);
                this.GenerateConvertToType(actualType, type);
                return type;
            }
 
            private void GenerateConvertToType(Type actualType, Type expectedType, Type readerMethodType) {
                GenerateConvertToType(readerMethodType, actualType);
                GenerateConvertToType(actualType, expectedType);
            }
 
            [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", 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.")]
            [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.")]
            private void GenerateConvertToType(Type actualType, Type expectedType) {
                if (expectedType != actualType &&
                    !(!actualType.IsValueType && actualType.IsSubclassOf(expectedType))
                    ) {
                    Type genActualType = actualType.IsGenericType ? actualType.GetGenericTypeDefinition() : null;
                    Type genExpectedType = expectedType.IsGenericType ? expectedType.GetGenericTypeDefinition() : null;
                    Type[] genExpectedTypeArgs = genExpectedType != null ? expectedType.GetGenericArguments() : null;
 
                    Type elemType = TypeSystem.GetElementType(actualType);
                    Type seqType = TypeSystem.GetSequenceType(elemType);
                    bool actualIsSequence = seqType.IsAssignableFrom(actualType);
 
                    if (expectedType == typeof(object) && actualType.IsValueType) {
                        gen.Emit(OpCodes.Box, actualType);
                    }
                    else if (actualType == typeof(object) && expectedType.IsValueType) {
                        gen.Emit(OpCodes.Unbox_Any, expectedType);
                    }
                    // is one type an explicit subtype of the other?
                    else if ((actualType.IsSubclassOf(expectedType) || expectedType.IsSubclassOf(actualType))
                        && !actualType.IsValueType && !expectedType.IsValueType) {
                        // (T)expr
                        gen.Emit(OpCodes.Castclass, expectedType);
                    }
                    // do we expected a sequence of a different element type?
                    else if (genExpectedType == typeof(IEnumerable<>) && actualIsSequence) {
                        if (elementType.IsInterface ||
                            genExpectedTypeArgs[0].IsInterface ||
                            elementType.IsSubclassOf(genExpectedTypeArgs[0]) ||
                            genExpectedTypeArgs[0].IsSubclassOf(elementType) ||
                            TypeSystem.GetNonNullableType(elementType) == TypeSystem.GetNonNullableType(genExpectedTypeArgs[0])
                            ) {
                            // reference or nullable conversion use seq.Cast<E>()
                            MethodInfo miCast = TypeSystem.FindSequenceMethod("Cast", new Type[] { seqType }, genExpectedTypeArgs[0]);
                            System.Diagnostics.Debug.Assert(miCast != null);
                            gen.Emit(OpCodes.Call, miCast);
                        }
                        else {
                            // otherwise use orb.Convert<E>(sequence)
                            Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
                            MethodInfo miConvert = TypeSystem.FindStaticMethod(orbType, "Convert", new Type[] { seqType }, genExpectedTypeArgs[0]);
                            System.Diagnostics.Debug.Assert(miConvert != null);
                            gen.Emit(OpCodes.Call, miConvert);
                        }
                    }
                    // Do we have a sequence where we wanted a singleton?
                    else if (expectedType == elemType && actualIsSequence) {
                        // seq.SingleOrDefault()
                        MethodInfo miFirst = TypeSystem.FindSequenceMethod("SingleOrDefault", new Type[] { seqType }, expectedType);
                        System.Diagnostics.Debug.Assert(miFirst != null);
                        gen.Emit(OpCodes.Call, miFirst);
                    }
                    // do we have a non-nullable value where we want a nullable value?
                    else if (TypeSystem.IsNullableType(expectedType) &&
                             TypeSystem.GetNonNullableType(expectedType) == actualType) {
                        // new Nullable<T>(expr)
                        ConstructorInfo ci = expectedType.GetConstructor(new Type[] { actualType });
                        gen.Emit(OpCodes.Newobj, ci);
                    }
                    // do we have a nullable value where we want a non-nullable value?
                    else if (TypeSystem.IsNullableType(actualType) &&
                             TypeSystem.GetNonNullableType(actualType) == expectedType) {
                        // expr.GetValueOrDefault()
                        LocalBuilder loc = gen.DeclareLocal(actualType);
                        gen.Emit(OpCodes.Stloc, loc);
                        gen.Emit(OpCodes.Ldloca, loc);
                        this.GenerateGetValueOrDefault(actualType);
                    }
                    // do we have a value when we want an EntityRef or Link of that value
                    else if (genExpectedType == typeof(EntityRef<>) || genExpectedType == typeof(Link<>)) {
                        if (actualType.IsAssignableFrom(genExpectedTypeArgs[0])) {
                            // new T(expr)
                            if (actualType != genExpectedTypeArgs[0]) {
                                // Ensure that the actual runtime type of the value is
                                // compatible.  For example, in inheritance scenarios
                                // the Type of the value can vary from row to row.
                                this.GenerateConvertToType(actualType, genExpectedTypeArgs[0]);
                            }
                            ConstructorInfo ci = expectedType.GetConstructor(new Type[] { genExpectedTypeArgs[0] });
                            System.Diagnostics.Debug.Assert(ci != null);
                            gen.Emit(OpCodes.Newobj, ci);
                        }
                        else if (seqType.IsAssignableFrom(actualType)) {
                            // new T(seq.SingleOrDefault())
                            MethodInfo miFirst = TypeSystem.FindSequenceMethod("SingleOrDefault", new Type[] { seqType }, elemType);
                            System.Diagnostics.Debug.Assert(miFirst != null);
                            gen.Emit(OpCodes.Call, miFirst);
                            ConstructorInfo ci = expectedType.GetConstructor(new Type[] { elemType });
                            System.Diagnostics.Debug.Assert(ci != null);
                            gen.Emit(OpCodes.Newobj, ci);
                        }
                        else {
                            throw Error.CannotConvertToEntityRef(actualType);
                        }
                    }
                    // do we have a sequence when we want IQueryable/IOrderedQueryable?
                    else if ((expectedType == typeof(IQueryable) ||
                              expectedType == typeof(IOrderedQueryable))
                              && typeof(IEnumerable).IsAssignableFrom(actualType)) {
                        // seq.AsQueryable()
                        MethodInfo miAsQueryable = TypeSystem.FindQueryableMethod("AsQueryable", new Type[] { typeof(IEnumerable) });
                        System.Diagnostics.Debug.Assert(miAsQueryable != null);
                        gen.Emit(OpCodes.Call, miAsQueryable);
                        if (genExpectedType == typeof(IOrderedQueryable)) {
                            gen.Emit(OpCodes.Castclass, expectedType);
                        }
                    }
                    // do we have a sequence when we want IQuerayble<T>/IOrderedQueryable<T>?
                    else if ((genExpectedType == typeof(IQueryable<>) ||
                              genExpectedType == typeof(IOrderedQueryable<>)) &&
                             actualIsSequence
                        ) {
                        if (elemType != genExpectedTypeArgs[0]) {
                            seqType = typeof(IEnumerable<>).MakeGenericType(genExpectedTypeArgs);
                            this.GenerateConvertToType(actualType, seqType);
                            elemType = genExpectedTypeArgs[0];
                        }
                        // seq.AsQueryable()
                        MethodInfo miAsQueryable = TypeSystem.FindQueryableMethod("AsQueryable", new Type[] { seqType }, elemType);
                        System.Diagnostics.Debug.Assert(miAsQueryable != null);
                        gen.Emit(OpCodes.Call, miAsQueryable);
                        if (genExpectedType == typeof(IOrderedQueryable<>)) {
                            gen.Emit(OpCodes.Castclass, expectedType);
                        }
                    }
                    // do we have a sequence when we want IOrderedEnumerable?
                    else if (genExpectedType == typeof(IOrderedEnumerable<>) && actualIsSequence) {
                        if (elemType != genExpectedTypeArgs[0]) {
                            seqType = typeof(IEnumerable<>).MakeGenericType(genExpectedTypeArgs);
                            this.GenerateConvertToType(actualType, seqType);
                            elemType = genExpectedTypeArgs[0];
                        }
                        // new OrderedResults<E>(seq)
                        Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.compiler.dataReaderType);
                        MethodInfo miCreateOrderedEnumerable = TypeSystem.FindStaticMethod(orbType, "CreateOrderedEnumerable", new Type[] { seqType }, elemType);
                        System.Diagnostics.Debug.Assert(miCreateOrderedEnumerable != null);
                        gen.Emit(OpCodes.Call, miCreateOrderedEnumerable);
                    }
                    // do we have a sequence when we want EntitySet<T> ?
                    else if (genExpectedType == typeof(EntitySet<>) && actualIsSequence) {
                        if (elemType != genExpectedTypeArgs[0]) {
                            seqType = typeof(IEnumerable<>).MakeGenericType(genExpectedTypeArgs);
                            this.GenerateConvertToType(actualType, seqType);
                            actualType = seqType;
                            elemType = genExpectedTypeArgs[0];
                        }
                        // loc = new EntitySet<E>(); loc.Assign(seq); loc
                        LocalBuilder locSeq = gen.DeclareLocal(actualType);
                        gen.Emit(OpCodes.Stloc, locSeq);
 
                        ConstructorInfo ci = expectedType.GetConstructor(System.Type.EmptyTypes);
                        System.Diagnostics.Debug.Assert(ci != null);
                        gen.Emit(OpCodes.Newobj, ci);
                        LocalBuilder locEs = gen.DeclareLocal(expectedType);
                        gen.Emit(OpCodes.Stloc, locEs);
 
                        gen.Emit(OpCodes.Ldloc, locEs);
                        gen.Emit(OpCodes.Ldloc, locSeq);
                        MethodInfo miAssign = expectedType.GetMethod("Assign", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { seqType }, null);
                        System.Diagnostics.Debug.Assert(miAssign != null);
                        gen.Emit(GetMethodCallOpCode(miAssign), miAssign);
 
                        gen.Emit(OpCodes.Ldloc, locEs);
                    }
                    // do we have a sequence when we want something assignable from List<T>?
                    else if (typeof(IEnumerable).IsAssignableFrom(expectedType) &&
                            actualIsSequence &&
                            expectedType.IsAssignableFrom(typeof(List<>).MakeGenericType(elemType))
                        ) {
                        // new List<E>(seq)
                        Type listType = typeof(List<>).MakeGenericType(elemType);
                        ConstructorInfo ci = listType.GetConstructor(new Type[] { seqType });
                        System.Diagnostics.Debug.Assert(ci != null);
                        gen.Emit(OpCodes.Newobj, ci);
                    }
                    // do we have a sequence when we want T[]?
                    else if (expectedType.IsArray && expectedType.GetArrayRank() == 1 &&
                             !actualType.IsArray && seqType.IsAssignableFrom(actualType) &&
                             expectedType.GetElementType().IsAssignableFrom(elemType)
                        ) {
                        // seq.ToArray()
                        MethodInfo miToArray = TypeSystem.FindSequenceMethod("ToArray", new Type[] { seqType }, elemType);
                        System.Diagnostics.Debug.Assert(miToArray != null);
                        gen.Emit(OpCodes.Call, miToArray);
                    }
                    // do we have a sequence when we want some other collection type?
                    else if (expectedType.IsClass &&
                            typeof(ICollection<>).MakeGenericType(elemType).IsAssignableFrom(expectedType) &&
                            expectedType.GetConstructor(System.Type.EmptyTypes) != null &&
                            seqType.IsAssignableFrom(actualType)
                        ) {
                        throw Error.GeneralCollectionMaterializationNotSupported();
                    }
                    // do we have an int when we want a bool?
                    else if (expectedType == typeof(bool) && actualType == typeof(int)) {
                        // expr != 0
                        Label labZero = gen.DefineLabel();
                        Label labExit = gen.DefineLabel();
                        gen.Emit(OpCodes.Ldc_I4_0);
                        gen.Emit(OpCodes.Ceq);
                        gen.Emit(OpCodes.Brtrue_S, labZero);
                        gen.Emit(OpCodes.Ldc_I4_1);
                        gen.Emit(OpCodes.Br_S, labExit);
                        gen.MarkLabel(labZero);
                        gen.Emit(OpCodes.Ldc_I4_0);
                        gen.MarkLabel(labExit);
                    }
                    else {
                        // last-ditch attempt: convert at runtime using DBConvert
                        // DBConvert.ChangeType(type, expr)
                        if (actualType.IsValueType) {
                            gen.Emit(OpCodes.Box, actualType);
                        }
                        gen.Emit(OpCodes.Ldtoken, expectedType);
                        MethodInfo miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public);
                        System.Diagnostics.Debug.Assert(miGetTypeFromHandle != null);
                        gen.Emit(OpCodes.Call, miGetTypeFromHandle);
                        MethodInfo miChangeType = typeof(DBConvert).GetMethod("ChangeType", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(object), typeof(Type) }, null);
                        System.Diagnostics.Debug.Assert(miChangeType != null);
                        gen.Emit(OpCodes.Call, miChangeType);
                        if (expectedType.IsValueType) {
                            gen.Emit(OpCodes.Unbox_Any, expectedType);
                        }
                        else if (expectedType != typeof(object)) {
                            gen.Emit(OpCodes.Castclass, expectedType);
                        }
                    }
                }
            }
 
            private Type GenerateColumnReference(SqlColumnRef cref) {
                this.GenerateColumnAccess(cref.ClrType, cref.SqlType, cref.Column.Ordinal, null);
                return cref.ClrType;
            }
 
            private Type GenerateUserColumn(SqlUserColumn suc) {
                // if the user column is not named, it must be the only one!
                if (string.IsNullOrEmpty(suc.Name)) {
                    this.GenerateColumnAccess(suc.ClrType, suc.SqlType, 0, null);
                    return suc.ClrType;
                }
                int iName = this.namedColumns.Count;
                this.namedColumns.Add(new NamedColumn(suc.Name, suc.IsRequired));
 
                Label labNotDefined = gen.DefineLabel();
                Label labExit = gen.DefineLabel();
                LocalBuilder locOrdinal = gen.DeclareLocal(typeof(int));
 
                // ordinal = session.ordinals[i]
                this.GenerateAccessOrdinals();
                this.GenerateConstInt(iName);
                this.GenerateArrayAccess(typeof(int), false);
                gen.Emit(OpCodes.Stloc, locOrdinal);
 
                // if (ordinal < 0) goto labNotDefined
                gen.Emit(OpCodes.Ldloc, locOrdinal);
                this.GenerateConstInt(0);
                gen.Emit(OpCodes.Clt);
                gen.Emit(OpCodes.Brtrue, labNotDefined);
 
                // access column at ordinal position
                this.GenerateColumnAccess(suc.ClrType, suc.SqlType, 0, locOrdinal);
                gen.Emit(OpCodes.Br_S, labExit);
 
                // not defined?
                gen.MarkLabel(labNotDefined);
                this.GenerateDefault(suc.ClrType, false);
 
                gen.MarkLabel(labExit);
 
                return suc.ClrType;
            }
 
            private void GenerateColumnAccess(Type cType, ProviderType pType, int ordinal, LocalBuilder locOrdinal) {
                Type rType = pType.GetClosestRuntimeType();
                MethodInfo readerMethod = this.GetReaderMethod(this.compiler.dataReaderType, rType);
                MethodInfo bufferMethod = this.GetReaderMethod(typeof(DbDataReader), rType);
 
                Label labIsNull = gen.DefineLabel();
                Label labExit = gen.DefineLabel();
                Label labReadFromBuffer = gen.DefineLabel();
 
                // if (buffer != null) goto ReadFromBuffer
                this.GenerateAccessBufferReader();
                gen.Emit(OpCodes.Ldnull);
                gen.Emit(OpCodes.Ceq);
                gen.Emit(OpCodes.Brfalse, labReadFromBuffer);
 
                // read from DataReader
                // this.reader.IsNull?
                this.GenerateAccessDataReader();
                if (locOrdinal != null)
                    gen.Emit(OpCodes.Ldloc, locOrdinal);
                else
                    this.GenerateConstInt(ordinal);
                gen.Emit(GetMethodCallOpCode(this.compiler.miDRisDBNull), this.compiler.miDRisDBNull);
                gen.Emit(OpCodes.Brtrue, labIsNull);
 
                // this.reader.GetXXX()
                this.GenerateAccessDataReader();
                if (locOrdinal != null)
                    gen.Emit(OpCodes.Ldloc, locOrdinal);
                else
                    this.GenerateConstInt(ordinal);
                gen.Emit(GetMethodCallOpCode(readerMethod), readerMethod);
                this.GenerateConvertToType(rType, cType, readerMethod.ReturnType);
                gen.Emit(OpCodes.Br_S, labExit);
 
                // read from BUFFER
                gen.MarkLabel(labReadFromBuffer);
 
                // this.bufferReader.IsNull?
                this.GenerateAccessBufferReader();
                if (locOrdinal != null)
                    gen.Emit(OpCodes.Ldloc, locOrdinal);
                else
                    this.GenerateConstInt(ordinal);
                gen.Emit(GetMethodCallOpCode(this.compiler.miBRisDBNull), this.compiler.miBRisDBNull);
                gen.Emit(OpCodes.Brtrue, labIsNull);
 
                // this.bufferReader.GetXXX()
                this.GenerateAccessBufferReader();
                if (locOrdinal != null)
                    gen.Emit(OpCodes.Ldloc, locOrdinal);
                else
                    this.GenerateConstInt(ordinal);
                gen.Emit(GetMethodCallOpCode(bufferMethod), bufferMethod);
                this.GenerateConvertToType(rType, cType, bufferMethod.ReturnType);
                gen.Emit(OpCodes.Br_S, labExit);
 
                // return NULL
                gen.MarkLabel(labIsNull);
                this.GenerateDefault(cType);
 
                gen.MarkLabel(labExit);
            }
 
            private Type GenerateClientCase(SqlClientCase scc, bool isDeferred, LocalBuilder locInstance) {
                LocalBuilder locDiscriminator = gen.DeclareLocal(scc.Expression.ClrType);
                this.GenerateExpressionForType(scc.Expression, scc.Expression.ClrType);
                gen.Emit(OpCodes.Stloc, locDiscriminator);
 
                Label labNext = gen.DefineLabel();
                Label labEnd = gen.DefineLabel();
                for (int i = 0, n = scc.Whens.Count; i < n; i++) {
                    if (i > 0) {
                        gen.MarkLabel(labNext);
                        labNext = gen.DefineLabel();
                    }
                    SqlClientWhen when = scc.Whens[i];
                    if (when.Match != null) {
                        gen.Emit(OpCodes.Ldloc, locDiscriminator);
                        this.GenerateExpressionForType(when.Match, scc.Expression.ClrType);
                        this.GenerateEquals(locDiscriminator.LocalType);
                        gen.Emit(OpCodes.Brfalse, labNext);
                    }
                    if (isDeferred) {
                        this.GenerateDeferredSource(when.Value, locInstance);
                    }
                    else {
                        this.GenerateExpressionForType(when.Value, scc.ClrType);
                    }
                    gen.Emit(OpCodes.Br, labEnd);
                }
                gen.MarkLabel(labEnd);
 
                return scc.ClrType;
            }
 
            private Type GenerateTypeCase(SqlTypeCase stc) {
                LocalBuilder locDiscriminator = gen.DeclareLocal(stc.Discriminator.ClrType);
                this.GenerateExpressionForType(stc.Discriminator, stc.Discriminator.ClrType);
                gen.Emit(OpCodes.Stloc, locDiscriminator);
 
                Label labNext = gen.DefineLabel();
                Label labEnd = gen.DefineLabel();
                bool hasDefault = false;
 
                for (int i = 0, n = stc.Whens.Count; i < n; i++) {
                    if (i > 0) {
                        gen.MarkLabel(labNext);
                        labNext = gen.DefineLabel();
                    }
                    SqlTypeCaseWhen when = stc.Whens[i];
                    if (when.Match != null) {
                        gen.Emit(OpCodes.Ldloc, locDiscriminator);
                        SqlValue vMatch = when.Match as SqlValue;
                        System.Diagnostics.Debug.Assert(vMatch != null);
                        this.GenerateConstant(locDiscriminator.LocalType, vMatch.Value);
                        this.GenerateEquals(locDiscriminator.LocalType);
                        gen.Emit(OpCodes.Brfalse, labNext);
                    }
                    else {
                        System.Diagnostics.Debug.Assert(i == n - 1);
                        hasDefault = true;
                    }
                    this.GenerateExpressionForType(when.TypeBinding, stc.ClrType);
                    gen.Emit(OpCodes.Br, labEnd);
                }
                gen.MarkLabel(labNext);
                if (!hasDefault) {
                    this.GenerateConstant(stc.ClrType, null);
                }
                gen.MarkLabel(labEnd);
 
                return stc.ClrType;
            }
 
            private Type GenerateDiscriminatedType(SqlDiscriminatedType dt) {
                System.Diagnostics.Debug.Assert(dt.ClrType == typeof(Type));
 
                LocalBuilder locDiscriminator = gen.DeclareLocal(dt.Discriminator.ClrType);
                this.GenerateExpressionForType(dt.Discriminator, dt.Discriminator.ClrType);
                gen.Emit(OpCodes.Stloc, locDiscriminator);
 
                return this.GenerateDiscriminatedType(dt.TargetType, locDiscriminator, dt.Discriminator.SqlType);
            }
 
            private Type GenerateDiscriminatedType(MetaType targetType, LocalBuilder locDiscriminator, ProviderType discriminatorType) {
                System.Diagnostics.Debug.Assert(targetType != null && locDiscriminator != null);
 
                MetaType defType = null;
                Label labNext = gen.DefineLabel();
                Label labEnd = gen.DefineLabel();
                foreach (MetaType imt in targetType.InheritanceTypes) {
                    if (imt.InheritanceCode != null) {
                        if (imt.IsInheritanceDefault) {
                            defType = imt;
                        }
                        // disc == code?
                        gen.Emit(OpCodes.Ldloc, locDiscriminator);
                        object code = InheritanceRules.InheritanceCodeForClientCompare(imt.InheritanceCode, discriminatorType);
                        this.GenerateConstant(locDiscriminator.LocalType, code);
                        this.GenerateEquals(locDiscriminator.LocalType);
                        gen.Emit(OpCodes.Brfalse, labNext);
 
                        this.GenerateConstant(typeof(Type), imt.Type);
                        gen.Emit(OpCodes.Br, labEnd);
 
                        gen.MarkLabel(labNext);
                        labNext = gen.DefineLabel();
                    }
                }
                gen.MarkLabel(labNext);
                if (defType != null) {
                    this.GenerateConstant(typeof(Type), defType.Type);
                }
                else {
                    this.GenerateDefault(typeof(Type));
                }
 
                gen.MarkLabel(labEnd);
 
                return typeof(Type);
            }
 
            private Type GenerateSearchedCase(SqlSearchedCase ssc) {
                Label labNext = gen.DefineLabel();
                Label labEnd = gen.DefineLabel();
                for (int i = 0, n = ssc.Whens.Count; i < n; i++) {
                    if (i > 0) {
                        gen.MarkLabel(labNext);
                        labNext = gen.DefineLabel();
                    }
                    SqlWhen when = ssc.Whens[i];
                    if (when.Match != null) {
                        this.GenerateExpressionForType(when.Match, typeof(bool)); // test
                        this.GenerateConstInt(0);
                        gen.Emit(OpCodes.Ceq);
                        gen.Emit(OpCodes.Brtrue, labNext);
                    }
                    this.GenerateExpressionForType(when.Value, ssc.ClrType);
                    gen.Emit(OpCodes.Br, labEnd);
                }
                gen.MarkLabel(labNext);
                if (ssc.Else != null) {
                    this.GenerateExpressionForType(ssc.Else, ssc.ClrType);
                }
                gen.MarkLabel(labEnd);
                return ssc.ClrType;
            }
 
            private void GenerateEquals(Type type) {
                switch (Type.GetTypeCode(type)) {
                    case TypeCode.Object:
                    case TypeCode.String:
                    case TypeCode.DBNull:
                        if (type.IsValueType) {
                            LocalBuilder locLeft = gen.DeclareLocal(type);
                            LocalBuilder locRight = gen.DeclareLocal(type);
                            gen.Emit(OpCodes.Stloc, locRight);
                            gen.Emit(OpCodes.Stloc, locLeft);
                            gen.Emit(OpCodes.Ldloc, locLeft);
                            gen.Emit(OpCodes.Box, type);
                            gen.Emit(OpCodes.Ldloc, locRight);
                            gen.Emit(OpCodes.Box, type);
                        }
                        MethodInfo miEquals = typeof(object).GetMethod("Equals", BindingFlags.Static | BindingFlags.Public);
                        System.Diagnostics.Debug.Assert(miEquals != null);
                        gen.Emit(GetMethodCallOpCode(miEquals), miEquals);
                        break;
                    default:
                        gen.Emit(OpCodes.Ceq);
                        break;
                }
            }
 
            private void GenerateDefault(Type type) {
                this.GenerateDefault(type, true);
            }
 
            private void GenerateDefault(Type type, bool throwIfNotNullable) {
                if (type.IsValueType) {
                    if (!throwIfNotNullable || TypeSystem.IsNullableType(type)) {
                        LocalBuilder loc = gen.DeclareLocal(type);
                        gen.Emit(OpCodes.Ldloca, loc);
                        gen.Emit(OpCodes.Initobj, type);
                        gen.Emit(OpCodes.Ldloc, loc);
                    }
                    else {
                        gen.Emit(OpCodes.Ldtoken, type);
                        gen.Emit(OpCodes.Call, typeof(Type).GetMethod(
                            "GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
 
                        MethodInfo mi = typeof(ObjectMaterializer<>)
                            .MakeGenericType(this.compiler.dataReaderType)
                            .GetMethod("ErrorAssignmentToNull", BindingFlags.Static | BindingFlags.Public);
                        System.Diagnostics.Debug.Assert(mi != null);
                        gen.Emit(OpCodes.Call, mi);
                        gen.Emit(OpCodes.Throw);
                    }
                }
                else {
                    gen.Emit(OpCodes.Ldnull);
                }
            }
 
            private static Type[] readMethodSignature = new Type[] { typeof(int) };
 
            [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Unknown reason.")]
            private MethodInfo GetReaderMethod(Type readerType, Type valueType) {
                if (valueType.IsEnum)
                    valueType = valueType.BaseType;
 
                TypeCode tc = Type.GetTypeCode(valueType);
                string name;
                if (tc == TypeCode.Single) {
                    name = "GetFloat";
                }
                else {
                    name = "Get" + valueType.Name;
                }
 
                MethodInfo readerMethod = readerType.GetMethod(
                   name,
                   BindingFlags.Instance | BindingFlags.Public,
                   null,
                   readMethodSignature,
                   null
                   );
 
                if (readerMethod == null) {
                    readerMethod = readerType.GetMethod(
                        "GetValue",
                        BindingFlags.Instance | BindingFlags.Public,
                        null,
                        readMethodSignature,
                        null
                        );
                }
                System.Diagnostics.Debug.Assert(readerMethod != null);
                return readerMethod;
            }
 
            private void GenerateHasValue(Type nullableType) {
                MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public);
                gen.Emit(OpCodes.Call, mi);
            }
 
            private void GenerateGetValue(Type nullableType) {
                MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public);
                gen.Emit(OpCodes.Call, mi);
            }
 
            private void GenerateGetValueOrDefault(Type nullableType) {
                MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", System.Type.EmptyTypes);
                gen.Emit(OpCodes.Call, mi);
            }
 
            private Type GenerateGlobalAccess(int iGlobal, Type type) {
                this.GenerateAccessGlobals();
                if (type.IsValueType) {
                    this.GenerateConstInt(iGlobal);
                    gen.Emit(OpCodes.Ldelem_Ref);
                    Type varType = typeof(StrongBox<>).MakeGenericType(type);
                    gen.Emit(OpCodes.Castclass, varType);
                    FieldInfo fi = varType.GetField("Value", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                    gen.Emit(OpCodes.Ldfld, fi);
                }
                else {
                    this.GenerateConstInt(iGlobal);
                    gen.Emit(OpCodes.Ldelem_Ref);
                    this.GenerateConvertToType(typeof(object), type);
                    gen.Emit(OpCodes.Castclass, type);
                }
                return type;
            }
 
            private int AddGlobal(Type type, object value) {
                int iGlobal = this.globals.Count;
                if (type.IsValueType) {
                    this.globals.Add(Activator.CreateInstance(typeof(StrongBox<>).MakeGenericType(type), new object[] { value }));
                }
                else {
                    this.globals.Add(value);
                }
                return iGlobal;
            }
 
            private int AllocateLocal() {
                return this.nLocals++;
            }
 
            private void GenerateStoreMember(MemberInfo mi) {
                FieldInfo fi = mi as FieldInfo;
                if (fi != null) {
                    gen.Emit(OpCodes.Stfld, fi);
                }
                else {
                    PropertyInfo pi = (PropertyInfo)mi;
                    MethodInfo meth = pi.GetSetMethod(true);
                    System.Diagnostics.Debug.Assert(meth != null);
                    gen.Emit(GetMethodCallOpCode(meth), meth);
                }
            }
 
            private void GenerateLoadMember(MemberInfo mi) {
                FieldInfo fi = mi as FieldInfo;
                if (fi != null) {
                    gen.Emit(OpCodes.Ldfld, fi);
                }
                else {
                    PropertyInfo pi = (PropertyInfo)mi;
                    MethodInfo meth = pi.GetGetMethod(true);
                    gen.Emit(GetMethodCallOpCode(meth), meth);
                }
            }
 
            [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", Justification = "Microsoft: The variable tc for which the rule fires is used in both a Debug.Assert and in a switch statement")]
            private void GenerateArrayAssign(Type type) {
                // This method was copied out of the expression compiler codebase.  
                // Since DLINQ doesn't currently consume array indexers most of this 
                // function goes unused. Currently, the DLINQ materializer only 
                // accesses only ararys of objects and array of integers.
                // The code is comment out to improve code coverage test.
                // If you see one of the following assert fails, try to enable 
                // the comment out code.
 
                if (type.IsEnum) {
                    gen.Emit(OpCodes.Stelem, type);
                }
                else {
                    TypeCode tc = Type.GetTypeCode(type);
 
                    switch (tc) {
                        case TypeCode.SByte:
                        case TypeCode.Byte:
                             gen.Emit(OpCodes.Stelem_I1);
                             break;
                        case TypeCode.Int16:
                        case TypeCode.UInt16:
                             gen.Emit(OpCodes.Stelem_I2);
                             break;
                        case TypeCode.Int32:
                        case TypeCode.UInt32:
                             gen.Emit(OpCodes.Stelem_I4);
                             break;
                        case TypeCode.Int64:
                        case TypeCode.UInt64:
                             gen.Emit(OpCodes.Stelem_I8);
                             break;
                        case TypeCode.Single:
                             gen.Emit(OpCodes.Stelem_R4);
                             break;
                        case TypeCode.Double:
                             gen.Emit(OpCodes.Stelem_R8);
                             break;
                        default:
                            if (type.IsValueType) {
                                gen.Emit(OpCodes.Stelem, type);
                            }
                            else {
                                gen.Emit(OpCodes.Stelem_Ref);
                            }
                            break;
                    }
                }
            }
 
            [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "address", Justification = "Microsoft: See comments in source. Usage commented out to improve code coverage test")]
            private Type GenerateArrayAccess(Type type, bool address) {
                // This method was copied out of the expression compiler codebase.  
                // Since DLINQ doesn't currently consume array indexers most of this 
                // function goes unused. Currently, the DLINQ materializer only 
                // accesses arrays of objects and array of integers.
                // The code is comment out to improve code coverage test.
                // If you see one of the following asserts fails, try to enable 
                // the comment out code.
 
                System.Diagnostics.Debug.Assert(address == false);
 
                // if (address)
                // {
                //    gen.Emit(OpCodes.Ldelema);
                //    return type.MakeByRefType();
                // }
                // else
                {
                    if (type.IsEnum) {
                        System.Diagnostics.Debug.Assert(false);
                        // gen.Emit(OpCodes.Ldelem, type);
                    }
                    else {
                        TypeCode tc = Type.GetTypeCode(type);
                        System.Diagnostics.Debug.Assert(tc == TypeCode.Int32);
 
                        switch (tc) {
                            //case TypeCode.SByte:
                            //     gen.Emit(OpCodes.Ldelem_I1);
                            //     break;
                            //case TypeCode.Int16:
                            //     gen.Emit(OpCodes.Ldelem_I2);
                            //     break;
                            case TypeCode.Int32:
                                gen.Emit(OpCodes.Ldelem_I4);
                                break;
                            //case TypeCode.Int64:
                            //     gen.Emit(OpCodes.Ldelem_I8);
                            //     break;
                            //case TypeCode.Single:
                            //     gen.Emit(OpCodes.Ldelem_R4);
                            //     break;
                            //case TypeCode.Double:
                            //     gen.Emit(OpCodes.Ldelem_R8);
                            //     break;
                            //default:
                            //     if (type.IsValueType) {
                            //        gen.Emit(OpCodes.Ldelem, type);
                            //     }
                            //     else {
                            //        gen.Emit(OpCodes.Ldelem_Ref);
                            //     }
                            //     break;
                        }
                    }
                    return type;
                }
            }
 
            private Type GenerateConstant(Type type, object value) {
                if (value == null) {
                    if (type.IsValueType) {
                        LocalBuilder loc = gen.DeclareLocal(type);
                        gen.Emit(OpCodes.Ldloca, loc);
                        gen.Emit(OpCodes.Initobj, type);
                        gen.Emit(OpCodes.Ldloc, loc);
                    }
                    else {
                        gen.Emit(OpCodes.Ldnull);
                    }
                }
                else {
                    TypeCode tc = Type.GetTypeCode(type);
                    switch (tc) {
                        case TypeCode.Boolean:
                            this.GenerateConstInt((bool)value ? 1 : 0);
                            break;
                        case TypeCode.SByte:
                            this.GenerateConstInt((SByte)value);
                            gen.Emit(OpCodes.Conv_I1);
                            break;
                        case TypeCode.Int16:
                            this.GenerateConstInt((Int16)value);
                            gen.Emit(OpCodes.Conv_I2);
                            break;
                        case TypeCode.Int32:
                            this.GenerateConstInt((Int32)value);
                            break;
                        case TypeCode.Int64:
                            gen.Emit(OpCodes.Ldc_I8, (Int64)value);
                            break;
                        case TypeCode.Single:
                            gen.Emit(OpCodes.Ldc_R4, (float)value);
                            break;
                        case TypeCode.Double:
                            gen.Emit(OpCodes.Ldc_R8, (double)value);
                            break;
                        default:
                            int iGlobal = this.AddGlobal(type, value);
                            return this.GenerateGlobalAccess(iGlobal, type);
                    }
                }
                return type;
            }
 
 
            private void GenerateConstInt(int value) {
                switch (value) {
                    case 0:
                        gen.Emit(OpCodes.Ldc_I4_0);
                        break;
                    case 1:
                        gen.Emit(OpCodes.Ldc_I4_1);
                        break;
                    case 2:
                        gen.Emit(OpCodes.Ldc_I4_2);
                        break;
                    case 3:
                        gen.Emit(OpCodes.Ldc_I4_3);
                        break;
                    case 4:
                        gen.Emit(OpCodes.Ldc_I4_4);
                        break;
                    case 5:
                        gen.Emit(OpCodes.Ldc_I4_5);
                        break;
                    case 6:
                        gen.Emit(OpCodes.Ldc_I4_6);
                        break;
                    case 7:
                        gen.Emit(OpCodes.Ldc_I4_7);
                        break;
                    case 8:
                        gen.Emit(OpCodes.Ldc_I4_8);
                        break;
                    default:
                        if (value == -1) {
                            gen.Emit(OpCodes.Ldc_I4_M1);
                        }
                        else if (value >= -127 && value < 128) {
                            gen.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
                        }
                        else {
                            gen.Emit(OpCodes.Ldc_I4, value);
                        }
                        break;
                }
            }
        }
 
        struct NamedColumn {
            string name;
            bool isRequired;
            internal NamedColumn(string name, bool isRequired) {
                this.name = name;
                this.isRequired = isRequired;
            }
            internal string Name {
                get { return this.name; }
            }
            internal bool IsRequired {
                get { return this.isRequired; }
            }
        }
 
        class ObjectReaderFactory<TDataReader, TObject> : IObjectReaderFactory
            where TDataReader : DbDataReader {
            Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize;
            NamedColumn[] namedColumns;
            object[] globals;
            int nLocals;
 
            internal ObjectReaderFactory(
                Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize,
                NamedColumn[] namedColumns,
                object[] globals,
                int nLocals
                ) {
                this.fnMaterialize = fnMaterialize;
                this.namedColumns = namedColumns;
                this.globals = globals;
                this.nLocals = nLocals;
            }
 
            public IObjectReader Create(DbDataReader dataReader, bool disposeDataReader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
                ObjectReaderSession<TDataReader> session = new ObjectReaderSession<TDataReader>((TDataReader)dataReader, provider, parentArgs, userArgs, subQueries);
                return session.CreateReader<TObject>(this.fnMaterialize, this.namedColumns, this.globals, this.nLocals, disposeDataReader);
            }
 
            public IObjectReader GetNextResult(IObjectReaderSession session, bool disposeDataReader) {
                ObjectReaderSession<TDataReader> ors = (ObjectReaderSession<TDataReader>)session;
                IObjectReader reader = ors.GetNextResult<TObject>(this.fnMaterialize, this.namedColumns, this.globals, this.nLocals, disposeDataReader);
                if (reader == null && disposeDataReader) {
                    ors.Dispose();
                }
                return reader;
            }
        }
 
        abstract class ObjectReaderBase<TDataReader> : ObjectMaterializer<TDataReader>
            where TDataReader : DbDataReader {
            protected ObjectReaderSession<TDataReader> session;
            bool hasRead;
            bool hasCurrentRow;
            bool isFinished;
            IDataServices services;
 
            internal ObjectReaderBase(
                ObjectReaderSession<TDataReader> session,
                NamedColumn[] namedColumns,
                object[] globals,
                object[] arguments,
                int nLocals
                )
                : base() {
                this.session = session;
                this.services = session.Provider.Services;
                this.DataReader = session.DataReader;
                this.Globals = globals;
                this.Arguments = arguments;
                if (nLocals > 0) {
                    this.Locals = new object[nLocals];
                }
                if (this.session.IsBuffered) {
                    this.Buffer();
                }
                this.Ordinals = this.GetColumnOrdinals(namedColumns);
            }
 
            // This method is called from within this class's constructor (through a call to Buffer()) so it is sealed to prevent
            // derived classes from overriding it. See FxCop rule CA2214 for more information on why this is necessary.
            public override sealed bool Read() {
                if (this.isFinished) {
                    return false;
                }
                if (this.BufferReader != null) {
                    this.hasCurrentRow = this.BufferReader.Read();
                }
                else {
                    this.hasCurrentRow = this.DataReader.Read();
                }
                if (!this.hasCurrentRow) {
                    this.isFinished = true;
                    this.session.Finish(this);
                }
                this.hasRead = true;
                return this.hasCurrentRow;
            }
 
            internal bool IsBuffered {
                get { return this.BufferReader != null; }
            }
 
            [SuppressMessage("Microsoft.Globalization", "CA1306:SetLocaleForDataTypes", Justification = "Microsoft: Used only as a buffer and never used for string comparison.")]
            internal void Buffer() {
                if (this.BufferReader == null && (this.hasCurrentRow || !this.hasRead)) {
                    if (this.session.IsBuffered) {
                        this.BufferReader = this.session.GetNextBufferedReader();
                    }
                    else {
                        DataSet ds = new DataSet();
                        ds.EnforceConstraints = false;
                        DataTable bufferTable = new DataTable();
                        ds.Tables.Add(bufferTable);
                        string[] names = this.session.GetActiveNames();
                        bufferTable.Load(new Rereader(this.DataReader, this.hasCurrentRow, null), LoadOption.OverwriteChanges);
                        this.BufferReader = new Rereader(bufferTable.CreateDataReader(), false, names);
                    }
                    if (this.hasCurrentRow) {
                        this.Read();
                    }
                }
            }
 
            public override object InsertLookup(int iMetaType, object instance) {
                MetaType mType = (MetaType)this.Globals[iMetaType];
                return this.services.InsertLookupCachedObject(mType, instance);
            }
 
            public override void SendEntityMaterialized(int iMetaType, object instance) {
                MetaType mType = (MetaType)this.Globals[iMetaType];
                this.services.OnEntityMaterialized(mType, instance);
            }
 
            public override IEnumerable ExecuteSubQuery(int iSubQuery, object[] parentArgs) {
                if (this.session.ParentArguments != null) {
                    // Create array to accumulate args, and add both parent
                    // args and the supplied args to the array
                    int nParent = this.session.ParentArguments.Length;
                    object[] tmp = new object[nParent + parentArgs.Length];
                    Array.Copy(this.session.ParentArguments, tmp, nParent);
                    Array.Copy(parentArgs, 0, tmp, nParent, parentArgs.Length);
                    parentArgs = tmp;
                }
                ICompiledSubQuery subQuery = this.session.SubQueries[iSubQuery];
                IEnumerable results = (IEnumerable)subQuery.Execute(this.session.Provider, parentArgs, this.session.UserArguments).ReturnValue;
                return results;
            }
 
            public override bool CanDeferLoad {
                get { return this.services.Context.DeferredLoadingEnabled; }
            }
 
            public override IEnumerable<T> GetLinkSource<T>(int iGlobalLink, int iLocalFactory, object[] keyValues) {
                IDeferredSourceFactory factory = (IDeferredSourceFactory)this.Locals[iLocalFactory];
                if (factory == null) {
                    MetaDataMember member = (MetaDataMember)this.Globals[iGlobalLink];
                    factory = this.services.GetDeferredSourceFactory(member);
                    this.Locals[iLocalFactory] = factory;
                }
                return (IEnumerable<T>)factory.CreateDeferredSource(keyValues);
            }
 
            public override IEnumerable<T> GetNestedLinkSource<T>(int iGlobalLink, int iLocalFactory, object instance) {
                IDeferredSourceFactory factory = (IDeferredSourceFactory)this.Locals[iLocalFactory];
                if (factory == null) {
                    MetaDataMember member = (MetaDataMember)this.Globals[iGlobalLink];
                    factory = this.services.GetDeferredSourceFactory(member);
                    this.Locals[iLocalFactory] = factory;
                }
                return (IEnumerable<T>)factory.CreateDeferredSource(instance);
            }
 
            private int[] GetColumnOrdinals(NamedColumn[] namedColumns) {
                DbDataReader reader = null;
                if (this.BufferReader != null) {
                    reader = this.BufferReader;
                }
                else {
                    reader = this.DataReader;
                }
                if (namedColumns == null || namedColumns.Length == 0) {
                    return null;
                }
                int[] columnOrdinals = new int[namedColumns.Length];
                Dictionary<string, int> lookup = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
                //we need to compare the quoted names on both sides
                //because the designer might quote the name unnecessarily
                for (int i = 0, n = reader.FieldCount; i < n; i++) {
                    lookup[SqlIdentifier.QuoteCompoundIdentifier(reader.GetName(i))] = i;
                }
                for (int i = 0, n = namedColumns.Length; i < n; i++) {
                    int ordinal;
                    if (lookup.TryGetValue(SqlIdentifier.QuoteCompoundIdentifier(namedColumns[i].Name), out ordinal)) {
                        columnOrdinals[i] = ordinal;
                    }
                    else if (namedColumns[i].IsRequired) {
                        throw Error.RequiredColumnDoesNotExist(namedColumns[i].Name);
                    }
                    else {
                        columnOrdinals[i] = -1;
                    }
                }
                return columnOrdinals;
            }
        }
 
        class ObjectReader<TDataReader, TObject>
            : ObjectReaderBase<TDataReader>, IEnumerator<TObject>, IObjectReader, IDisposable
            where TDataReader : DbDataReader {
            Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize;
            TObject current;
            bool disposeSession;
 
            internal ObjectReader(
                ObjectReaderSession<TDataReader> session,
                NamedColumn[] namedColumns,
                object[] globals,
                object[] arguments,
                int nLocals,
                bool disposeSession,
                Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize
                )
                : base(session, namedColumns, globals, arguments, nLocals) {
                this.disposeSession = disposeSession;
                this.fnMaterialize = fnMaterialize;
            }
 
            public IObjectReaderSession Session {
                get { return this.session; }
            }
 
            public void Dispose() {
#if PERFORMANCE_BUILD
                if (this.CollectQueryPerf) {
                    timer.Stop();
                    started = false;
                    pcSqlQueryEnumGetCurrent.IncrementBy(timer.Duration);
                    bpcSqlQueryEnumGetCurrent.Increment();
                }
#endif
                // Technically, calling GC.SuppressFinalize is not required because the class does not
                // have a finalizer, but it does no harm, protects against the case where a finalizer is added
                // in the future, and prevents an FxCop warning.
                GC.SuppressFinalize(this);
                if (this.disposeSession) {
                    this.session.Dispose();
                }
            }
 
            public bool MoveNext() {
#if PERFORMANCE_BUILD
                if (this.CollectQueryPerf) {
                    if (!started) {
                        started = true;
                        timer.Start();
                    }
                }
#endif
                if (this.Read()) {
                    this.current = this.fnMaterialize(this);
                    return true;
                }
                else {
                    this.current = default(TObject);
                    this.Dispose();
                    return false;
                }
            }
 
            public TObject Current {
                get { return this.current; }
            }
 
            public void Reset() {
            }
 
            object IEnumerator.Current {
                get {
                    return this.Current;
                }
            }
 
#if PERFORMANCE_BUILD
            PerformanceCounter pcSqlQueryEnumGetCurrent = null;
            PerformanceCounter bpcSqlQueryEnumGetCurrent = null;
            PerfTimer timer = null;
            bool collectQueryPerf;
            bool collectQueryPerfInitialized = false;
            bool started;
 
            private bool CollectQueryPerf {
                get {
                    if (!collectQueryPerfInitialized) {
                        collectQueryPerf = this.enumerable.session.context.CollectQueryPerf;
                        if (collectQueryPerf) {
                            pcSqlQueryEnumGetCurrent = new PerformanceCounter("DLinq", "SqlQueryEnumGetCurrentElapsedTime", false);
                            bpcSqlQueryEnumGetCurrent = new PerformanceCounter("DLinq", "SqlQueryEnumGetCurrentElapsedTimeBase", false);
                            timer = new PerfTimer();
                        }
                        collectQueryPerfInitialized = true;
                    }
                    return this.collectQueryPerf;
                }
            }
#endif
        }
 
        class ObjectReaderSession<TDataReader> : IObjectReaderSession, IDisposable, IConnectionUser
            where TDataReader : DbDataReader {
            TDataReader dataReader;
            ObjectReaderBase<TDataReader> currentReader;
            IReaderProvider provider;
            List<DbDataReader> buffer;
            int iNextBufferedReader;
            bool isDisposed;
            bool isDataReaderDisposed;
            bool hasResults;
            object[] parentArgs;
            object[] userArgs;
            ICompiledSubQuery[] subQueries;
 
            internal ObjectReaderSession(
                TDataReader dataReader,
                IReaderProvider provider,
                object[] parentArgs,
                object[] userArgs,
                ICompiledSubQuery[] subQueries
                ) {
                this.dataReader = dataReader;
                this.provider = provider;
                this.parentArgs = parentArgs;
                this.userArgs = userArgs;
                this.subQueries = subQueries;
                this.hasResults = true;
            }
 
            internal ObjectReaderBase<TDataReader> CurrentReader {
                get { return this.currentReader; }
            }
 
            internal TDataReader DataReader {
                get { return this.dataReader; }
            }
 
            internal IReaderProvider Provider {
                get { return this.provider; }
            }
 
            internal object[] ParentArguments {
                get { return this.parentArgs; }
            }
 
            internal object[] UserArguments {
                get { return this.userArgs; }
            }
 
            internal ICompiledSubQuery[] SubQueries {
                get { return this.subQueries; }
            }
 
            internal void Finish(ObjectReaderBase<TDataReader> finishedReader) {
                if (this.currentReader == finishedReader) {
                    this.CheckNextResults();
                }
            }
 
            private void CheckNextResults() {
                this.hasResults = !this.dataReader.IsClosed && this.dataReader.NextResult();
                this.currentReader = null;
                if (!this.hasResults) {
                    this.Dispose();
                }
            }
 
            internal DbDataReader GetNextBufferedReader() {
                if (this.iNextBufferedReader < this.buffer.Count) {
                    return this.buffer[this.iNextBufferedReader++];
                }
                System.Diagnostics.Debug.Assert(false);
                return null;
            }
 
            public bool IsBuffered {
                get { return this.buffer != null; }
            }
 
            [SuppressMessage("Microsoft.Globalization", "CA1306:SetLocaleForDataTypes", Justification = "Microsoft: Used only as a buffer and never used for string comparison.")]
            public void Buffer() {
                if (this.buffer == null) {
                    if (this.currentReader != null && !this.currentReader.IsBuffered) {
                        this.currentReader.Buffer();
                        this.CheckNextResults();
                    }
                    // buffer anything remaining in the session
                    this.buffer = new List<DbDataReader>();
                    while (this.hasResults) {
                        DataSet ds = new DataSet();
                        ds.EnforceConstraints = false;
                        DataTable tb = new DataTable();
                        ds.Tables.Add(tb);
                        string[] names = this.GetActiveNames();
                        tb.Load(new Rereader(this.dataReader, false, null), LoadOption.OverwriteChanges);
                        this.buffer.Add(new Rereader(tb.CreateDataReader(), false, names));
                        this.CheckNextResults();
                    }
                }
            }
 
            internal string[] GetActiveNames() {
                string[] names = new string[this.DataReader.FieldCount];
                for (int i = 0, n = this.DataReader.FieldCount; i < n; i++) {
                    names[i] = this.DataReader.GetName(i);
                }
                return names;
            }
 
            public void CompleteUse() {
                this.Buffer();
            }
 
            public void Dispose() {
                if (!this.isDisposed) {
                    // Technically, calling GC.SuppressFinalize is not required because the class does not
                    // have a finalizer, but it does no harm, protects against the case where a finalizer is added
                    // in the future, and prevents an FxCop warning.
                    GC.SuppressFinalize(this);
                    this.isDisposed = true;
                    if (!this.isDataReaderDisposed) {
                        this.isDataReaderDisposed = true;
                        this.dataReader.Dispose();
                    }
                    this.provider.ConnectionManager.ReleaseConnection(this);
                }
            }
 
            internal ObjectReader<TDataReader, TObject> CreateReader<TObject>(
                Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize,
                NamedColumn[] namedColumns,
                object[] globals,
                int nLocals,
                bool disposeDataReader
                ) {
                ObjectReader<TDataReader, TObject> objectReader =
                    new ObjectReader<TDataReader, TObject>(this, namedColumns, globals, this.userArgs, nLocals, disposeDataReader, fnMaterialize);
                this.currentReader = objectReader;
                return objectReader;
            }
 
            internal ObjectReader<TDataReader, TObject> GetNextResult<TObject>(
                Func<ObjectMaterializer<TDataReader>, TObject> fnMaterialize,
                NamedColumn[] namedColumns,
                object[] globals,
                int nLocals,
                bool disposeDataReader
                ) {
                // skip forward to next results
                if (this.buffer != null) {
                    if (this.iNextBufferedReader >= this.buffer.Count) {
                        return null;
                    }
                }
                else {
                    if (this.currentReader != null) {
                        // buffer current reader
                        this.currentReader.Buffer();
                        this.CheckNextResults();
                    }
                    if (!this.hasResults) {
                        return null;
                    }
                }
 
                ObjectReader<TDataReader, TObject> objectReader =
                    new ObjectReader<TDataReader, TObject>(this, namedColumns, globals, this.userArgs, nLocals, disposeDataReader, fnMaterialize);
 
                this.currentReader = objectReader;
                return objectReader;
            }
        }
 
        class Rereader : DbDataReader, IDisposable {
            bool first;
            DbDataReader reader;
            string[] names;
 
            internal Rereader(DbDataReader reader, bool hasCurrentRow, string[] names) {
                this.reader = reader;
                this.first = hasCurrentRow;
                this.names = names;
            }
 
            public override bool Read() {
                if (this.first) {
                    this.first = false;
                    return true;
                }
                return this.reader.Read();
            }
 
            public override string GetName(int i) {
                if (this.names != null) {
                    return this.names[i];
                }
                return reader.GetName(i);
            }
 
            public override void Close() { }
            public override bool NextResult() { return false; }
 
            public override int Depth { get { return reader.Depth; } }
            public override bool IsClosed { get { return reader.IsClosed; } }
            public override int RecordsAffected { get { return reader.RecordsAffected; } }
            public override DataTable GetSchemaTable() { return reader.GetSchemaTable(); }
 
            public override int FieldCount { get { return reader.FieldCount; } }
            public override object this[int i] { get { return reader[i]; } }
            public override object this[string name] { get { return reader[name]; } }
            public override bool GetBoolean(int i) { return reader.GetBoolean(i); }
            public override byte GetByte(int i) { return reader.GetByte(i); }
            public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferOffset, int length) { return reader.GetBytes(i, fieldOffset, buffer, bufferOffset, length); }
            public override char GetChar(int i) { return reader.GetChar(i); }
            public override long GetChars(int i, long fieldOffset, char[] buffer, int bufferOffset, int length) { return reader.GetChars(i, fieldOffset, buffer, bufferOffset, length); }
            public override string GetDataTypeName(int i) { return reader.GetDataTypeName(i); }
            public override DateTime GetDateTime(int i) { return reader.GetDateTime(i); }
            public override decimal GetDecimal(int i) { return reader.GetDecimal(i); }
            public override double GetDouble(int i) { return reader.GetDouble(i); }
            public override Type GetFieldType(int i) { return reader.GetFieldType(i); }
            public override float GetFloat(int i) { return reader.GetFloat(i); }
            public override Guid GetGuid(int i) { return reader.GetGuid(i); }
            public override short GetInt16(int i) { return reader.GetInt16(i); }
            public override int GetInt32(int i) { return reader.GetInt32(i); }
            public override long GetInt64(int i) { return reader.GetInt64(i); }
            public override int GetOrdinal(string name) { return reader.GetOrdinal(name); }
            public override string GetString(int i) { return reader.GetString(i); }
            public override object GetValue(int i) { return reader.GetValue(i); }
            public override int GetValues(object[] values) { return reader.GetValues(values); }
            public override bool IsDBNull(int i) { return reader.IsDBNull(i); }
 
            public override IEnumerator GetEnumerator() {
                return this.reader.GetEnumerator();
            }
            public override bool HasRows {
                get { return this.first || this.reader.HasRows; }
            }
        }
 
        internal class Group<K, T> : IGrouping<K, T>, IEnumerable<T>, IEnumerable {
            K key;
            IEnumerable<T> items;
 
            internal Group(K key, IEnumerable<T> items) {
                this.key = key;
                this.items = items;
            }
 
            K IGrouping<K, T>.Key {
                get { return this.key; }
            }
 
            IEnumerator IEnumerable.GetEnumerator() {
                return (IEnumerator)this.GetEnumerator();
            }
 
            public IEnumerator<T> GetEnumerator() {
                return this.items.GetEnumerator();
            }
        }
 
        internal class OrderedResults<T> : IOrderedEnumerable<T>, IEnumerable<T> {
            List<T> values;
            internal OrderedResults(IEnumerable<T> results) {
                this.values = results as List<T>;
                if (this.values == null)
                    this.values = new List<T>(results);
            }
            IOrderedEnumerable<T> IOrderedEnumerable<T>.CreateOrderedEnumerable<K>(Func<T, K> keySelector, IComparer<K> comparer, bool descending) {
                throw Error.NotSupported();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return ((IEnumerable)this.values).GetEnumerator();
            }
            IEnumerator<T> IEnumerable<T>.GetEnumerator() {
                return this.values.GetEnumerator();
            }
        }
    }
#endif
}