Merge pull request #960 from ermshiperete/ShowHelp
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / DataContext.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Collections;\r
29 using System.Data;\r
30 using System.Data.Common;\r
31 using System.Data.Linq;\r
32 using System.Data.Linq.Mapping;\r
33 using System.Linq.Expressions;\r
34 using System.Collections.Generic;\r
35 using System.IO;\r
36 using System.Linq;\r
37 using System.Reflection;\r
38 using System.Reflection.Emit;\r
39 \r
40 #if MONO_STRICT\r
41 using AttributeMappingSource  = System.Data.Linq.Mapping.AttributeMappingSource;\r
42 #else\r
43 using AttributeMappingSource  = DbLinq.Data.Linq.Mapping.AttributeMappingSource;\r
44 #endif\r
45 \r
46 using DbLinq;\r
47 using DbLinq.Data.Linq;\r
48 using DbLinq.Data.Linq.Database;\r
49 using DbLinq.Data.Linq.Database.Implementation;\r
50 using DbLinq.Data.Linq.Identity;\r
51 using DbLinq.Data.Linq.Implementation;\r
52 using DbLinq.Data.Linq.Mapping;\r
53 using DbLinq.Data.Linq.Sugar;\r
54 using DbLinq.Factory;\r
55 using DbLinq.Util;\r
56 using DbLinq.Vendor;\r
57 \r
58 #if MONO_STRICT\r
59 namespace System.Data.Linq\r
60 #else\r
61 namespace DbLinq.Data.Linq\r
62 #endif\r
63 {\r
64     public partial class DataContext : IDisposable\r
65     {\r
66         //private readonly Dictionary<string, ITable> _tableMap = new Dictionary<string, ITable>();\r
67         private readonly Dictionary<Type, ITable> _tableMap = new Dictionary<Type, ITable>();\r
68 \r
69         public MetaModel Mapping { get; private set; }\r
70         // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection\r
71         //              WTF?\r
72         public DbConnection Connection { get { return DatabaseContext.Connection as DbConnection; } }\r
73 \r
74         // all properties below are set public to optionally be injected\r
75         internal IVendor Vendor { get; set; }\r
76         internal IQueryBuilder QueryBuilder { get; set; }\r
77         internal IQueryRunner QueryRunner { get; set; }\r
78         internal IMemberModificationHandler MemberModificationHandler { get; set; }\r
79         internal IDatabaseContext DatabaseContext { get; private set; }\r
80         // /all properties...\r
81 \r
82         private bool objectTrackingEnabled = true;\r
83         private bool deferredLoadingEnabled = true;\r
84 \r
85         private bool queryCacheEnabled = false;\r
86 \r
87         /// <summary>\r
88         /// Disable the QueryCache: this is surely good for rarely used Select, since preparing\r
89         /// the SelectQuery to be cached could require more time than build the sql from scratch.\r
90         /// </summary>\r
91         [DBLinqExtended]\r
92         public bool QueryCacheEnabled \r
93         {\r
94             get { return queryCacheEnabled; }\r
95             set { queryCacheEnabled = value; }\r
96         }\r
97 \r
98         private IEntityTracker currentTransactionEntities;\r
99         private IEntityTracker CurrentTransactionEntities\r
100         {\r
101             get\r
102             {\r
103                 if (this.currentTransactionEntities == null)\r
104                 {\r
105                     if (this.ObjectTrackingEnabled)\r
106                         this.currentTransactionEntities = new EntityTracker();\r
107                     else\r
108                         this.currentTransactionEntities = new DisabledEntityTracker();\r
109                 }\r
110                 return this.currentTransactionEntities;\r
111             }\r
112         }\r
113 \r
114         private IEntityTracker allTrackedEntities;\r
115         private IEntityTracker AllTrackedEntities\r
116         {\r
117             get\r
118             {\r
119                 if (this.allTrackedEntities == null)\r
120                 {\r
121                     allTrackedEntities = ObjectTrackingEnabled\r
122                         ? (IEntityTracker) new EntityTracker()\r
123                         : (IEntityTracker) new DisabledEntityTracker();\r
124                 }\r
125                 return this.allTrackedEntities;\r
126             }\r
127         }\r
128 \r
129         private IIdentityReaderFactory identityReaderFactory;\r
130         private readonly IDictionary<Type, IIdentityReader> identityReaders = new Dictionary<Type, IIdentityReader>();\r
131 \r
132         /// <summary>\r
133         /// The default behavior creates one MappingContext.\r
134         /// </summary>\r
135         [DBLinqExtended]\r
136         internal virtual MappingContext _MappingContext { get; set; }\r
137 \r
138         [DBLinqExtended]\r
139         internal IVendorProvider _VendorProvider { get; set; }\r
140 \r
141         public DataContext(IDbConnection connection, MappingSource mapping)\r
142         {\r
143             Profiler.At("START DataContext(IDbConnection, MappingSource)");\r
144             Init(new DatabaseContext(connection), mapping, null);\r
145             Profiler.At("END DataContext(IDbConnection, MappingSource)");\r
146         }\r
147 \r
148         public DataContext(IDbConnection connection)\r
149         {\r
150             Profiler.At("START DataContext(IDbConnection)");\r
151             if (connection == null)\r
152                 throw new ArgumentNullException("connection");\r
153 \r
154             Init(new DatabaseContext(connection), null, null);\r
155             Profiler.At("END DataContext(IDbConnection)");\r
156         }\r
157 \r
158         [DbLinqToDo]\r
159         public DataContext(string fileOrServerOrConnection, MappingSource mapping)\r
160         {\r
161             Profiler.At("START DataContext(string, MappingSource)");\r
162             if (fileOrServerOrConnection == null)\r
163                 throw new ArgumentNullException("fileOrServerOrConnection");\r
164             if (mapping == null)\r
165                 throw new ArgumentNullException("mapping");\r
166 \r
167             if (File.Exists(fileOrServerOrConnection))\r
168                 throw new NotImplementedException("File names not supported.");\r
169 \r
170             // Is this a decent server name check?\r
171             // It assumes that the connection string will have at least 2\r
172             // parameters (separated by ';')\r
173             if (!fileOrServerOrConnection.Contains(";"))\r
174                 throw new NotImplementedException("Server name not supported.");\r
175 \r
176             // Assume it's a connection string...\r
177             IVendor ivendor = GetVendor(ref fileOrServerOrConnection);\r
178 \r
179             IDbConnection dbConnection = ivendor.CreateDbConnection(fileOrServerOrConnection);\r
180             Init(new DatabaseContext(dbConnection), mapping, ivendor);\r
181             Profiler.At("END DataContext(string, MappingSource)");\r
182         }\r
183 \r
184         /// <summary>\r
185         /// Construct DataContext, given a connectionString.\r
186         /// To determine which DB type to go against, we look for 'DbLinqProvider=xxx' substring.\r
187         /// If not found, we assume that we are dealing with MS Sql Server.\r
188         /// \r
189         /// Valid values are names of provider DLLs (or any other DLL containing an IVendor implementation)\r
190         /// DbLinqProvider=Mysql\r
191         /// DbLinqProvider=Oracle etc.\r
192         /// </summary>\r
193         /// <param name="connectionString">specifies file or server connection</param>\r
194         [DbLinqToDo]\r
195         public DataContext(string connectionString)\r
196         {\r
197             Profiler.At("START DataContext(string)");\r
198             IVendor ivendor = GetVendor(ref connectionString);\r
199 \r
200             IDbConnection dbConnection = ivendor.CreateDbConnection(connectionString);\r
201             Init(new DatabaseContext(dbConnection), null, ivendor);\r
202 \r
203             Profiler.At("END DataContext(string)");\r
204         }\r
205 \r
206         private IVendor GetVendor(ref string connectionString)\r
207         {\r
208             if (connectionString == null)\r
209                 throw new ArgumentNullException("connectionString");\r
210 \r
211             Assembly assy;\r
212             string vendorClassToLoad;\r
213             GetVendorInfo(ref connectionString, out assy, out vendorClassToLoad);\r
214 \r
215             var types =\r
216                 from type in assy.GetTypes()\r
217                 where type.Name.ToLowerInvariant() == vendorClassToLoad.ToLowerInvariant() &&\r
218                     type.GetInterfaces().Contains(typeof(IVendor)) &&\r
219                     type.GetConstructor(Type.EmptyTypes) != null\r
220                 select type;\r
221             if (!types.Any())\r
222             {\r
223                 throw new ArgumentException(string.Format("Found no IVendor class in assembly `{0}' named `{1}' having a default constructor.",\r
224                     assy.GetName().Name, vendorClassToLoad));\r
225             }\r
226             else if (types.Count() > 1)\r
227             {\r
228                 throw new ArgumentException(string.Format("Found too many IVendor classes in assembly `{0}' named `{1}' having a default constructor.",\r
229                     assy.GetName().Name, vendorClassToLoad));\r
230             }\r
231             return (IVendor) Activator.CreateInstance(types.First());\r
232         }\r
233 \r
234         private void GetVendorInfo(ref string connectionString, out Assembly assembly, out string typeName)\r
235         {\r
236             System.Text.RegularExpressions.Regex reProvider\r
237                 = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+);?");\r
238 \r
239             string assemblyName = null;\r
240             string vendor;\r
241             if (!reProvider.IsMatch(connectionString))\r
242             {\r
243                 vendor       = "SqlServer";\r
244                 assemblyName = "DbLinq.SqlServer";\r
245             }\r
246             else\r
247             {\r
248                 var match    = reProvider.Match(connectionString);\r
249                 vendor       = match.Groups[1].Value;\r
250                 assemblyName = "DbLinq." + vendor;\r
251 \r
252                 //plain DbLinq - non MONO: \r
253                 //IVendor classes are in DLLs such as "DbLinq.MySql.dll"\r
254                 if (vendor.Contains("."))\r
255                 {\r
256                     //already fully qualified DLL name?\r
257                     throw new ArgumentException("Please provide a short name, such as 'MySql', not '" + vendor + "'");\r
258                 }\r
259 \r
260                 //shorten: "DbLinqProvider=X;Server=Y" -> ";Server=Y"\r
261                 connectionString = reProvider.Replace(connectionString, "");\r
262             }\r
263 \r
264             typeName = vendor + "Vendor";\r
265 \r
266             try\r
267             {\r
268 #if MONO_STRICT\r
269                 assembly = typeof (DataContext).Assembly; // System.Data.Linq.dll\r
270 #else\r
271                 assembly = Assembly.Load(assemblyName);\r
272 #endif\r
273             }\r
274             catch (Exception e)\r
275             {\r
276                 throw new ArgumentException(\r
277                         string.Format(\r
278                             "Unable to load the `{0}' DbLinq vendor within assembly '{1}.dll'.",\r
279                             assemblyName, vendor),\r
280                         "connectionString", e);\r
281             }\r
282         }\r
283 \r
284         private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor)\r
285         {\r
286             if (databaseContext == null)\r
287                 throw new ArgumentNullException("databaseContext");\r
288 \r
289             // Yes, .NET throws an NRE for this.  Why it's not ArgumentNullException, I couldn't tell you.\r
290             if (databaseContext.Connection.ConnectionString == null)\r
291                 throw new NullReferenceException();\r
292 \r
293             string connectionString = databaseContext.Connection.ConnectionString;\r
294             _VendorProvider = ObjectFactory.Get<IVendorProvider>();\r
295             Vendor = vendor ?? \r
296                 (connectionString != null ? GetVendor(ref connectionString) : null) ??\r
297 #if MOBILE\r
298                 _VendorProvider.FindVendorByProviderType(typeof(DbLinq.Sqlite.SqliteSqlProvider));\r
299 #else\r
300                 _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider));\r
301 #endif\r
302             \r
303             DatabaseContext = databaseContext;\r
304 \r
305             MemberModificationHandler = ObjectFactory.Create<IMemberModificationHandler>(); // not a singleton: object is stateful\r
306             QueryBuilder = ObjectFactory.Get<IQueryBuilder>();\r
307             QueryRunner = ObjectFactory.Get<IQueryRunner>();\r
308 \r
309             //EntityMap = ObjectFactory.Create<IEntityMap>();\r
310             identityReaderFactory = ObjectFactory.Get<IIdentityReaderFactory>();\r
311 \r
312             _MappingContext = new MappingContext();\r
313 \r
314             // initialize the mapping information\r
315             if (mappingSource == null)\r
316                 mappingSource = new AttributeMappingSource();\r
317             Mapping = mappingSource.GetModel(GetType());\r
318         }\r
319 \r
320         /// <summary>\r
321         /// Checks if the table is allready mapped or maps it if not.\r
322         /// </summary>\r
323         /// <param name="tableType">Type of the table.</param>\r
324         /// <exception cref="InvalidOperationException">Thrown if the table is not mappable.</exception>\r
325         private void CheckTableMapping(Type tableType)\r
326         {\r
327             //This will throw an exception if the table is not found\r
328             if(Mapping.GetTable(tableType) == null)\r
329             {\r
330                 throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table.");\r
331             }\r
332         }\r
333 \r
334         /// <summary>\r
335         /// Returns a Table for the type TEntity.\r
336         /// </summary>\r
337         /// <exception cref="InvalidOperationException">If the type TEntity is not mappable as a Table.</exception>\r
338         /// <typeparam name="TEntity">The table type.</typeparam>\r
339         public Table<TEntity> GetTable<TEntity>() where TEntity : class\r
340         {\r
341             return (Table<TEntity>)GetTable(typeof(TEntity));\r
342         }\r
343 \r
344         /// <summary>\r
345         /// Returns a Table for the given type.\r
346         /// </summary>\r
347         /// <param name="type">The table type.</param>\r
348         /// <exception cref="InvalidOperationException">If the type is not mappable as a Table.</exception>\r
349         public ITable GetTable(Type type)\r
350         {\r
351             Profiler.At("DataContext.GetTable(typeof({0}))", type != null ? type.Name : null);\r
352             ITable tableExisting;\r
353             if (_tableMap.TryGetValue(type, out tableExisting))\r
354                 return tableExisting;\r
355 \r
356             //Check for table mapping\r
357             CheckTableMapping(type);\r
358 \r
359             var tableNew = Activator.CreateInstance(\r
360                               typeof(Table<>).MakeGenericType(type)\r
361                               , BindingFlags.NonPublic | BindingFlags.Instance\r
362                               , null\r
363                               , new object[] { this }\r
364                               , System.Globalization.CultureInfo.CurrentCulture) as ITable;\r
365 \r
366             _tableMap[type] = tableNew;\r
367             return tableNew;\r
368         }\r
369 \r
370         public void SubmitChanges()\r
371         {\r
372             SubmitChanges(ConflictMode.FailOnFirstConflict);\r
373         }\r
374 \r
375         /// <summary>\r
376         /// Pings database\r
377         /// </summary>\r
378         /// <returns></returns>\r
379         public bool DatabaseExists()\r
380         {\r
381             try\r
382             {\r
383                 return Vendor.Ping(this);\r
384             }\r
385             catch (Exception)\r
386             {\r
387                 return false;\r
388             }\r
389         }\r
390 \r
391         /// <summary>\r
392         /// Commits all pending changes to database \r
393         /// </summary>\r
394         /// <param name="failureMode"></param>\r
395         public virtual void SubmitChanges(ConflictMode failureMode)\r
396         {\r
397             if (this.objectTrackingEnabled == false)\r
398                 throw new InvalidOperationException("Object tracking is not enabled for the current data context instance.");\r
399             using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us\r
400             {\r
401                 if (Transaction != null)\r
402                     SubmitChangesImpl(failureMode);\r
403                 else\r
404                 {\r
405                     using (IDbTransaction transaction = DatabaseContext.CreateTransaction())\r
406                     {\r
407                         try\r
408                         {\r
409                             Transaction = (DbTransaction) transaction;\r
410                             SubmitChangesImpl(failureMode);\r
411                             // TODO: handle conflicts (which can only occur when concurrency mode is implemented)\r
412                             transaction.Commit();\r
413                         }\r
414                         finally\r
415                         {\r
416                             Transaction = null;\r
417                         }\r
418                     }\r
419                 }\r
420             }\r
421         }\r
422 \r
423         void SubmitChangesImpl(ConflictMode failureMode)\r
424         {\r
425             var queryContext = new QueryContext(this);\r
426 \r
427             // There's no sense in updating an entity when it's going to \r
428             // be deleted in the current transaction, so do deletes first.\r
429             foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll().ToList())\r
430             {\r
431                 switch (entityTrack.EntityState)\r
432                 {\r
433                     case EntityState.ToDelete:\r
434                         var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);\r
435                         QueryRunner.Delete(entityTrack.Entity, deleteQuery);\r
436 \r
437                         UnregisterDelete(entityTrack.Entity);\r
438                         AllTrackedEntities.RegisterToDelete(entityTrack.Entity);\r
439                         AllTrackedEntities.RegisterDeleted(entityTrack.Entity);\r
440                         break;\r
441                     default:\r
442                         // ignore.\r
443                         break;\r
444                 }\r
445             }\r
446             foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll()\r
447                     .Concat(AllTrackedEntities.EnumerateAll())\r
448                     .ToList())\r
449             {\r
450                 switch (entityTrack.EntityState)\r
451                 {\r
452                     case EntityState.ToInsert:\r
453                         foreach (var toInsert in GetReferencedObjects(entityTrack.Entity))\r
454                         {\r
455                             InsertEntity(toInsert, queryContext);\r
456                         }\r
457                         break;\r
458                     case EntityState.ToWatch:\r
459                         foreach (var toUpdate in GetReferencedObjects(entityTrack.Entity))\r
460                         {\r
461                             UpdateEntity(toUpdate, queryContext);\r
462                         }\r
463                         break;\r
464                     default:\r
465                         throw new ArgumentOutOfRangeException();\r
466                 }\r
467             }\r
468         }\r
469 \r
470         private IEnumerable<object> GetReferencedObjects(object value)\r
471         {\r
472             var values = new EntitySet<object>();\r
473             FillReferencedObjects(value, values);\r
474             return values;\r
475         }\r
476 \r
477         // Breadth-first traversal of an object graph\r
478         private void FillReferencedObjects(object parent, EntitySet<object> values)\r
479         {\r
480             if (parent == null)\r
481                 return;\r
482             var children = new Queue<object>();\r
483                         children.Enqueue(parent);\r
484                         while (children.Count > 0)\r
485                         {\r
486                 object value = children.Dequeue();\r
487                 values.Add(value);\r
488                 IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(value.GetType()).Associations.Where(a => !a.IsForeignKey);\r
489                 if (associationList.Any())\r
490                             {\r
491                                     foreach (MetaAssociation association in associationList)\r
492                     {\r
493                         var memberData = association.ThisMember;\r
494                         var entitySetValue = memberData.Member.GetMemberValue(value);\r
495 \r
496                         if (entitySetValue != null)\r
497                         {\r
498                                                     var hasLoadedOrAssignedValues = entitySetValue.GetType().GetProperty("HasLoadedOrAssignedValues");\r
499                                                     if (!((bool)hasLoadedOrAssignedValues.GetValue(entitySetValue, null)))\r
500                                                             continue;   // execution deferred; ignore.\r
501                                                     foreach (var o in ((IEnumerable)entitySetValue))\r
502                                                             children.Enqueue(o);\r
503                                             }\r
504                     }\r
505                 }\r
506                         }\r
507         }\r
508 \r
509         private void InsertEntity(object entity, QueryContext queryContext)\r
510         {\r
511             var insertQuery = QueryBuilder.GetInsertQuery(entity, queryContext);\r
512             QueryRunner.Insert(entity, insertQuery);\r
513             Register(entity);\r
514             UpdateReferencedObjects(entity);\r
515             MoveToAllTrackedEntities(entity, true);\r
516         }\r
517 \r
518         private void UpdateEntity(object entity, QueryContext queryContext)\r
519         {\r
520             if (!AllTrackedEntities.ContainsReference(entity))\r
521                 InsertEntity(entity, queryContext);\r
522             else if (MemberModificationHandler.IsModified(entity, Mapping))\r
523             {\r
524                 var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entity, Mapping);\r
525                 var updateQuery = QueryBuilder.GetUpdateQuery(entity, modifiedMembers, queryContext);\r
526                 QueryRunner.Update(entity, updateQuery, modifiedMembers);\r
527 \r
528                 RegisterUpdateAgain(entity);\r
529                 UpdateReferencedObjects(entity);\r
530                 MoveToAllTrackedEntities(entity, false);\r
531             }\r
532         }\r
533 \r
534         private void UpdateReferencedObjects(object root)\r
535         {\r
536             var metaType = Mapping.GetMetaType(root.GetType());\r
537             foreach (var assoc in metaType.Associations)\r
538             {\r
539                 var memberData = assoc.ThisMember;\r
540                                 //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
541                                 //Confirmed against default .NET l2sql - association columns are always set, even if AutoSync==AutoSync.Never\r
542                                 //if (memberData.Association.ThisKey.Any(m => (m.AutoSync != AutoSync.Always) && (m.AutoSync != sync)))\r
543                 //    continue;\r
544                 var oks = memberData.Association.OtherKey.Select(m => m.StorageMember).ToList();\r
545                 if (oks.Count == 0)\r
546                     continue;\r
547                 var pks = memberData.Association.ThisKey\r
548                     .Select(m => m.StorageMember.GetMemberValue(root))\r
549                     .ToList();\r
550                 if (pks.Count != oks.Count)\r
551                     throw new InvalidOperationException(\r
552                         string.Format("Count of primary keys ({0}) doesn't match count of other keys ({1}).",\r
553                             pks.Count, oks.Count));\r
554                 var members = memberData.Member.GetMemberValue(root) as IEnumerable;\r
555                 if (members == null)\r
556                     continue;\r
557                 foreach (var member in members)\r
558                 {\r
559                     for (int i = 0; i < pks.Count; ++i)\r
560                     {\r
561                         oks[i].SetMemberValue(member, pks[i]);\r
562                     }\r
563                 }\r
564             }\r
565         }\r
566 \r
567         private void MoveToAllTrackedEntities(object entity, bool insert)\r
568         {\r
569             if (!ObjectTrackingEnabled)\r
570                 return;\r
571             if (CurrentTransactionEntities.ContainsReference(entity))\r
572             {\r
573                 CurrentTransactionEntities.RegisterToDelete(entity);\r
574                 if (!insert)\r
575                     CurrentTransactionEntities.RegisterDeleted(entity);\r
576             }\r
577             if (!AllTrackedEntities.ContainsReference(entity))\r
578             {\r
579                 var identityReader = _GetIdentityReader(entity.GetType());\r
580                 AllTrackedEntities.RegisterToWatch(entity, identityReader.GetIdentityKey(entity));\r
581             }\r
582         }\r
583 \r
584         /// <summary>\r
585         /// TODO - allow generated methods to call into stored procedures\r
586         /// </summary>\r
587         [DBLinqExtended]\r
588         internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams)\r
589         {\r
590             using (DatabaseContext.OpenConnection())\r
591             {\r
592                 System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams);\r
593                 return result;\r
594             }\r
595         }\r
596 \r
597         [DbLinqToDo]\r
598         protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters)\r
599         {\r
600             throw new NotImplementedException();\r
601         }\r
602 \r
603         #region Identity management\r
604 \r
605         [DBLinqExtended]\r
606         internal IIdentityReader _GetIdentityReader(Type t)\r
607         {\r
608             IIdentityReader identityReader;\r
609             if (!identityReaders.TryGetValue(t, out identityReader))\r
610             {\r
611                 identityReader = identityReaderFactory.GetReader(t, this);\r
612                 identityReaders[t] = identityReader;\r
613             }\r
614             return identityReader;\r
615         }\r
616 \r
617         [DBLinqExtended]\r
618         internal object _GetRegisteredEntity(object entity)\r
619         {\r
620             // TODO: check what is faster: by identity or by ref\r
621             var identityReader = _GetIdentityReader(entity.GetType());\r
622             var identityKey = identityReader.GetIdentityKey(entity);\r
623             if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK\r
624                 return entity;\r
625             // even \r
626             var registeredEntityTrack = \r
627                 CurrentTransactionEntities.FindByIdentity(identityKey) ??\r
628                 AllTrackedEntities.FindByIdentity(identityKey);\r
629             if (registeredEntityTrack != null)\r
630                 return registeredEntityTrack.Entity;\r
631             return null;\r
632         }\r
633 \r
634         //internal object GetRegisteredEntityByKey(IdentityKey identityKey)\r
635         //{\r
636         //    return EntityMap[identityKey];\r
637         //}\r
638 \r
639         /// <summary>\r
640         /// Registers an entity in a watch state\r
641         /// </summary>\r
642         /// <param name="entity"></param>\r
643         /// <returns></returns>\r
644         [DBLinqExtended]\r
645         internal object _GetOrRegisterEntity(object entity)\r
646         {\r
647             var identityReader = _GetIdentityReader(entity.GetType());\r
648             var identityKey = identityReader.GetIdentityKey(entity);\r
649             SetEntitySetsQueries(entity);\r
650             SetEntityRefQueries(entity);\r
651 \r
652             // if we have no identity, we can't track it\r
653             if (identityKey == null)\r
654                 return entity;\r
655 \r
656             // try to find an already registered entity and return it\r
657             var registeredEntityTrack = \r
658                 CurrentTransactionEntities.FindByIdentity(identityKey) ??\r
659                 AllTrackedEntities.FindByIdentity(identityKey);\r
660             if (registeredEntityTrack != null)\r
661                 return registeredEntityTrack.Entity;\r
662 \r
663             // otherwise, register and return\r
664             AllTrackedEntities.RegisterToWatch(entity, identityKey);\r
665             return entity;\r
666         }\r
667 \r
668         readonly IDataMapper DataMapper = ObjectFactory.Get<IDataMapper>();\r
669                 private void SetEntityRefQueries(object entity)\r
670                 {\r
671             if (!this.deferredLoadingEnabled)\r
672                 return;\r
673 \r
674             // BUG: This is ignoring External Mappings from XmlMappingSource.\r
675 \r
676                         Type thisType = entity.GetType();\r
677                         IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(entity.GetType()).Associations.Where(a => a.IsForeignKey);\r
678                         foreach (MetaAssociation association in associationList)\r
679                         {\r
680                                 //example of entityRef:Order.Employee\r
681                                 var memberData = association.ThisMember;\r
682                                 Type otherTableType = association.OtherType.Type;\r
683                                 ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
684 \r
685                                 var otherTable = GetTable(otherTableType);\r
686 \r
687                                 //ie:EmployeeTerritories.EmployeeID\r
688                                 var foreignKeys = memberData.Association.ThisKey;\r
689                                 BinaryExpression predicate = null;\r
690                                 var otherPKs = memberData.Association.OtherKey;\r
691                                 IEnumerator<MetaDataMember> otherPKEnumerator = otherPKs.GetEnumerator();\r
692 \r
693                                 if (otherPKs.Count != foreignKeys.Count)\r
694                                         throw new InvalidOperationException("Foreign keys don't match ThisKey");\r
695                                 foreach (MetaDataMember key in foreignKeys)\r
696                                 {\r
697                                         otherPKEnumerator.MoveNext();\r
698 \r
699                                         var thisForeignKeyProperty = (PropertyInfo)key.Member;\r
700                                         object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);\r
701 \r
702                                         if (thisForeignKeyValue != null)\r
703                                         {\r
704                                                 BinaryExpression keyPredicate;\r
705                                                 if (!(thisForeignKeyProperty.PropertyType.IsNullable()))\r
706                                                 {\r
707                                                         keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKEnumerator.Current.Member),\r
708                                                                                                                                                 Expression.Constant(thisForeignKeyValue));\r
709                                                 }\r
710                                                 else\r
711                                                 {\r
712                                                         var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");\r
713                                                         keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKEnumerator.Current.Member),\r
714                                                                                                                                          Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));\r
715                                                 }\r
716 \r
717                                                 if (predicate == null)\r
718                                                         predicate = keyPredicate;\r
719                                                 else\r
720                                                         predicate = Expression.And(predicate, keyPredicate);\r
721                                         }\r
722                                 }\r
723                                 IEnumerable query = null;\r
724                                 if (predicate != null)\r
725                                 {\r
726                                         query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;\r
727                                         //it would be interesting surround the above query with a .Take(1) expression for performance.\r
728                                 }\r
729 \r
730                                 // If no separate Storage is specified, use the member directly\r
731                                 MemberInfo storage = memberData.StorageMember;\r
732                                 if (storage == null)\r
733                                         storage = memberData.Member;\r
734 \r
735                                  // Check that the storage is a field or a writable property\r
736                                 if (!(storage is FieldInfo) && !(storage is PropertyInfo && ((PropertyInfo)storage).CanWrite)) {\r
737                                         throw new InvalidOperationException(String.Format(\r
738                                                 "Member {0}.{1} is not a field nor a writable property",\r
739                                                 storage.DeclaringType, storage.Name));\r
740                                 }\r
741 \r
742                                 Type storageType = storage.GetMemberType();\r
743 \r
744                                 object entityRefValue = null;\r
745                                 if (query != null)\r
746                                         entityRefValue = Activator.CreateInstance(storageType, query);\r
747                                 else\r
748                                         entityRefValue = Activator.CreateInstance(storageType);\r
749 \r
750                                 storage.SetMemberValue(entity, entityRefValue);\r
751                         }\r
752                 }\r
753 \r
754         /// <summary>\r
755         /// 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
756         /// Here we set the query source of each EntitySetProperty\r
757         /// </summary>\r
758         /// <param name="entity"></param>\r
759         private void SetEntitySetsQueries(object entity)\r
760         {\r
761             if (!this.deferredLoadingEnabled)\r
762                 return;\r
763 \r
764             // BUG: This is ignoring External Mappings from XmlMappingSource.\r
765 \r
766                         IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(entity.GetType()).Associations.Where(a => !a.IsForeignKey);\r
767 \r
768                         if (associationList.Any())\r
769                         {\r
770                                 foreach (MetaAssociation association in associationList)\r
771                 {\r
772                                         //example of entitySet: Employee.EmployeeTerritories\r
773                                         var memberData = association.ThisMember;\r
774                                         Type otherTableType = association.OtherType.Type;\r
775                     ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
776 \r
777                     //other table:EmployeeTerritories\r
778                     var otherTable = GetTable(otherTableType);\r
779 \r
780                                         var otherKeys = memberData.Association.OtherKey;\r
781                                         var thisKeys = memberData.Association.ThisKey;\r
782                     if (otherKeys.Count != thisKeys.Count)\r
783                         throw new InvalidOperationException("This keys don't match OtherKey");\r
784                     BinaryExpression predicate = null;\r
785                     IEnumerator<MetaDataMember> thisKeyEnumerator = thisKeys.GetEnumerator();\r
786                                         foreach (MetaDataMember otherKey in otherKeys)\r
787                     {\r
788                         thisKeyEnumerator.MoveNext();\r
789                         //other table member:EmployeeTerritories.EmployeeID\r
790                                                 var otherTableMember = (PropertyInfo)otherKey.Member;\r
791 \r
792                         BinaryExpression keyPredicate;\r
793                         if (!(otherTableMember.PropertyType.IsNullable()))\r
794                         {\r
795                             keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),\r
796                                                                         Expression.Constant(thisKeyEnumerator.Current.Member.GetMemberValue(entity)));\r
797                         }\r
798                         else\r
799                         {\r
800                             var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");\r
801                             keyPredicate = Expression.Equal(Expression.MakeMemberAccess(\r
802                                                                         Expression.MakeMemberAccess(p, otherTableMember),\r
803                                                                         ValueProperty),\r
804                                                                      Expression.Constant(thisKeyEnumerator.Current.Member.GetMemberValue(entity)));\r
805                         }\r
806                         if (predicate == null)\r
807                             predicate = keyPredicate;\r
808                         else\r
809                             predicate = Expression.And(predicate, keyPredicate);\r
810                     }\r
811 \r
812                     var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable);\r
813 \r
814                                         var entitySetValue = memberData.Member.GetMemberValue(entity);\r
815 \r
816                     if (entitySetValue == null)\r
817                     {\r
818                                                 entitySetValue = Activator.CreateInstance(memberData.Member.GetMemberType());\r
819                                                 memberData.Member.SetMemberValue(entity, entitySetValue);\r
820                     }\r
821 \r
822                     var hasLoadedOrAssignedValues = entitySetValue.GetType().GetProperty("HasLoadedOrAssignedValues");\r
823                     if ((bool)hasLoadedOrAssignedValues.GetValue(entitySetValue, null))\r
824                         continue;\r
825 \r
826                     var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource");\r
827                     setSourceMethod.Invoke(entitySetValue, new[] { query });\r
828                     //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH"))\r
829                 }\r
830             }\r
831         }\r
832 \r
833                 private static MethodInfo _WhereMethod;\r
834         internal object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)\r
835         {\r
836             if (_WhereMethod == null)\r
837                 System.Threading.Interlocked.CompareExchange (ref _WhereMethod, typeof(Queryable).GetMethods().First(m => m.Name == "Where"), null);\r
838 \r
839             //predicate: other.EmployeeID== "WARTH"\r
840             Expression lambdaPredicate = Expression.Lambda(predicate, parameter);\r
841             //lambdaPredicate: other=>other.EmployeeID== "WARTH"\r
842 \r
843                         Expression call = Expression.Call(_WhereMethod.MakeGenericMethod(otherTableType), otherTable.Expression, lambdaPredicate);\r
844             //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")\r
845 \r
846             return otherTable.Provider.CreateQuery(call);\r
847         }\r
848 \r
849         #endregion\r
850 \r
851         #region Insert/Update/Delete management\r
852 \r
853         /// <summary>\r
854         /// Registers an entity for insert\r
855         /// </summary>\r
856         /// <param name="entity"></param>\r
857         internal void RegisterInsert(object entity)\r
858         {\r
859             CurrentTransactionEntities.RegisterToInsert(entity);\r
860         }\r
861 \r
862         private void DoRegisterUpdate(object entity)\r
863         {\r
864             if (entity == null)\r
865                 throw new ArgumentNullException("entity");\r
866 \r
867             if (!this.objectTrackingEnabled)\r
868                 return;\r
869 \r
870             var identityReader = _GetIdentityReader(entity.GetType());\r
871             var identityKey = identityReader.GetIdentityKey(entity);\r
872             // if we have no key, we can not watch\r
873             if (identityKey == null || identityKey.Keys.Count == 0)\r
874                 return;\r
875             // register entity\r
876             AllTrackedEntities.RegisterToWatch(entity, identityKey);\r
877         }\r
878 \r
879         /// <summary>\r
880         /// Registers an entity for update\r
881         /// The entity will be updated only if some of its members have changed after the registration\r
882         /// </summary>\r
883         /// <param name="entity"></param>\r
884         internal void RegisterUpdate(object entity)\r
885         {\r
886             DoRegisterUpdate(entity);\r
887                         MemberModificationHandler.Register(entity, Mapping);\r
888         }\r
889 \r
890         /// <summary>\r
891         /// Registers or re-registers an entity and clears its state\r
892         /// </summary>\r
893         /// <param name="entity"></param>\r
894         /// <returns></returns>\r
895         internal object Register(object entity)\r
896         {\r
897             if (! this.objectTrackingEnabled)\r
898                 return entity;\r
899             var registeredEntity = _GetOrRegisterEntity(entity);\r
900             // the fact of registering again clears the modified state, so we're... clear with that\r
901             MemberModificationHandler.Register(registeredEntity, Mapping);\r
902             return registeredEntity;\r
903         }\r
904 \r
905         /// <summary>\r
906         /// Registers an entity for update\r
907         /// The entity will be updated only if some of its members have changed after the registration\r
908         /// </summary>\r
909         /// <param name="entity"></param>\r
910         /// <param name="entityOriginalState"></param>\r
911         internal void RegisterUpdate(object entity, object entityOriginalState)\r
912         {\r
913             if (!this.objectTrackingEnabled)\r
914                 return;\r
915             DoRegisterUpdate(entity);\r
916             MemberModificationHandler.Register(entity, entityOriginalState, Mapping);\r
917         }\r
918 \r
919         /// <summary>\r
920         /// Clears the current state, and marks the object as clean\r
921         /// </summary>\r
922         /// <param name="entity"></param>\r
923         internal void RegisterUpdateAgain(object entity)\r
924         {\r
925             if (!this.objectTrackingEnabled)\r
926                 return;\r
927             MemberModificationHandler.ClearModified(entity, Mapping);\r
928         }\r
929 \r
930         /// <summary>\r
931         /// Registers an entity for delete\r
932         /// </summary>\r
933         /// <param name="entity"></param>\r
934         internal void RegisterDelete(object entity)\r
935         {\r
936             if (!this.objectTrackingEnabled)\r
937                 return;\r
938             CurrentTransactionEntities.RegisterToDelete(entity);\r
939         }\r
940 \r
941         /// <summary>\r
942         /// Unregisters entity after deletion\r
943         /// </summary>\r
944         /// <param name="entity"></param>\r
945         internal void UnregisterDelete(object entity)\r
946         {\r
947             if (!this.objectTrackingEnabled)\r
948                 return;\r
949             CurrentTransactionEntities.RegisterDeleted(entity);\r
950         }\r
951 \r
952         #endregion\r
953 \r
954         /// <summary>\r
955         /// Changed object determine \r
956         /// </summary>\r
957         /// <returns>Lists of inserted, updated, deleted objects</returns>\r
958         public ChangeSet GetChangeSet()\r
959         {\r
960             var inserts = new List<object>();\r
961             var updates = new List<object>();\r
962             var deletes = new List<object>();\r
963             foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll()\r
964                     .Concat(AllTrackedEntities.EnumerateAll()))\r
965             {\r
966                 switch (entityTrack.EntityState)\r
967                 {\r
968                     case EntityState.ToInsert:\r
969                         inserts.Add(entityTrack.Entity);\r
970                         break;\r
971                     case EntityState.ToWatch:\r
972                         if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
973                             updates.Add(entityTrack.Entity);\r
974                         break;\r
975                     case EntityState.ToDelete:\r
976                         deletes.Add(entityTrack.Entity);\r
977                         break;\r
978                     default:\r
979                         throw new ArgumentOutOfRangeException();\r
980                 }\r
981             }\r
982             return new ChangeSet(inserts, updates, deletes);\r
983         }\r
984 \r
985         /// <summary>\r
986         /// use ExecuteCommand to call raw SQL\r
987         /// </summary>\r
988         public int ExecuteCommand(string command, params object[] parameters)\r
989         {\r
990             var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this));\r
991             return QueryRunner.Execute(directQuery, parameters);\r
992         }\r
993 \r
994         /// <summary>\r
995         /// Execute raw SQL query and return object\r
996         /// </summary>\r
997         public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : new()\r
998         {\r
999             if (query == null)\r
1000                 throw new ArgumentNullException("query");\r
1001 \r
1002             return CreateExecuteQueryEnumerable<TResult>(query, parameters);\r
1003         }\r
1004 \r
1005         private IEnumerable<TResult> CreateExecuteQueryEnumerable<TResult>(string query, object[] parameters)\r
1006             where TResult : new()\r
1007         {\r
1008             foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters))\r
1009                 yield return result;\r
1010         }\r
1011 \r
1012         public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters)\r
1013         {\r
1014             if (elementType == null)\r
1015                 throw new ArgumentNullException("elementType");\r
1016             if (query == null)\r
1017                 throw new ArgumentNullException("query");\r
1018 \r
1019             var queryContext = new QueryContext(this);\r
1020             var directQuery = QueryBuilder.GetDirectQuery(query, queryContext);\r
1021             return QueryRunner.ExecuteSelect(elementType, directQuery, parameters);\r
1022         }\r
1023 \r
1024         /// <summary>\r
1025         /// Gets or sets the load options\r
1026         /// </summary>\r
1027         [DbLinqToDo]\r
1028                 public DataLoadOptions LoadOptions\r
1029                 {\r
1030                         get { throw new NotImplementedException(); }\r
1031                         set { throw new NotImplementedException(); }\r
1032                 }\r
1033 \r
1034         public DbTransaction Transaction {\r
1035             get { return (DbTransaction) DatabaseContext.CurrentTransaction; }\r
1036             set { DatabaseContext.CurrentTransaction = value; }\r
1037         }\r
1038 \r
1039         /// <summary>\r
1040         /// Runs the given reader and returns columns.\r
1041         /// </summary>\r
1042         /// <typeparam name="TResult">The type of the result.</typeparam>\r
1043         /// <param name="reader">The reader.</param>\r
1044         /// <returns></returns>\r
1045         public IEnumerable<TResult> Translate<TResult>(DbDataReader reader)\r
1046         {\r
1047             if (reader == null)\r
1048                 throw new ArgumentNullException("reader");\r
1049             return CreateTranslateIterator<TResult>(reader);\r
1050         }\r
1051 \r
1052         IEnumerable<TResult> CreateTranslateIterator<TResult>(DbDataReader reader)\r
1053         {\r
1054             foreach (TResult result in Translate(typeof(TResult), reader))\r
1055                 yield return result;\r
1056         }\r
1057 \r
1058         public IMultipleResults Translate(DbDataReader reader)\r
1059         {\r
1060             throw new NotImplementedException();\r
1061         }\r
1062 \r
1063         public IEnumerable Translate(Type elementType, DbDataReader reader)\r
1064         {\r
1065             if (elementType == null)\r
1066                 throw new ArgumentNullException("elementType");\r
1067             if (reader == null)\r
1068                 throw new ArgumentNullException("reader");\r
1069 \r
1070             return QueryRunner.EnumerateResult(elementType, reader, this);\r
1071         }\r
1072 \r
1073         public void Dispose()\r
1074         {\r
1075             //connection closing should not be done here.\r
1076             //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx\r
1077 \r
1078                         //We own the instance of MemberModificationHandler - we must unregister listeners of entities we attached to\r
1079                         MemberModificationHandler.UnregisterAll();\r
1080         }\r
1081 \r
1082         [DbLinqToDo]\r
1083         protected virtual void Dispose(bool disposing)\r
1084         {\r
1085             throw new NotImplementedException();\r
1086         }\r
1087 \r
1088         /// <summary>\r
1089         /// Creates a IDbDataAdapter. Used internally by Vendors\r
1090         /// </summary>\r
1091         /// <returns></returns>\r
1092         internal IDbDataAdapter CreateDataAdapter()\r
1093         {\r
1094             return DatabaseContext.CreateDataAdapter();\r
1095         }\r
1096 \r
1097         /// <summary>\r
1098         /// Sets a TextWriter where generated SQL commands are written\r
1099         /// </summary>\r
1100         public TextWriter Log { get; set; }\r
1101 \r
1102         /// <summary>\r
1103         /// Writes text on Log (if not null)\r
1104         /// Internal helper\r
1105         /// </summary>\r
1106         /// <param name="text"></param>\r
1107         internal void WriteLog(string text)\r
1108         {\r
1109             if (Log != null)\r
1110                 Log.WriteLine(text);\r
1111         }\r
1112 \r
1113         /// <summary>\r
1114         /// Write an IDbCommand to Log (if non null)\r
1115         /// </summary>\r
1116         /// <param name="command"></param>\r
1117         internal void WriteLog(IDbCommand command)\r
1118         {\r
1119             if (Log != null)\r
1120             {\r
1121                 Log.WriteLine(command.CommandText);\r
1122                 foreach (IDbDataParameter parameter in command.Parameters)\r
1123                     WriteLog(parameter);\r
1124                 Log.Write("--");\r
1125                 Log.Write(" Context: {0}", Vendor.VendorName);\r
1126                 Log.Write(" Model: {0}", Mapping.GetType().Name);\r
1127                 Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version);\r
1128                 Log.WriteLine();\r
1129             }\r
1130         }\r
1131 \r
1132         /// <summary>\r
1133         /// Writes and IDbDataParameter to Log (if non null)\r
1134         /// </summary>\r
1135         /// <param name="parameter"></param>\r
1136         internal void WriteLog(IDbDataParameter parameter)\r
1137         {\r
1138             if (Log != null)\r
1139             {\r
1140                 // -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2]\r
1141                 // -- <name>: <direction> <type> (...) [<value>]\r
1142                 Log.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]",\r
1143                     parameter.ParameterName, parameter.Direction, parameter.DbType,\r
1144                     parameter.Size, parameter.Precision, parameter.Scale, parameter.Value);\r
1145             }\r
1146         }\r
1147 \r
1148         public bool ObjectTrackingEnabled\r
1149         {\r
1150             get { return this.objectTrackingEnabled; }\r
1151             set \r
1152             {\r
1153                 if (this.currentTransactionEntities != null && value != this.objectTrackingEnabled)\r
1154                     throw new InvalidOperationException("Data context options cannot be modified after results have been returned from a query.");\r
1155                 this.objectTrackingEnabled = value;\r
1156             }\r
1157         }\r
1158 \r
1159         [DbLinqToDo]\r
1160         public int CommandTimeout\r
1161         {\r
1162             get { throw new NotImplementedException(); }\r
1163             set { throw new NotImplementedException(); }\r
1164         }\r
1165 \r
1166         public bool DeferredLoadingEnabled\r
1167         {\r
1168             get { return this.deferredLoadingEnabled; }\r
1169             set\r
1170             {\r
1171                 if (this.currentTransactionEntities != null && value != this.deferredLoadingEnabled)\r
1172                     throw new InvalidOperationException("Data context options cannot be modified after results have been returned from a query.");\r
1173                 this.deferredLoadingEnabled = value;\r
1174             }\r
1175         }\r
1176 \r
1177         [DbLinqToDo]\r
1178         public ChangeConflictCollection ChangeConflicts\r
1179         {\r
1180             get { throw new NotImplementedException(); }\r
1181         }\r
1182 \r
1183         [DbLinqToDo]\r
1184         public DbCommand GetCommand(IQueryable query)\r
1185         {\r
1186             DbCommand dbCommand = GetIDbCommand(query) as DbCommand;\r
1187             if (dbCommand == null)\r
1188                 throw new InvalidOperationException();\r
1189 \r
1190             return dbCommand;\r
1191         }\r
1192 \r
1193         [DBLinqExtended]\r
1194         public IDbCommand GetIDbCommand(IQueryable query)\r
1195         {\r
1196             if (query == null)\r
1197                 throw new ArgumentNullException("query");\r
1198 \r
1199             var qp = query.Provider as QueryProvider;\r
1200             if (qp == null)\r
1201                 throw new InvalidOperationException();\r
1202 \r
1203             if (qp.ExpressionChain.Expressions.Count == 0)\r
1204                 qp.ExpressionChain.Expressions.Add(CreateDefaultQuery(query));\r
1205 \r
1206             return qp.GetQuery(null).GetCommand().Command;\r
1207         }\r
1208 \r
1209         private Expression CreateDefaultQuery(IQueryable query)\r
1210         {\r
1211             // Manually create the expression tree for: IQueryable<TableType>.Select(e => e)\r
1212             var identityParameter = Expression.Parameter(query.ElementType, "e");\r
1213             var identityBody = Expression.Lambda(\r
1214                 typeof(Func<,>).MakeGenericType(query.ElementType, query.ElementType),\r
1215                 identityParameter,\r
1216                 new[] { identityParameter }\r
1217             );\r
1218 \r
1219             return Expression.Call(\r
1220                 typeof(Queryable),\r
1221                 "Select",\r
1222                 new[] { query.ElementType, query.ElementType },\r
1223                 query.Expression,\r
1224                 Expression.Quote(identityBody)\r
1225             );\r
1226         }\r
1227 \r
1228         [DbLinqToDo]\r
1229         public void Refresh(RefreshMode mode, IEnumerable entities)\r
1230         {\r
1231             throw new NotImplementedException();\r
1232         }\r
1233 \r
1234         [DbLinqToDo]\r
1235         public void Refresh(RefreshMode mode, params object[] entities)\r
1236         {\r
1237             throw new NotImplementedException();\r
1238         }\r
1239 \r
1240         [DbLinqToDo]\r
1241         public void Refresh(RefreshMode mode, object entity)\r
1242         {\r
1243             throw new NotImplementedException();\r
1244         }\r
1245 \r
1246         [DbLinqToDo]\r
1247         public void DeleteDatabase()\r
1248         {\r
1249             throw new NotImplementedException();\r
1250         }\r
1251 \r
1252         [DbLinqToDo]\r
1253         public void CreateDatabase()\r
1254         {\r
1255             throw new NotImplementedException();\r
1256         }\r
1257 \r
1258         [DbLinqToDo]\r
1259         protected internal IQueryable<TResult> CreateMethodCallQuery<TResult>(object instance, MethodInfo methodInfo, params object[] parameters)\r
1260         {\r
1261             throw new NotImplementedException();\r
1262         }\r
1263 \r
1264         [DbLinqToDo]\r
1265         protected internal void ExecuteDynamicDelete(object entity)\r
1266         {\r
1267             throw new NotImplementedException();\r
1268         }\r
1269 \r
1270         [DbLinqToDo]\r
1271         protected internal void ExecuteDynamicInsert(object entity)\r
1272         {\r
1273             throw new NotImplementedException();\r
1274         }\r
1275 \r
1276         [DbLinqToDo]\r
1277         protected internal void ExecuteDynamicUpdate(object entity)\r
1278         {\r
1279             throw new NotImplementedException();\r
1280         }\r
1281     }\r
1282 }\r