Merge pull request #4621 from alexanderkyte/strdup_env
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / DataContext.cs
index 2eaa680c1633f5ef70fc7d1495da4c2abeaec0ce..db68b96777b3473655a9a97785e4452b77f7157f 100644 (file)
@@ -28,36 +28,32 @@ using System;
 using System.Collections;\r
 using System.Data;\r
 using System.Data.Common;\r
+using System.Data.Linq;\r
 using System.Data.Linq.Mapping;\r
+using System.Linq.Expressions;\r
 using System.Collections.Generic;\r
 using System.IO;\r
 using System.Linq;\r
 using System.Reflection;\r
+using System.Reflection.Emit;\r
 \r
 #if MONO_STRICT\r
-using System.Data.Linq.Implementation;\r
-using System.Data.Linq.Sugar;\r
-using System.Data.Linq.Identity;\r
-using DbLinq.Util;\r
-using AttributeMappingSource = System.Data.Linq.Mapping.AttributeMappingSource;\r
-using MappingContext = System.Data.Linq.Mapping.MappingContext;\r
-using DbLinq;\r
+using AttributeMappingSource  = System.Data.Linq.Mapping.AttributeMappingSource;\r
 #else\r
-using DbLinq.Data.Linq.Implementation;\r
-using DbLinq.Data.Linq.Sugar;\r
-using DbLinq.Data.Linq.Identity;\r
-using DbLinq.Util;\r
-using AttributeMappingSource = DbLinq.Data.Linq.Mapping.AttributeMappingSource;\r
-using MappingContext = DbLinq.Data.Linq.Mapping.MappingContext;\r
-using System.Data.Linq;\r
+using AttributeMappingSource  = DbLinq.Data.Linq.Mapping.AttributeMappingSource;\r
 #endif\r
 \r
-using DbLinq.Factory;\r
-using DbLinq.Vendor;\r
+using DbLinq;\r
+using DbLinq.Data.Linq;\r
 using DbLinq.Data.Linq.Database;\r
 using DbLinq.Data.Linq.Database.Implementation;\r
-using System.Linq.Expressions;\r
-using System.Reflection.Emit;\r
+using DbLinq.Data.Linq.Identity;\r
+using DbLinq.Data.Linq.Implementation;\r
+using DbLinq.Data.Linq.Mapping;\r
+using DbLinq.Data.Linq.Sugar;\r
+using DbLinq.Factory;\r
+using DbLinq.Util;\r
+using DbLinq.Vendor;\r
 \r
 #if MONO_STRICT\r
 namespace System.Data.Linq\r
@@ -68,7 +64,7 @@ namespace DbLinq.Data.Linq
     public partial class DataContext : IDisposable\r
     {\r
         //private readonly Dictionary<string, ITable> _tableMap = new Dictionary<string, ITable>();\r
-               private readonly Dictionary<Type, ITable> _tableMap = new Dictionary<Type, ITable>();\r
+        private readonly Dictionary<Type, ITable> _tableMap = new Dictionary<Type, ITable>();\r
 \r
         public MetaModel Mapping { get; private set; }\r
         // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection\r
@@ -83,7 +79,52 @@ namespace DbLinq.Data.Linq
         internal IDatabaseContext DatabaseContext { get; private set; }\r
         // /all properties...\r
 \r
-        private readonly EntityTracker entityTracker = new EntityTracker();\r
+        private bool objectTrackingEnabled = true;\r
+        private bool deferredLoadingEnabled = true;\r
+\r
+        private bool queryCacheEnabled = false;\r
+\r
+        /// <summary>\r
+        /// Disable the QueryCache: this is surely good for rarely used Select, since preparing\r
+        /// the SelectQuery to be cached could require more time than build the sql from scratch.\r
+        /// </summary>\r
+        [DBLinqExtended]\r
+        public bool QueryCacheEnabled \r
+        {\r
+            get { return queryCacheEnabled; }\r
+            set { queryCacheEnabled = value; }\r
+        }\r
+\r
+        private IEntityTracker currentTransactionEntities;\r
+        private IEntityTracker CurrentTransactionEntities\r
+        {\r
+            get\r
+            {\r
+                if (this.currentTransactionEntities == null)\r
+                {\r
+                    if (this.ObjectTrackingEnabled)\r
+                        this.currentTransactionEntities = new EntityTracker();\r
+                    else\r
+                        this.currentTransactionEntities = new DisabledEntityTracker();\r
+                }\r
+                return this.currentTransactionEntities;\r
+            }\r
+        }\r
+\r
+        private IEntityTracker allTrackedEntities;\r
+        private IEntityTracker AllTrackedEntities\r
+        {\r
+            get\r
+            {\r
+                if (this.allTrackedEntities == null)\r
+                {\r
+                    allTrackedEntities = ObjectTrackingEnabled\r
+                        ? (IEntityTracker) new EntityTracker()\r
+                        : (IEntityTracker) new DisabledEntityTracker();\r
+                }\r
+                return this.allTrackedEntities;\r
+            }\r
+        }\r
 \r
         private IIdentityReaderFactory identityReaderFactory;\r
         private readonly IDictionary<Type, IIdentityReader> identityReaders = new Dictionary<Type, IIdentityReader>();\r
@@ -99,21 +140,45 @@ namespace DbLinq.Data.Linq
 \r
         public DataContext(IDbConnection connection, MappingSource mapping)\r
         {\r
+            Profiler.At("START DataContext(IDbConnection, MappingSource)");\r
             Init(new DatabaseContext(connection), mapping, null);\r
+            Profiler.At("END DataContext(IDbConnection, MappingSource)");\r
         }\r
 \r
         public DataContext(IDbConnection connection)\r
         {\r
+            Profiler.At("START DataContext(IDbConnection)");\r
             if (connection == null)\r
                 throw new ArgumentNullException("connection");\r
 \r
             Init(new DatabaseContext(connection), null, null);\r
+            Profiler.At("END DataContext(IDbConnection)");\r
         }\r
 \r
         [DbLinqToDo]\r
         public DataContext(string fileOrServerOrConnection, MappingSource mapping)\r
         {\r
-            throw new NotImplementedException();\r
+            Profiler.At("START DataContext(string, MappingSource)");\r
+            if (fileOrServerOrConnection == null)\r
+                throw new ArgumentNullException("fileOrServerOrConnection");\r
+            if (mapping == null)\r
+                throw new ArgumentNullException("mapping");\r
+\r
+            if (File.Exists(fileOrServerOrConnection))\r
+                throw new NotImplementedException("File names not supported.");\r
+\r
+            // Is this a decent server name check?\r
+            // It assumes that the connection string will have at least 2\r
+            // parameters (separated by ';')\r
+            if (!fileOrServerOrConnection.Contains(";"))\r
+                throw new NotImplementedException("Server name not supported.");\r
+\r
+            // Assume it's a connection string...\r
+            IVendor ivendor = GetVendor(ref fileOrServerOrConnection);\r
+\r
+            IDbConnection dbConnection = ivendor.CreateDbConnection(fileOrServerOrConnection);\r
+            Init(new DatabaseContext(dbConnection), mapping, ivendor);\r
+            Profiler.At("END DataContext(string, MappingSource)");\r
         }\r
 \r
         /// <summary>\r
@@ -127,23 +192,25 @@ namespace DbLinq.Data.Linq
         /// </summary>\r
         /// <param name="connectionString">specifies file or server connection</param>\r
         [DbLinqToDo]\r
-        public DataContext(string connectionString)\r
+        public DataContext(string fileOrServerOrConnection)\r
         {\r
-            IVendor ivendor = GetVendor(connectionString);\r
+            Profiler.At("START DataContext(string)");\r
+            IVendor ivendor = GetVendor(ref fileOrServerOrConnection);\r
 \r
-            IDbConnection dbConnection = ivendor.CreateDbConnection(connectionString);\r
+            IDbConnection dbConnection = ivendor.CreateDbConnection(fileOrServerOrConnection);\r
             Init(new DatabaseContext(dbConnection), null, ivendor);\r
 \r
+            Profiler.At("END DataContext(string)");\r
         }\r
 \r
-        private IVendor GetVendor(string connectionString)\r
+        private IVendor GetVendor(ref string connectionString)\r
         {\r
             if (connectionString == null)\r
                 throw new ArgumentNullException("connectionString");\r
 \r
             Assembly assy;\r
             string vendorClassToLoad;\r
-            GetVendorInfo(connectionString, out assy, out vendorClassToLoad);\r
+            GetVendorInfo(ref connectionString, out assy, out vendorClassToLoad);\r
 \r
             var types =\r
                 from type in assy.GetTypes()\r
@@ -164,23 +231,23 @@ namespace DbLinq.Data.Linq
             return (IVendor) Activator.CreateInstance(types.First());\r
         }\r
 \r
-        private void GetVendorInfo(string connectionString, out Assembly assembly, out string typeName)\r
+        private void GetVendorInfo(ref string connectionString, out Assembly assembly, out string typeName)\r
         {\r
             System.Text.RegularExpressions.Regex reProvider\r
-                = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+)");\r
+                = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+);?");\r
 \r
-            string assemblyFile = null;\r
+            string assemblyName = null;\r
             string vendor;\r
             if (!reProvider.IsMatch(connectionString))\r
             {\r
                 vendor       = "SqlServer";\r
-                assemblyFile = "DbLinq.SqlServer.dll";\r
+                assemblyName = "DbLinq.SqlServer";\r
             }\r
             else\r
             {\r
                 var match    = reProvider.Match(connectionString);\r
                 vendor       = match.Groups[1].Value;\r
-                assemblyFile = "DbLinq." + vendor + ".dll";\r
+                assemblyName = "DbLinq." + vendor;\r
 \r
                 //plain DbLinq - non MONO: \r
                 //IVendor classes are in DLLs such as "DbLinq.MySql.dll"\r
@@ -201,16 +268,15 @@ namespace DbLinq.Data.Linq
 #if MONO_STRICT\r
                 assembly = typeof (DataContext).Assembly; // System.Data.Linq.dll\r
 #else\r
-                //TODO: check if DLL is already loaded?\r
-                assembly = Assembly.LoadFrom(assemblyFile);\r
+                assembly = Assembly.Load(assemblyName);\r
 #endif\r
             }\r
             catch (Exception e)\r
             {\r
                 throw new ArgumentException(\r
                         string.Format(\r
-                            "Unable to load the `{0}' DbLinq vendor within assembly `{1}'.",\r
-                            assemblyFile, vendor),\r
+                            "Unable to load the `{0}' DbLinq vendor within assembly '{1}.dll'.",\r
+                            assemblyName, vendor),\r
                         "connectionString", e);\r
             }\r
         }\r
@@ -224,13 +290,16 @@ namespace DbLinq.Data.Linq
             if (databaseContext.Connection.ConnectionString == null)\r
                 throw new NullReferenceException();\r
 \r
+            string connectionString = databaseContext.Connection.ConnectionString;\r
             _VendorProvider = ObjectFactory.Get<IVendorProvider>();\r
             Vendor = vendor ?? \r
-                (databaseContext.Connection.ConnectionString != null\r
-                    ? GetVendor(databaseContext.Connection.ConnectionString)\r
-                    : null) ??\r
+                (connectionString != null ? GetVendor(ref connectionString) : null) ??\r
+#if MOBILE\r
+                _VendorProvider.FindVendorByProviderType(typeof(DbLinq.Sqlite.SqliteSqlProvider));\r
+#else\r
                 _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider));\r
-\r
+#endif\r
+            \r
             DatabaseContext = databaseContext;\r
 \r
             MemberModificationHandler = ObjectFactory.Create<IMemberModificationHandler>(); // not a singleton: object is stateful\r
@@ -248,56 +317,54 @@ namespace DbLinq.Data.Linq
             Mapping = mappingSource.GetModel(GetType());\r
         }\r
 \r
-               /// <summary>\r
-               /// Checks if the table is allready mapped or maps it if not.\r
-               /// </summary>\r
-               /// <param name="tableType">Type of the table.</param>\r
-               /// <exception cref="InvalidOperationException">Thrown if the table is not mappable.</exception>\r
-               private void CheckTableMapping(Type tableType)\r
-               {\r
-                       //This will throw an exception if the table is not found\r
-                       if(Mapping.GetTable(tableType) == null)\r
-                       {\r
-                               throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table.");\r
-                       }\r
-               }\r
+        /// <summary>\r
+        /// Checks if the table is allready mapped or maps it if not.\r
+        /// </summary>\r
+        /// <param name="tableType">Type of the table.</param>\r
+        /// <exception cref="InvalidOperationException">Thrown if the table is not mappable.</exception>\r
+        private void CheckTableMapping(Type tableType)\r
+        {\r
+            //This will throw an exception if the table is not found\r
+            if(Mapping.GetTable(tableType) == null)\r
+            {\r
+                throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table.");\r
+            }\r
+        }\r
 \r
-               /// <summary>\r
-               /// Returns a Table for the type TEntity.\r
-               /// </summary>\r
-               /// <exception cref="InvalidOperationException">If the type TEntity is not mappable as a Table.</exception>\r
-               /// <typeparam name="TEntity">The table type.</typeparam>\r
-       public Table<TEntity> GetTable<TEntity>() where TEntity : class\r
+        /// <summary>\r
+        /// Returns a Table for the type TEntity.\r
+        /// </summary>\r
+        /// <exception cref="InvalidOperationException">If the type TEntity is not mappable as a Table.</exception>\r
+        /// <typeparam name="TEntity">The table type.</typeparam>\r
+        public Table<TEntity> GetTable<TEntity>() where TEntity : class\r
         {\r
             return (Table<TEntity>)GetTable(typeof(TEntity));\r
         }\r
 \r
-               /// <summary>\r
-               /// Returns a Table for the given type.\r
-               /// </summary>\r
-               /// <param name="type">The table type.</param>\r
-               /// <exception cref="InvalidOperationException">If the type is not mappable as a Table.</exception>\r
+        /// <summary>\r
+        /// Returns a Table for the given type.\r
+        /// </summary>\r
+        /// <param name="type">The table type.</param>\r
+        /// <exception cref="InvalidOperationException">If the type is not mappable as a Table.</exception>\r
         public ITable GetTable(Type type)\r
         {\r
-            lock (_tableMap)\r
-            {\r
-                ITable tableExisting;\r
-                               if (_tableMap.TryGetValue(type, out tableExisting))\r
-                    return tableExisting;\r
-\r
-                               //Check for table mapping\r
-                               CheckTableMapping(type);\r
-\r
-                var tableNew = Activator.CreateInstance(\r
-                                  typeof(Table<>).MakeGenericType(type)\r
-                                  , BindingFlags.NonPublic | BindingFlags.Instance\r
-                                  , null\r
-                                  , new object[] { this }\r
-                                  , System.Globalization.CultureInfo.CurrentCulture) as ITable;\r
-\r
-                _tableMap[type] = tableNew;\r
-                return tableNew;\r
-            }\r
+            Profiler.At("DataContext.GetTable(typeof({0}))", type != null ? type.Name : null);\r
+            ITable tableExisting;\r
+            if (_tableMap.TryGetValue(type, out tableExisting))\r
+                return tableExisting;\r
+\r
+            //Check for table mapping\r
+            CheckTableMapping(type);\r
+\r
+            var tableNew = Activator.CreateInstance(\r
+                              typeof(Table<>).MakeGenericType(type)\r
+                              , BindingFlags.NonPublic | BindingFlags.Instance\r
+                              , null\r
+                              , new object[] { this }\r
+                              , System.Globalization.CultureInfo.CurrentCulture) as ITable;\r
+\r
+            _tableMap[type] = tableNew;\r
+            return tableNew;\r
         }\r
 \r
         public void SubmitChanges()\r
@@ -327,42 +394,190 @@ namespace DbLinq.Data.Linq
         /// <param name="failureMode"></param>\r
         public virtual void SubmitChanges(ConflictMode failureMode)\r
         {\r
+            if (this.objectTrackingEnabled == false)\r
+                throw new InvalidOperationException("Object tracking is not enabled for the current data context instance.");\r
             using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us\r
-            using (IDatabaseTransaction transaction = DatabaseContext.Transaction())\r
             {\r
-                var queryContext = new QueryContext(this);\r
-                var entityTracks = entityTracker.EnumerateAll().ToList();\r
-                foreach (var entityTrack in entityTracks)\r
+                if (Transaction != null)\r
+                    SubmitChangesImpl(failureMode);\r
+                else\r
                 {\r
-                    switch (entityTrack.EntityState)\r
+                    using (IDbTransaction transaction = DatabaseContext.CreateTransaction())\r
                     {\r
-                        case EntityState.ToInsert:\r
-                            var insertQuery = QueryBuilder.GetInsertQuery(entityTrack.Entity, queryContext);\r
-                            QueryRunner.Insert(entityTrack.Entity, insertQuery);\r
-                            Register(entityTrack.Entity);\r
-                            break;\r
-                        case EntityState.ToWatch:\r
-                            if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
-                            {\r
-                                var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entityTrack.Entity, Mapping);\r
-                                var updateQuery = QueryBuilder.GetUpdateQuery(entityTrack.Entity, modifiedMembers, queryContext);\r
-                                QueryRunner.Update(entityTrack.Entity, updateQuery, modifiedMembers);\r
-\r
-                                RegisterUpdateAgain(entityTrack.Entity);\r
-                            }\r
-                            break;\r
-                        case EntityState.ToDelete:\r
-                            var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);\r
-                            QueryRunner.Delete(entityTrack.Entity, deleteQuery);\r
-\r
-                            UnregisterDelete(entityTrack.Entity);\r
-                            break;\r
-                        default:\r
-                            throw new ArgumentOutOfRangeException();\r
+                        try\r
+                        {\r
+                            Transaction = (DbTransaction) transaction;\r
+                            SubmitChangesImpl(failureMode);\r
+                            // TODO: handle conflicts (which can only occur when concurrency mode is implemented)\r
+                            transaction.Commit();\r
+                        }\r
+                        finally\r
+                        {\r
+                            Transaction = null;\r
+                        }\r
                     }\r
                 }\r
-                // TODO: handle conflicts (which can only occur when concurrency mode is implemented)\r
-                transaction.Commit();\r
+            }\r
+        }\r
+\r
+        void SubmitChangesImpl(ConflictMode failureMode)\r
+        {\r
+            var queryContext = new QueryContext(this);\r
+\r
+            // There's no sense in updating an entity when it's going to \r
+            // be deleted in the current transaction, so do deletes first.\r
+            foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll().ToList())\r
+            {\r
+                switch (entityTrack.EntityState)\r
+                {\r
+                    case EntityState.ToDelete:\r
+                        var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);\r
+                        QueryRunner.Delete(entityTrack.Entity, deleteQuery);\r
+\r
+                        UnregisterDelete(entityTrack.Entity);\r
+                        AllTrackedEntities.RegisterToDelete(entityTrack.Entity);\r
+                        AllTrackedEntities.RegisterDeleted(entityTrack.Entity);\r
+                        break;\r
+                    default:\r
+                        // ignore.\r
+                        break;\r
+                }\r
+            }\r
+            foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll()\r
+                    .Concat(AllTrackedEntities.EnumerateAll())\r
+                    .ToList())\r
+            {\r
+                switch (entityTrack.EntityState)\r
+                {\r
+                    case EntityState.ToInsert:\r
+                        foreach (var toInsert in GetReferencedObjects(entityTrack.Entity))\r
+                        {\r
+                            InsertEntity(toInsert, queryContext);\r
+                        }\r
+                        break;\r
+                    case EntityState.ToWatch:\r
+                        foreach (var toUpdate in GetReferencedObjects(entityTrack.Entity))\r
+                        {\r
+                            UpdateEntity(toUpdate, queryContext);\r
+                        }\r
+                        break;\r
+                    default:\r
+                        throw new ArgumentOutOfRangeException();\r
+                }\r
+            }\r
+        }\r
+\r
+        private IEnumerable<object> GetReferencedObjects(object value)\r
+        {\r
+            var values = new EntitySet<object>();\r
+            FillReferencedObjects(value, values);\r
+            return values;\r
+        }\r
+\r
+        // Breadth-first traversal of an object graph\r
+        private void FillReferencedObjects(object parent, EntitySet<object> values)\r
+        {\r
+            if (parent == null)\r
+                return;\r
+            var children = new Queue<object>();\r
+                       children.Enqueue(parent);\r
+                       while (children.Count > 0)\r
+                       {\r
+                object value = children.Dequeue();\r
+                values.Add(value);\r
+                IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(value.GetType()).Associations.Where(a => !a.IsForeignKey);\r
+                if (associationList.Any())\r
+                           {\r
+                                   foreach (MetaAssociation association in associationList)\r
+                    {\r
+                        var memberData = association.ThisMember;\r
+                        var entitySetValue = memberData.Member.GetMemberValue(value);\r
+\r
+                        if (entitySetValue != null)\r
+                        {\r
+                                                   var hasLoadedOrAssignedValues = entitySetValue.GetType().GetProperty("HasLoadedOrAssignedValues");\r
+                                                   if (!((bool)hasLoadedOrAssignedValues.GetValue(entitySetValue, null)))\r
+                                                           continue;   // execution deferred; ignore.\r
+                                                   foreach (var o in ((IEnumerable)entitySetValue))\r
+                                                           children.Enqueue(o);\r
+                                           }\r
+                    }\r
+                }\r
+                       }\r
+        }\r
+\r
+        private void InsertEntity(object entity, QueryContext queryContext)\r
+        {\r
+            var insertQuery = QueryBuilder.GetInsertQuery(entity, queryContext);\r
+            QueryRunner.Insert(entity, insertQuery);\r
+            Register(entity);\r
+            UpdateReferencedObjects(entity);\r
+            MoveToAllTrackedEntities(entity, true);\r
+        }\r
+\r
+        private void UpdateEntity(object entity, QueryContext queryContext)\r
+        {\r
+            if (!AllTrackedEntities.ContainsReference(entity))\r
+                InsertEntity(entity, queryContext);\r
+            else if (MemberModificationHandler.IsModified(entity, Mapping))\r
+            {\r
+                var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entity, Mapping);\r
+                var updateQuery = QueryBuilder.GetUpdateQuery(entity, modifiedMembers, queryContext);\r
+                QueryRunner.Update(entity, updateQuery, modifiedMembers);\r
+\r
+                RegisterUpdateAgain(entity);\r
+                UpdateReferencedObjects(entity);\r
+                MoveToAllTrackedEntities(entity, false);\r
+            }\r
+        }\r
+\r
+        private void UpdateReferencedObjects(object root)\r
+        {\r
+            var metaType = Mapping.GetMetaType(root.GetType());\r
+            foreach (var assoc in metaType.Associations)\r
+            {\r
+                var memberData = assoc.ThisMember;\r
+                               //This is not correct - AutoSyncing applies to auto-updating columns, such as a TimeStamp, not to foreign key associations, which is always automatically synched\r
+                               //Confirmed against default .NET l2sql - association columns are always set, even if AutoSync==AutoSync.Never\r
+                               //if (memberData.Association.ThisKey.Any(m => (m.AutoSync != AutoSync.Always) && (m.AutoSync != sync)))\r
+                //    continue;\r
+                var oks = memberData.Association.OtherKey.Select(m => m.StorageMember).ToList();\r
+                if (oks.Count == 0)\r
+                    continue;\r
+                var pks = memberData.Association.ThisKey\r
+                    .Select(m => m.StorageMember.GetMemberValue(root))\r
+                    .ToList();\r
+                if (pks.Count != oks.Count)\r
+                    throw new InvalidOperationException(\r
+                        string.Format("Count of primary keys ({0}) doesn't match count of other keys ({1}).",\r
+                            pks.Count, oks.Count));\r
+                var members = memberData.Member.GetMemberValue(root) as IEnumerable;\r
+                if (members == null)\r
+                    continue;\r
+                foreach (var member in members)\r
+                {\r
+                    for (int i = 0; i < pks.Count; ++i)\r
+                    {\r
+                        oks[i].SetMemberValue(member, pks[i]);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        private void MoveToAllTrackedEntities(object entity, bool insert)\r
+        {\r
+            if (!ObjectTrackingEnabled)\r
+                return;\r
+            if (CurrentTransactionEntities.ContainsReference(entity))\r
+            {\r
+                CurrentTransactionEntities.RegisterToDelete(entity);\r
+                if (!insert)\r
+                    CurrentTransactionEntities.RegisterDeleted(entity);\r
+            }\r
+            if (!AllTrackedEntities.ContainsReference(entity))\r
+            {\r
+                var identityReader = _GetIdentityReader(entity.GetType());\r
+                AllTrackedEntities.RegisterToWatch(entity, identityReader.GetIdentityKey(entity));\r
             }\r
         }\r
 \r
@@ -391,13 +606,10 @@ namespace DbLinq.Data.Linq
         internal IIdentityReader _GetIdentityReader(Type t)\r
         {\r
             IIdentityReader identityReader;\r
-            lock (identityReaders)\r
+            if (!identityReaders.TryGetValue(t, out identityReader))\r
             {\r
-                if (!identityReaders.TryGetValue(t, out identityReader))\r
-                {\r
-                    identityReader = identityReaderFactory.GetReader(t, this);\r
-                    identityReaders[t] = identityReader;\r
-                }\r
+                identityReader = identityReaderFactory.GetReader(t, this);\r
+                identityReaders[t] = identityReader;\r
             }\r
             return identityReader;\r
         }\r
@@ -411,7 +623,9 @@ namespace DbLinq.Data.Linq
             if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK\r
                 return entity;\r
             // even \r
-            var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);\r
+            var registeredEntityTrack = \r
+                CurrentTransactionEntities.FindByIdentity(identityKey) ??\r
+                AllTrackedEntities.FindByIdentity(identityKey);\r
             if (registeredEntityTrack != null)\r
                 return registeredEntityTrack.Entity;\r
             return null;\r
@@ -440,70 +654,102 @@ namespace DbLinq.Data.Linq
                 return entity;\r
 \r
             // try to find an already registered entity and return it\r
-            var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);\r
+            var registeredEntityTrack = \r
+                CurrentTransactionEntities.FindByIdentity(identityKey) ??\r
+                AllTrackedEntities.FindByIdentity(identityKey);\r
             if (registeredEntityTrack != null)\r
                 return registeredEntityTrack.Entity;\r
 \r
             // otherwise, register and return\r
-            entityTracker.RegisterToWatch(entity, identityKey);\r
+            AllTrackedEntities.RegisterToWatch(entity, identityKey);\r
             return entity;\r
         }\r
 \r
         readonly IDataMapper DataMapper = ObjectFactory.Get<IDataMapper>();\r
-        private void SetEntityRefQueries(object entity)\r
-        {\r
-            Type thisType = entity.GetType();\r
-            IList<MemberInfo> properties = DataMapper.GetEntityRefAssociations(thisType);\r
-\r
-\r
-            foreach (PropertyInfo prop in properties)\r
-            {\r
-                //example of entityRef:Order.Employee\r
-                AssociationAttribute associationInfo = prop.GetAttribute<AssociationAttribute>();\r
-                Type otherTableType = prop.PropertyType;\r
-                IList<MemberInfo> otherPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(otherTableType));\r
-\r
-                if (otherPKs.Count > 1)\r
-                    throw new NotSupportedException("Multiple keys object not supported yet.");\r
-\r
-                var otherTable = GetTable(otherTableType);\r
-\r
-                //ie:EmployeeTerritories.EmployeeID\r
-\r
-                var thisForeignKeyProperty = thisType.GetProperty(associationInfo.ThisKey);\r
-                object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);\r
-\r
-                IEnumerable query = null;\r
-                if (thisForeignKeyValue != null)\r
-                {\r
-                    ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
-                    Expression predicate;\r
-                    if (!(thisForeignKeyProperty.PropertyType.IsNullable()))\r
-                    {\r
-                        predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),\r
-                                                                    Expression.Constant(thisForeignKeyValue));\r
-                    }\r
-                    else\r
-                    {\r
-                        var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");\r
-                        predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),\r
-                                                                 Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));\r
-                    }\r
-\r
-                    query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;\r
-                    //it would be interesting surround the above query with a .Take(1) expression for performance.\r
-                }\r
+               private void SetEntityRefQueries(object entity)\r
+               {\r
+            if (!this.deferredLoadingEnabled)\r
+                return;\r
 \r
+            // BUG: This is ignoring External Mappings from XmlMappingSource.\r
 \r
-                FieldInfo entityRefField = entity.GetType().GetField(associationInfo.Storage, BindingFlags.NonPublic | BindingFlags.Instance);\r
-                object entityRefValue = null;\r
-                if (query != null)\r
-                    entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query);\r
-                else\r
-                    entityRefValue = Activator.CreateInstance(entityRefField.FieldType);\r
-                entityRefField.SetValue(entity, entityRefValue);\r
-            }\r
-        }\r
+                       Type thisType = entity.GetType();\r
+                       IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(entity.GetType()).Associations.Where(a => a.IsForeignKey);\r
+                       foreach (MetaAssociation association in associationList)\r
+                       {\r
+                               //example of entityRef:Order.Employee\r
+                               var memberData = association.ThisMember;\r
+                               Type otherTableType = association.OtherType.Type;\r
+                               ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
+\r
+                               var otherTable = GetTable(otherTableType);\r
+\r
+                               //ie:EmployeeTerritories.EmployeeID\r
+                               var foreignKeys = memberData.Association.ThisKey;\r
+                               BinaryExpression predicate = null;\r
+                               var otherPKs = memberData.Association.OtherKey;\r
+                               IEnumerator<MetaDataMember> otherPKEnumerator = otherPKs.GetEnumerator();\r
+\r
+                               if (otherPKs.Count != foreignKeys.Count)\r
+                                       throw new InvalidOperationException("Foreign keys don't match ThisKey");\r
+                               foreach (MetaDataMember key in foreignKeys)\r
+                               {\r
+                                       otherPKEnumerator.MoveNext();\r
+\r
+                                       var thisForeignKeyProperty = (PropertyInfo)key.Member;\r
+                                       object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);\r
+\r
+                                       if (thisForeignKeyValue != null)\r
+                                       {\r
+                                               BinaryExpression keyPredicate;\r
+                                               if (!(thisForeignKeyProperty.PropertyType.IsNullable()))\r
+                                               {\r
+                                                       keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKEnumerator.Current.Member),\r
+                                                                                                                                               Expression.Constant(thisForeignKeyValue));\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");\r
+                                                       keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKEnumerator.Current.Member),\r
+                                                                                                                                        Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));\r
+                                               }\r
+\r
+                                               if (predicate == null)\r
+                                                       predicate = keyPredicate;\r
+                                               else\r
+                                                       predicate = Expression.And(predicate, keyPredicate);\r
+                                       }\r
+                               }\r
+                               IEnumerable query = null;\r
+                               if (predicate != null)\r
+                               {\r
+                                       query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;\r
+                                       //it would be interesting surround the above query with a .Take(1) expression for performance.\r
+                               }\r
+\r
+                               // If no separate Storage is specified, use the member directly\r
+                               MemberInfo storage = memberData.StorageMember;\r
+                               if (storage == null)\r
+                                       storage = memberData.Member;\r
+\r
+                                // Check that the storage is a field or a writable property\r
+                               if (!(storage is FieldInfo) && !(storage is PropertyInfo && ((PropertyInfo)storage).CanWrite)) {\r
+                                       throw new InvalidOperationException(String.Format(\r
+                                               "Member {0}.{1} is not a field nor a writable property",\r
+                                               storage.DeclaringType, storage.Name));\r
+                               }\r
+\r
+                               Type storageType = storage.GetMemberType();\r
+\r
+                               object entityRefValue = null;\r
+                               if (query != null)\r
+                                       entityRefValue = Activator.CreateInstance(storageType, query);\r
+                               else\r
+                                       entityRefValue = Activator.CreateInstance(storageType);\r
+\r
+                               storage.SetMemberValue(entity, entityRefValue);\r
+                       }\r
+               }\r
 \r
         /// <summary>\r
         /// This method is executed when the entity is being registered. Each EntitySet property has a internal query that can be set using the EntitySet.SetSource method.\r
@@ -512,55 +758,71 @@ namespace DbLinq.Data.Linq
         /// <param name="entity"></param>\r
         private void SetEntitySetsQueries(object entity)\r
         {\r
-            IList<MemberInfo> properties = DataMapper.GetEntitySetAssociations(entity.GetType());\r
-            \r
-            if (properties.Any()) {\r
-                IList<MemberInfo> thisPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(entity.GetType()));\r
-\r
-                if (thisPKs.Count > 1)\r
-                    throw new NotSupportedException("Multiple keys object not supported yet.");\r
+            if (!this.deferredLoadingEnabled)\r
+                return;\r
 \r
-                object primaryKeyValue = (thisPKs.First() as PropertyInfo).GetValue(entity, null);\r
+            // BUG: This is ignoring External Mappings from XmlMappingSource.\r
 \r
+                       IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(entity.GetType()).Associations.Where(a => !a.IsForeignKey);\r
 \r
-                foreach (PropertyInfo prop in properties)\r
+                       if (associationList.Any())\r
+                       {\r
+                               foreach (MetaAssociation association in associationList)\r
                 {\r
-                    //example of entitySet: Employee.EmployeeTerritories\r
-                    var associationInfo = prop.GetAttribute<AssociationAttribute>();\r
-                    Type otherTableType = prop.PropertyType.GetGenericArguments().First();\r
+                                       //example of entitySet: Employee.EmployeeTerritories\r
+                                       var memberData = association.ThisMember;\r
+                                       Type otherTableType = association.OtherType.Type;\r
+                    ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
 \r
                     //other table:EmployeeTerritories\r
                     var otherTable = GetTable(otherTableType);\r
-                    //other table member:EmployeeTerritories.EmployeeID\r
-                    var otherTableMember = otherTableType.GetProperty(associationInfo.OtherKey);\r
 \r
-\r
-                    ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
-                    Expression predicate;\r
-                    if (!(otherTableMember.PropertyType.IsNullable()))\r
+                                       var otherKeys = memberData.Association.OtherKey;\r
+                                       var thisKeys = memberData.Association.ThisKey;\r
+                    if (otherKeys.Count != thisKeys.Count)\r
+                        throw new InvalidOperationException("This keys don't match OtherKey");\r
+                    BinaryExpression predicate = null;\r
+                    IEnumerator<MetaDataMember> thisKeyEnumerator = thisKeys.GetEnumerator();\r
+                                       foreach (MetaDataMember otherKey in otherKeys)\r
                     {\r
-                        predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),\r
-                                                                    Expression.Constant(primaryKeyValue));\r
-                    }\r
-                    else\r
-                    {\r
-                        var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");\r
-                        predicate = Expression.Equal(Expression.MakeMemberAccess(\r
-                                                                    Expression.MakeMemberAccess(p, otherTableMember),\r
-                                                                    ValueProperty),\r
-                                                                 Expression.Constant(primaryKeyValue));\r
+                        thisKeyEnumerator.MoveNext();\r
+                        //other table member:EmployeeTerritories.EmployeeID\r
+                                               var otherTableMember = (PropertyInfo)otherKey.Member;\r
+\r
+                        BinaryExpression keyPredicate;\r
+                        if (!(otherTableMember.PropertyType.IsNullable()))\r
+                        {\r
+                            keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),\r
+                                                                        Expression.Constant(thisKeyEnumerator.Current.Member.GetMemberValue(entity)));\r
+                        }\r
+                        else\r
+                        {\r
+                            var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");\r
+                            keyPredicate = Expression.Equal(Expression.MakeMemberAccess(\r
+                                                                        Expression.MakeMemberAccess(p, otherTableMember),\r
+                                                                        ValueProperty),\r
+                                                                     Expression.Constant(thisKeyEnumerator.Current.Member.GetMemberValue(entity)));\r
+                        }\r
+                        if (predicate == null)\r
+                            predicate = keyPredicate;\r
+                        else\r
+                            predicate = Expression.And(predicate, keyPredicate);\r
                     }\r
 \r
                     var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable);\r
 \r
-                    var entitySetValue = prop.GetValue(entity, null);\r
+                                       var entitySetValue = memberData.Member.GetMemberValue(entity);\r
 \r
                     if (entitySetValue == null)\r
                     {\r
-                        entitySetValue = Activator.CreateInstance(prop.PropertyType);\r
-                        prop.SetValue(entity, entitySetValue, null);\r
+                                               entitySetValue = Activator.CreateInstance(memberData.Member.GetMemberType());\r
+                                               memberData.Member.SetMemberValue(entity, entitySetValue);\r
                     }\r
 \r
+                    var hasLoadedOrAssignedValues = entitySetValue.GetType().GetProperty("HasLoadedOrAssignedValues");\r
+                    if ((bool)hasLoadedOrAssignedValues.GetValue(entitySetValue, null))\r
+                        continue;\r
+\r
                     var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource");\r
                     setSourceMethod.Invoke(entitySetValue, new[] { query });\r
                     //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH"))\r
@@ -568,18 +830,17 @@ namespace DbLinq.Data.Linq
             }\r
         }\r
 \r
-        private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)\r
+               private static MethodInfo _WhereMethod;\r
+        internal object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)\r
         {\r
+            if (_WhereMethod == null)\r
+                System.Threading.Interlocked.CompareExchange (ref _WhereMethod, typeof(Queryable).GetMethods().First(m => m.Name == "Where"), null);\r
+\r
             //predicate: other.EmployeeID== "WARTH"\r
             Expression lambdaPredicate = Expression.Lambda(predicate, parameter);\r
             //lambdaPredicate: other=>other.EmployeeID== "WARTH"\r
 \r
-            var whereMethod = typeof(Queryable)\r
-                              .GetMethods().First(m => m.Name == "Where")\r
-                              .MakeGenericMethod(otherTableType);\r
-\r
-\r
-            Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate);\r
+                       Expression call = Expression.Call(_WhereMethod.MakeGenericMethod(otherTableType), otherTable.Expression, lambdaPredicate);\r
             //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")\r
 \r
             return otherTable.Provider.CreateQuery(call);\r
@@ -595,27 +856,35 @@ namespace DbLinq.Data.Linq
         /// <param name="entity"></param>\r
         internal void RegisterInsert(object entity)\r
         {\r
-            entityTracker.RegisterToInsert(entity);\r
+            CurrentTransactionEntities.RegisterToInsert(entity);\r
         }\r
 \r
-        /// <summary>\r
-        /// Registers an entity for update\r
-        /// The entity will be updated only if some of its members have changed after the registration\r
-        /// </summary>\r
-        /// <param name="entity"></param>\r
-        internal void RegisterUpdate(object entity)\r
+        private void DoRegisterUpdate(object entity)\r
         {\r
             if (entity == null)\r
                 throw new ArgumentNullException("entity");\r
 \r
+            if (!this.objectTrackingEnabled)\r
+                return;\r
+\r
             var identityReader = _GetIdentityReader(entity.GetType());\r
             var identityKey = identityReader.GetIdentityKey(entity);\r
-            Console.WriteLine("# identityKey={0}", identityKey == null ? "<null>" : identityKey.ToString());\r
             // if we have no key, we can not watch\r
-            if (identityKey == null)\r
+            if (identityKey == null || identityKey.Keys.Count == 0)\r
                 return;\r
             // register entity\r
-            entityTracker.RegisterToWatch(entity, identityKey);\r
+            AllTrackedEntities.RegisterToWatch(entity, identityKey);\r
+        }\r
+\r
+        /// <summary>\r
+        /// Registers an entity for update\r
+        /// The entity will be updated only if some of its members have changed after the registration\r
+        /// </summary>\r
+        /// <param name="entity"></param>\r
+        internal void RegisterUpdate(object entity)\r
+        {\r
+            DoRegisterUpdate(entity);\r
+                       MemberModificationHandler.Register(entity, Mapping);\r
         }\r
 \r
         /// <summary>\r
@@ -625,6 +894,8 @@ namespace DbLinq.Data.Linq
         /// <returns></returns>\r
         internal object Register(object entity)\r
         {\r
+            if (! this.objectTrackingEnabled)\r
+                return entity;\r
             var registeredEntity = _GetOrRegisterEntity(entity);\r
             // the fact of registering again clears the modified state, so we're... clear with that\r
             MemberModificationHandler.Register(registeredEntity, Mapping);\r
@@ -639,7 +910,9 @@ namespace DbLinq.Data.Linq
         /// <param name="entityOriginalState"></param>\r
         internal void RegisterUpdate(object entity, object entityOriginalState)\r
         {\r
-            RegisterUpdate(entity);\r
+            if (!this.objectTrackingEnabled)\r
+                return;\r
+            DoRegisterUpdate(entity);\r
             MemberModificationHandler.Register(entity, entityOriginalState, Mapping);\r
         }\r
 \r
@@ -649,6 +922,8 @@ namespace DbLinq.Data.Linq
         /// <param name="entity"></param>\r
         internal void RegisterUpdateAgain(object entity)\r
         {\r
+            if (!this.objectTrackingEnabled)\r
+                return;\r
             MemberModificationHandler.ClearModified(entity, Mapping);\r
         }\r
 \r
@@ -658,7 +933,9 @@ namespace DbLinq.Data.Linq
         /// <param name="entity"></param>\r
         internal void RegisterDelete(object entity)\r
         {\r
-            entityTracker.RegisterToDelete(entity);\r
+            if (!this.objectTrackingEnabled)\r
+                return;\r
+            CurrentTransactionEntities.RegisterToDelete(entity);\r
         }\r
 \r
         /// <summary>\r
@@ -667,7 +944,9 @@ namespace DbLinq.Data.Linq
         /// <param name="entity"></param>\r
         internal void UnregisterDelete(object entity)\r
         {\r
-            entityTracker.RegisterDeleted(entity);\r
+            if (!this.objectTrackingEnabled)\r
+                return;\r
+            CurrentTransactionEntities.RegisterDeleted(entity);\r
         }\r
 \r
         #endregion\r
@@ -681,7 +960,8 @@ namespace DbLinq.Data.Linq
             var inserts = new List<object>();\r
             var updates = new List<object>();\r
             var deletes = new List<object>();\r
-            foreach (var entityTrack in entityTracker.EnumerateAll())\r
+            foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll()\r
+                    .Concat(AllTrackedEntities.EnumerateAll()))\r
             {\r
                 switch (entityTrack.EntityState)\r
                 {\r
@@ -714,7 +994,7 @@ namespace DbLinq.Data.Linq
         /// <summary>\r
         /// Execute raw SQL query and return object\r
         /// </summary>\r
-        public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : class, new()\r
+        public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : new()\r
         {\r
             if (query == null)\r
                 throw new ArgumentNullException("query");\r
@@ -723,7 +1003,7 @@ namespace DbLinq.Data.Linq
         }\r
 \r
         private IEnumerable<TResult> CreateExecuteQueryEnumerable<TResult>(string query, object[] parameters)\r
-            where TResult : class, new()\r
+            where TResult : new()\r
         {\r
             foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters))\r
                 yield return result;\r
@@ -731,7 +1011,6 @@ namespace DbLinq.Data.Linq
 \r
         public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters)\r
         {\r
-            Console.WriteLine("# ExecuteQuery: query={0}", query != null ? query : "<null>");\r
             if (elementType == null)\r
                 throw new ArgumentNullException("elementType");\r
             if (query == null)\r
@@ -746,9 +1025,16 @@ namespace DbLinq.Data.Linq
         /// Gets or sets the load options\r
         /// </summary>\r
         [DbLinqToDo]\r
-        public DataLoadOptions LoadOptions { get; set; }\r
+               public DataLoadOptions LoadOptions\r
+               {\r
+                       get { throw new NotImplementedException(); }\r
+                       set { throw new NotImplementedException(); }\r
+               }\r
 \r
-        public DbTransaction Transaction { get; set; }\r
+        public DbTransaction Transaction {\r
+            get { return (DbTransaction) DatabaseContext.CurrentTransaction; }\r
+            set { DatabaseContext.CurrentTransaction = value; }\r
+        }\r
 \r
         /// <summary>\r
         /// Runs the given reader and returns columns.\r
@@ -788,6 +1074,9 @@ namespace DbLinq.Data.Linq
         {\r
             //connection closing should not be done here.\r
             //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx\r
+\r
+                       //We own the instance of MemberModificationHandler - we must unregister listeners of entities we attached to\r
+                       MemberModificationHandler.UnregisterAll();\r
         }\r
 \r
         [DbLinqToDo]\r
@@ -856,12 +1145,15 @@ namespace DbLinq.Data.Linq
             }\r
         }\r
 \r
-\r
-        [DbLinqToDo]\r
         public bool ObjectTrackingEnabled\r
         {\r
-            get { throw new NotImplementedException(); }\r
-            set { throw new NotImplementedException(); }\r
+            get { return this.objectTrackingEnabled; }\r
+            set \r
+            {\r
+                if (this.currentTransactionEntities != null && value != this.objectTrackingEnabled)\r
+                    throw new InvalidOperationException("Data context options cannot be modified after results have been returned from a query.");\r
+                this.objectTrackingEnabled = value;\r
+            }\r
         }\r
 \r
         [DbLinqToDo]\r
@@ -871,11 +1163,15 @@ namespace DbLinq.Data.Linq
             set { throw new NotImplementedException(); }\r
         }\r
 \r
-        [DbLinqToDo]\r
         public bool DeferredLoadingEnabled\r
         {\r
-            get { throw new NotImplementedException(); }\r
-            set { throw new NotImplementedException(); }\r
+            get { return this.deferredLoadingEnabled; }\r
+            set\r
+            {\r
+                if (this.currentTransactionEntities != null && value != this.deferredLoadingEnabled)\r
+                    throw new InvalidOperationException("Data context options cannot be modified after results have been returned from a query.");\r
+                this.deferredLoadingEnabled = value;\r
+            }\r
         }\r
 \r
         [DbLinqToDo]\r
@@ -886,6 +1182,16 @@ namespace DbLinq.Data.Linq
 \r
         [DbLinqToDo]\r
         public DbCommand GetCommand(IQueryable query)\r
+        {\r
+            DbCommand dbCommand = GetIDbCommand(query) as DbCommand;\r
+            if (dbCommand == null)\r
+                throw new InvalidOperationException();\r
+\r
+            return dbCommand;\r
+        }\r
+\r
+        [DBLinqExtended]\r
+        public IDbCommand GetIDbCommand(IQueryable query)\r
         {\r
             if (query == null)\r
                 throw new ArgumentNullException("query");\r
@@ -894,11 +1200,29 @@ namespace DbLinq.Data.Linq
             if (qp == null)\r
                 throw new InvalidOperationException();\r
 \r
-            IDbCommand dbCommand = qp.GetQuery(null).GetCommand().Command;\r
-            if (!(dbCommand is DbCommand))\r
-                throw new InvalidOperationException();\r
+            if (qp.ExpressionChain.Expressions.Count == 0)\r
+                qp.ExpressionChain.Expressions.Add(CreateDefaultQuery(query));\r
 \r
-            return (DbCommand)dbCommand;\r
+            return qp.GetQuery(null).GetCommand().Command;\r
+        }\r
+\r
+        private Expression CreateDefaultQuery(IQueryable query)\r
+        {\r
+            // Manually create the expression tree for: IQueryable<TableType>.Select(e => e)\r
+            var identityParameter = Expression.Parameter(query.ElementType, "e");\r
+            var identityBody = Expression.Lambda(\r
+                typeof(Func<,>).MakeGenericType(query.ElementType, query.ElementType),\r
+                identityParameter,\r
+                new[] { identityParameter }\r
+            );\r
+\r
+            return Expression.Call(\r
+                typeof(Queryable),\r
+                "Select",\r
+                new[] { query.ElementType, query.ElementType },\r
+                query.Expression,\r
+                Expression.Quote(identityBody)\r
+            );\r
         }\r
 \r
         [DbLinqToDo]\r