2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[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 assemblyFile = null;\r
240             string vendor;\r
241             if (!reProvider.IsMatch(connectionString))\r
242             {\r
243                 vendor       = "SqlServer";\r
244                 assemblyFile = "DbLinq.SqlServer.dll";\r
245             }\r
246             else\r
247             {\r
248                 var match    = reProvider.Match(connectionString);\r
249                 vendor       = match.Groups[1].Value;\r
250                 assemblyFile = "DbLinq." + vendor + ".dll";\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                 //TODO: check if DLL is already loaded?\r
272                 assembly = Assembly.LoadFrom(assemblyFile);\r
273 #endif\r
274             }\r
275             catch (Exception e)\r
276             {\r
277                 throw new ArgumentException(\r
278                         string.Format(\r
279                             "Unable to load the `{0}' DbLinq vendor within assembly `{1}'.",\r
280                             assemblyFile, vendor),\r
281                         "connectionString", e);\r
282             }\r
283         }\r
284 \r
285         private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor)\r
286         {\r
287             if (databaseContext == null)\r
288                 throw new ArgumentNullException("databaseContext");\r
289 \r
290             // Yes, .NET throws an NRE for this.  Why it's not ArgumentNullException, I couldn't tell you.\r
291             if (databaseContext.Connection.ConnectionString == null)\r
292                 throw new NullReferenceException();\r
293 \r
294             string connectionString = databaseContext.Connection.ConnectionString;\r
295             _VendorProvider = ObjectFactory.Get<IVendorProvider>();\r
296             Vendor = vendor ?? \r
297                 (connectionString != null ? GetVendor(ref connectionString) : null) ??\r
298                 _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider));\r
299             \r
300             DatabaseContext = databaseContext;\r
301 \r
302             MemberModificationHandler = ObjectFactory.Create<IMemberModificationHandler>(); // not a singleton: object is stateful\r
303             QueryBuilder = ObjectFactory.Get<IQueryBuilder>();\r
304             QueryRunner = ObjectFactory.Get<IQueryRunner>();\r
305 \r
306             //EntityMap = ObjectFactory.Create<IEntityMap>();\r
307             identityReaderFactory = ObjectFactory.Get<IIdentityReaderFactory>();\r
308 \r
309             _MappingContext = new MappingContext();\r
310 \r
311             // initialize the mapping information\r
312             if (mappingSource == null)\r
313                 mappingSource = new AttributeMappingSource();\r
314             Mapping = mappingSource.GetModel(GetType());\r
315         }\r
316 \r
317         /// <summary>\r
318         /// Checks if the table is allready mapped or maps it if not.\r
319         /// </summary>\r
320         /// <param name="tableType">Type of the table.</param>\r
321         /// <exception cref="InvalidOperationException">Thrown if the table is not mappable.</exception>\r
322         private void CheckTableMapping(Type tableType)\r
323         {\r
324             //This will throw an exception if the table is not found\r
325             if(Mapping.GetTable(tableType) == null)\r
326             {\r
327                 throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table.");\r
328             }\r
329         }\r
330 \r
331         /// <summary>\r
332         /// Returns a Table for the type TEntity.\r
333         /// </summary>\r
334         /// <exception cref="InvalidOperationException">If the type TEntity is not mappable as a Table.</exception>\r
335         /// <typeparam name="TEntity">The table type.</typeparam>\r
336         public Table<TEntity> GetTable<TEntity>() where TEntity : class\r
337         {\r
338             return (Table<TEntity>)GetTable(typeof(TEntity));\r
339         }\r
340 \r
341         /// <summary>\r
342         /// Returns a Table for the given type.\r
343         /// </summary>\r
344         /// <param name="type">The table type.</param>\r
345         /// <exception cref="InvalidOperationException">If the type is not mappable as a Table.</exception>\r
346         public ITable GetTable(Type type)\r
347         {\r
348             Profiler.At("DataContext.GetTable(typeof({0}))", type != null ? type.Name : null);\r
349             ITable tableExisting;\r
350             if (_tableMap.TryGetValue(type, out tableExisting))\r
351                 return tableExisting;\r
352 \r
353             //Check for table mapping\r
354             CheckTableMapping(type);\r
355 \r
356             var tableNew = Activator.CreateInstance(\r
357                               typeof(Table<>).MakeGenericType(type)\r
358                               , BindingFlags.NonPublic | BindingFlags.Instance\r
359                               , null\r
360                               , new object[] { this }\r
361                               , System.Globalization.CultureInfo.CurrentCulture) as ITable;\r
362 \r
363             _tableMap[type] = tableNew;\r
364             return tableNew;\r
365         }\r
366 \r
367         public void SubmitChanges()\r
368         {\r
369             SubmitChanges(ConflictMode.FailOnFirstConflict);\r
370         }\r
371 \r
372         /// <summary>\r
373         /// Pings database\r
374         /// </summary>\r
375         /// <returns></returns>\r
376         public bool DatabaseExists()\r
377         {\r
378             try\r
379             {\r
380                 return Vendor.Ping(this);\r
381             }\r
382             catch (Exception)\r
383             {\r
384                 return false;\r
385             }\r
386         }\r
387 \r
388         /// <summary>\r
389         /// Commits all pending changes to database \r
390         /// </summary>\r
391         /// <param name="failureMode"></param>\r
392         public virtual void SubmitChanges(ConflictMode failureMode)\r
393         {\r
394             if (this.objectTrackingEnabled == false)\r
395                 throw new InvalidOperationException("Object tracking is not enabled for the current data context instance.");\r
396             using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us\r
397             using (IDatabaseTransaction transaction = DatabaseContext.Transaction())\r
398             {\r
399                 var queryContext = new QueryContext(this);\r
400 \r
401                 // There's no sense in updating an entity when it's going to \r
402                 // be deleted in the current transaction, so do deletes first.\r
403                 foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll().ToList())\r
404                 {\r
405                     switch (entityTrack.EntityState)\r
406                     {\r
407                         case EntityState.ToDelete:\r
408                             var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);\r
409                             QueryRunner.Delete(entityTrack.Entity, deleteQuery);\r
410 \r
411                             UnregisterDelete(entityTrack.Entity);\r
412                             AllTrackedEntities.RegisterToDelete(entityTrack.Entity);\r
413                             AllTrackedEntities.RegisterDeleted(entityTrack.Entity);\r
414                             break;\r
415                         default:\r
416                             // ignore.\r
417                             break;\r
418                     }\r
419                 }\r
420                 foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll()\r
421                         .Concat(AllTrackedEntities.EnumerateAll())\r
422                         .ToList())\r
423                 {\r
424                     switch (entityTrack.EntityState)\r
425                     {\r
426                         case EntityState.ToInsert:\r
427                             foreach (var toInsert in GetReferencedObjects(entityTrack.Entity))\r
428                             {\r
429                                 InsertEntity(toInsert, queryContext);\r
430                             }\r
431                             break;\r
432                         case EntityState.ToWatch:\r
433                             foreach (var toUpdate in GetReferencedObjects(entityTrack.Entity))\r
434                             {\r
435                                 UpdateEntity(toUpdate, queryContext);\r
436                             }\r
437                             break;\r
438                         default:\r
439                             throw new ArgumentOutOfRangeException();\r
440                     }\r
441                 }\r
442                 // TODO: handle conflicts (which can only occur when concurrency mode is implemented)\r
443                 transaction.Commit();\r
444             }\r
445         }\r
446 \r
447         private static IEnumerable<object> GetReferencedObjects(object value)\r
448         {\r
449             var values = new EntitySet<object>();\r
450             FillReferencedObjects(value, values);\r
451             return values;\r
452         }\r
453 \r
454         // Breadth-first traversal of an object graph\r
455         private static void FillReferencedObjects(object value, EntitySet<object> values)\r
456         {\r
457             if (value == null)\r
458                 return;\r
459             values.Add(value);\r
460             var children = new List<object>();\r
461             foreach (var p in value.GetType().GetProperties())\r
462             {\r
463                 var type = p.PropertyType.IsGenericType\r
464                     ? p.PropertyType.GetGenericTypeDefinition()\r
465                     : null;\r
466                 if (type != null && p.CanRead && type == typeof(EntitySet<>) &&\r
467                         p.GetGetMethod().GetParameters().Length == 0)\r
468                 {\r
469                     var set = p.GetValue(value, null);\r
470                     if (set == null)\r
471                         continue;\r
472                     var hasLoadedOrAssignedValues = p.PropertyType.GetProperty("HasLoadedOrAssignedValues");\r
473                     if (!((bool)hasLoadedOrAssignedValues.GetValue(set, null)))\r
474                         continue;   // execution deferred; ignore.\r
475                     foreach (var o in ((IEnumerable)set))\r
476                         children.Add(o);\r
477                 }\r
478             }\r
479             foreach (var c in children)\r
480             {\r
481                 FillReferencedObjects(c, values);\r
482             }\r
483         }\r
484 \r
485         private void InsertEntity(object entity, QueryContext queryContext)\r
486         {\r
487             var insertQuery = QueryBuilder.GetInsertQuery(entity, queryContext);\r
488             QueryRunner.Insert(entity, insertQuery);\r
489             Register(entity);\r
490             UpdateReferencedObjects(entity, AutoSync.OnInsert);\r
491             MoveToAllTrackedEntities(entity, true);\r
492         }\r
493 \r
494         private void UpdateEntity(object entity, QueryContext queryContext)\r
495         {\r
496             if (!AllTrackedEntities.ContainsReference(entity))\r
497                 InsertEntity(entity, queryContext);\r
498             else if (MemberModificationHandler.IsModified(entity, Mapping))\r
499             {\r
500                 var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entity, Mapping);\r
501                 var updateQuery = QueryBuilder.GetUpdateQuery(entity, modifiedMembers, queryContext);\r
502                 QueryRunner.Update(entity, updateQuery, modifiedMembers);\r
503 \r
504                 RegisterUpdateAgain(entity);\r
505                 UpdateReferencedObjects(entity, AutoSync.OnUpdate);\r
506                 MoveToAllTrackedEntities(entity, false);\r
507             }\r
508         }\r
509 \r
510         private void UpdateReferencedObjects(object root, AutoSync sync)\r
511         {\r
512             var metaType = Mapping.GetMetaType(root.GetType());\r
513             foreach (var assoc in metaType.Associations)\r
514             {\r
515                 var memberData = assoc.ThisMember;\r
516                 if (memberData.Association.ThisKey.Any(m => m.AutoSync != sync))\r
517                     continue;\r
518                 var oks = memberData.Association.OtherKey.Select(m => m.StorageMember).ToList();\r
519                 if (oks.Count == 0)\r
520                     continue;\r
521                 var pks = memberData.Association.ThisKey\r
522                     .Select(m => m.StorageMember.GetMemberValue(root))\r
523                     .ToList();\r
524                 if (pks.Count != pks.Count)\r
525                     throw new InvalidOperationException(\r
526                         string.Format("Count of primary keys ({0}) doesn't match count of other keys ({1}).",\r
527                             pks.Count, oks.Count));\r
528                 var members = memberData.Member.GetMemberValue(root) as IEnumerable;\r
529                 if (members == null)\r
530                     continue;\r
531                 foreach (var member in members)\r
532                 {\r
533                     for (int i = 0; i < pks.Count; ++i)\r
534                     {\r
535                         oks[i].SetMemberValue(member, pks[i]);\r
536                     }\r
537                 }\r
538             }\r
539         }\r
540 \r
541         private void MoveToAllTrackedEntities(object entity, bool insert)\r
542         {\r
543             if (!ObjectTrackingEnabled)\r
544                 return;\r
545             if (CurrentTransactionEntities.ContainsReference(entity))\r
546             {\r
547                 CurrentTransactionEntities.RegisterToDelete(entity);\r
548                 if (!insert)\r
549                     CurrentTransactionEntities.RegisterDeleted(entity);\r
550             }\r
551             if (!AllTrackedEntities.ContainsReference(entity))\r
552             {\r
553                 var identityReader = _GetIdentityReader(entity.GetType());\r
554                 AllTrackedEntities.RegisterToWatch(entity, identityReader.GetIdentityKey(entity));\r
555             }\r
556         }\r
557 \r
558         /// <summary>\r
559         /// TODO - allow generated methods to call into stored procedures\r
560         /// </summary>\r
561         [DBLinqExtended]\r
562         internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams)\r
563         {\r
564             using (DatabaseContext.OpenConnection())\r
565             {\r
566                 System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams);\r
567                 return result;\r
568             }\r
569         }\r
570 \r
571         [DbLinqToDo]\r
572         protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters)\r
573         {\r
574             throw new NotImplementedException();\r
575         }\r
576 \r
577         #region Identity management\r
578 \r
579         [DBLinqExtended]\r
580         internal IIdentityReader _GetIdentityReader(Type t)\r
581         {\r
582             IIdentityReader identityReader;\r
583             if (!identityReaders.TryGetValue(t, out identityReader))\r
584             {\r
585                 identityReader = identityReaderFactory.GetReader(t, this);\r
586                 identityReaders[t] = identityReader;\r
587             }\r
588             return identityReader;\r
589         }\r
590 \r
591         [DBLinqExtended]\r
592         internal object _GetRegisteredEntity(object entity)\r
593         {\r
594             // TODO: check what is faster: by identity or by ref\r
595             var identityReader = _GetIdentityReader(entity.GetType());\r
596             var identityKey = identityReader.GetIdentityKey(entity);\r
597             if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK\r
598                 return entity;\r
599             // even \r
600             var registeredEntityTrack = \r
601                 CurrentTransactionEntities.FindByIdentity(identityKey) ??\r
602                 AllTrackedEntities.FindByIdentity(identityKey);\r
603             if (registeredEntityTrack != null)\r
604                 return registeredEntityTrack.Entity;\r
605             return null;\r
606         }\r
607 \r
608         //internal object GetRegisteredEntityByKey(IdentityKey identityKey)\r
609         //{\r
610         //    return EntityMap[identityKey];\r
611         //}\r
612 \r
613         /// <summary>\r
614         /// Registers an entity in a watch state\r
615         /// </summary>\r
616         /// <param name="entity"></param>\r
617         /// <returns></returns>\r
618         [DBLinqExtended]\r
619         internal object _GetOrRegisterEntity(object entity)\r
620         {\r
621             var identityReader = _GetIdentityReader(entity.GetType());\r
622             var identityKey = identityReader.GetIdentityKey(entity);\r
623             SetEntitySetsQueries(entity);\r
624             SetEntityRefQueries(entity);\r
625 \r
626             // if we have no identity, we can't track it\r
627             if (identityKey == null)\r
628                 return entity;\r
629 \r
630             // try to find an already registered entity and return it\r
631             var registeredEntityTrack = \r
632                 CurrentTransactionEntities.FindByIdentity(identityKey) ??\r
633                 AllTrackedEntities.FindByIdentity(identityKey);\r
634             if (registeredEntityTrack != null)\r
635                 return registeredEntityTrack.Entity;\r
636 \r
637             // otherwise, register and return\r
638             AllTrackedEntities.RegisterToWatch(entity, identityKey);\r
639             return entity;\r
640         }\r
641 \r
642         readonly IDataMapper DataMapper = ObjectFactory.Get<IDataMapper>();\r
643                 private void SetEntityRefQueries(object entity)\r
644                 {\r
645             if (!this.deferredLoadingEnabled)\r
646                 return;\r
647 \r
648             // BUG: This is ignoring External Mappings from XmlMappingSource.\r
649 \r
650                         Type thisType = entity.GetType();\r
651                         IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(entity.GetType()).Associations.Where(a => a.IsForeignKey);\r
652                         foreach (MetaAssociation association in associationList)\r
653                         {\r
654                                 //example of entityRef:Order.Employee\r
655                                 var memberData = association.ThisMember;\r
656                                 Type otherTableType = association.OtherType.Type;\r
657                                 ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
658 \r
659                                 var otherTable = GetTable(otherTableType);\r
660 \r
661                                 //ie:EmployeeTerritories.EmployeeID\r
662                                 var foreignKeys = memberData.Association.ThisKey;\r
663                                 BinaryExpression predicate = null;\r
664                                 var otherPKs = memberData.Association.OtherKey;\r
665                                 IEnumerator<MetaDataMember> otherPKEnumerator = otherPKs.GetEnumerator();\r
666 \r
667                                 if (otherPKs.Count != foreignKeys.Count)\r
668                                         throw new InvalidOperationException("Foreign keys don't match ThisKey");\r
669                                 foreach (MetaDataMember key in foreignKeys)\r
670                                 {\r
671                                         otherPKEnumerator.MoveNext();\r
672 \r
673                                         var thisForeignKeyProperty = (PropertyInfo)key.Member;\r
674                                         object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);\r
675 \r
676                                         if (thisForeignKeyValue != null)\r
677                                         {\r
678                                                 BinaryExpression keyPredicate;\r
679                                                 if (!(thisForeignKeyProperty.PropertyType.IsNullable()))\r
680                                                 {\r
681                                                         keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKEnumerator.Current.Member),\r
682                                                                                                                                                 Expression.Constant(thisForeignKeyValue));\r
683                                                 }\r
684                                                 else\r
685                                                 {\r
686                                                         var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");\r
687                                                         keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKEnumerator.Current.Member),\r
688                                                                                                                                          Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));\r
689                                                 }\r
690 \r
691                                                 if (predicate == null)\r
692                                                         predicate = keyPredicate;\r
693                                                 else\r
694                                                         predicate = Expression.And(predicate, keyPredicate);\r
695                                         }\r
696                                 }\r
697                                 IEnumerable query = null;\r
698                                 if (predicate != null)\r
699                                 {\r
700                                         query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;\r
701                                         //it would be interesting surround the above query with a .Take(1) expression for performance.\r
702                                 }\r
703 \r
704 \r
705                                 FieldInfo entityRefField = (FieldInfo)memberData.StorageMember;\r
706                                 object entityRefValue = null;\r
707                                 if (query != null)\r
708                                         entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query);\r
709                                 else\r
710                                         entityRefValue = Activator.CreateInstance(entityRefField.FieldType);\r
711                                 entityRefField.SetValue(entity, entityRefValue);\r
712                         }\r
713                 }\r
714 \r
715         /// <summary>\r
716         /// 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
717         /// Here we set the query source of each EntitySetProperty\r
718         /// </summary>\r
719         /// <param name="entity"></param>\r
720         private void SetEntitySetsQueries(object entity)\r
721         {\r
722             if (!this.deferredLoadingEnabled)\r
723                 return;\r
724 \r
725             // BUG: This is ignoring External Mappings from XmlMappingSource.\r
726 \r
727                         IEnumerable<MetaAssociation> associationList = Mapping.GetMetaType(entity.GetType()).Associations.Where(a => !a.IsForeignKey);\r
728 \r
729                         if (associationList.Any())\r
730                         {\r
731                                 foreach (MetaAssociation association in associationList)\r
732                 {\r
733                                         //example of entitySet: Employee.EmployeeTerritories\r
734                                         var memberData = association.ThisMember;\r
735                                         Type otherTableType = association.OtherType.Type;\r
736                     ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
737 \r
738                     //other table:EmployeeTerritories\r
739                     var otherTable = GetTable(otherTableType);\r
740 \r
741                                         var otherKeys = memberData.Association.OtherKey;\r
742                                         var thisKeys = memberData.Association.ThisKey;\r
743                     if (otherKeys.Count != thisKeys.Count)\r
744                         throw new InvalidOperationException("This keys don't match OtherKey");\r
745                     BinaryExpression predicate = null;\r
746                     IEnumerator<MetaDataMember> thisKeyEnumerator = thisKeys.GetEnumerator();\r
747                                         foreach (MetaDataMember otherKey in otherKeys)\r
748                     {\r
749                         thisKeyEnumerator.MoveNext();\r
750                         //other table member:EmployeeTerritories.EmployeeID\r
751                                                 var otherTableMember = (PropertyInfo)otherKey.Member;\r
752 \r
753                         BinaryExpression keyPredicate;\r
754                         if (!(otherTableMember.PropertyType.IsNullable()))\r
755                         {\r
756                             keyPredicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),\r
757                                                                         Expression.Constant(thisKeyEnumerator.Current.Member.GetMemberValue(entity)));\r
758                         }\r
759                         else\r
760                         {\r
761                             var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");\r
762                             keyPredicate = Expression.Equal(Expression.MakeMemberAccess(\r
763                                                                         Expression.MakeMemberAccess(p, otherTableMember),\r
764                                                                         ValueProperty),\r
765                                                                      Expression.Constant(thisKeyEnumerator.Current.Member.GetMemberValue(entity)));\r
766                         }\r
767                         if (predicate == null)\r
768                             predicate = keyPredicate;\r
769                         else\r
770                             predicate = Expression.And(predicate, keyPredicate);\r
771                     }\r
772 \r
773                     var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable);\r
774 \r
775                                         var entitySetValue = memberData.Member.GetMemberValue(entity);\r
776 \r
777                     if (entitySetValue == null)\r
778                     {\r
779                                                 entitySetValue = Activator.CreateInstance(memberData.Member.GetMemberType());\r
780                                                 memberData.Member.SetMemberValue(entity, entitySetValue);\r
781                     }\r
782 \r
783                     var hasLoadedOrAssignedValues = entitySetValue.GetType().GetProperty("HasLoadedOrAssignedValues");\r
784                     if ((bool)hasLoadedOrAssignedValues.GetValue(entitySetValue, null))\r
785                         continue;\r
786 \r
787                     var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource");\r
788                     setSourceMethod.Invoke(entitySetValue, new[] { query });\r
789                     //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH"))\r
790                 }\r
791             }\r
792         }\r
793 \r
794         private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)\r
795         {\r
796             //predicate: other.EmployeeID== "WARTH"\r
797             Expression lambdaPredicate = Expression.Lambda(predicate, parameter);\r
798             //lambdaPredicate: other=>other.EmployeeID== "WARTH"\r
799 \r
800             var whereMethod = typeof(Queryable)\r
801                               .GetMethods().First(m => m.Name == "Where")\r
802                               .MakeGenericMethod(otherTableType);\r
803 \r
804 \r
805             Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate);\r
806             //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")\r
807 \r
808             return otherTable.Provider.CreateQuery(call);\r
809         }\r
810 \r
811         #endregion\r
812 \r
813         #region Insert/Update/Delete management\r
814 \r
815         /// <summary>\r
816         /// Registers an entity for insert\r
817         /// </summary>\r
818         /// <param name="entity"></param>\r
819         internal void RegisterInsert(object entity)\r
820         {\r
821             CurrentTransactionEntities.RegisterToInsert(entity);\r
822         }\r
823 \r
824         /// <summary>\r
825         /// Registers an entity for update\r
826         /// The entity will be updated only if some of its members have changed after the registration\r
827         /// </summary>\r
828         /// <param name="entity"></param>\r
829         internal void RegisterUpdate(object entity)\r
830         {\r
831             if (entity == null)\r
832                 throw new ArgumentNullException("entity");\r
833 \r
834             if (!this.objectTrackingEnabled)\r
835                 return;\r
836 \r
837             var identityReader = _GetIdentityReader(entity.GetType());\r
838             var identityKey = identityReader.GetIdentityKey(entity);\r
839             // if we have no key, we can not watch\r
840             if (identityKey == null || identityKey.Keys.Count == 0)\r
841                 return;\r
842             // register entity\r
843             AllTrackedEntities.RegisterToWatch(entity, identityKey);\r
844         }\r
845 \r
846         /// <summary>\r
847         /// Registers or re-registers an entity and clears its state\r
848         /// </summary>\r
849         /// <param name="entity"></param>\r
850         /// <returns></returns>\r
851         internal object Register(object entity)\r
852         {\r
853             if (! this.objectTrackingEnabled)\r
854                 return entity;\r
855             var registeredEntity = _GetOrRegisterEntity(entity);\r
856             // the fact of registering again clears the modified state, so we're... clear with that\r
857             MemberModificationHandler.Register(registeredEntity, Mapping);\r
858             return registeredEntity;\r
859         }\r
860 \r
861         /// <summary>\r
862         /// Registers an entity for update\r
863         /// The entity will be updated only if some of its members have changed after the registration\r
864         /// </summary>\r
865         /// <param name="entity"></param>\r
866         /// <param name="entityOriginalState"></param>\r
867         internal void RegisterUpdate(object entity, object entityOriginalState)\r
868         {\r
869             if (!this.objectTrackingEnabled)\r
870                 return;\r
871             RegisterUpdate(entity);\r
872             MemberModificationHandler.Register(entity, entityOriginalState, Mapping);\r
873         }\r
874 \r
875         /// <summary>\r
876         /// Clears the current state, and marks the object as clean\r
877         /// </summary>\r
878         /// <param name="entity"></param>\r
879         internal void RegisterUpdateAgain(object entity)\r
880         {\r
881             if (!this.objectTrackingEnabled)\r
882                 return;\r
883             MemberModificationHandler.ClearModified(entity, Mapping);\r
884         }\r
885 \r
886         /// <summary>\r
887         /// Registers an entity for delete\r
888         /// </summary>\r
889         /// <param name="entity"></param>\r
890         internal void RegisterDelete(object entity)\r
891         {\r
892             if (!this.objectTrackingEnabled)\r
893                 return;\r
894             CurrentTransactionEntities.RegisterToDelete(entity);\r
895         }\r
896 \r
897         /// <summary>\r
898         /// Unregisters entity after deletion\r
899         /// </summary>\r
900         /// <param name="entity"></param>\r
901         internal void UnregisterDelete(object entity)\r
902         {\r
903             if (!this.objectTrackingEnabled)\r
904                 return;\r
905             CurrentTransactionEntities.RegisterDeleted(entity);\r
906         }\r
907 \r
908         #endregion\r
909 \r
910         /// <summary>\r
911         /// Changed object determine \r
912         /// </summary>\r
913         /// <returns>Lists of inserted, updated, deleted objects</returns>\r
914         public ChangeSet GetChangeSet()\r
915         {\r
916             var inserts = new List<object>();\r
917             var updates = new List<object>();\r
918             var deletes = new List<object>();\r
919             foreach (var entityTrack in CurrentTransactionEntities.EnumerateAll()\r
920                     .Concat(AllTrackedEntities.EnumerateAll()))\r
921             {\r
922                 switch (entityTrack.EntityState)\r
923                 {\r
924                     case EntityState.ToInsert:\r
925                         inserts.Add(entityTrack.Entity);\r
926                         break;\r
927                     case EntityState.ToWatch:\r
928                         if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
929                             updates.Add(entityTrack.Entity);\r
930                         break;\r
931                     case EntityState.ToDelete:\r
932                         deletes.Add(entityTrack.Entity);\r
933                         break;\r
934                     default:\r
935                         throw new ArgumentOutOfRangeException();\r
936                 }\r
937             }\r
938             return new ChangeSet(inserts, updates, deletes);\r
939         }\r
940 \r
941         /// <summary>\r
942         /// use ExecuteCommand to call raw SQL\r
943         /// </summary>\r
944         public int ExecuteCommand(string command, params object[] parameters)\r
945         {\r
946             var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this));\r
947             return QueryRunner.Execute(directQuery, parameters);\r
948         }\r
949 \r
950         /// <summary>\r
951         /// Execute raw SQL query and return object\r
952         /// </summary>\r
953         public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : class, new()\r
954         {\r
955             if (query == null)\r
956                 throw new ArgumentNullException("query");\r
957 \r
958             return CreateExecuteQueryEnumerable<TResult>(query, parameters);\r
959         }\r
960 \r
961         private IEnumerable<TResult> CreateExecuteQueryEnumerable<TResult>(string query, object[] parameters)\r
962             where TResult : class, new()\r
963         {\r
964             foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters))\r
965                 yield return result;\r
966         }\r
967 \r
968         public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters)\r
969         {\r
970             if (elementType == null)\r
971                 throw new ArgumentNullException("elementType");\r
972             if (query == null)\r
973                 throw new ArgumentNullException("query");\r
974 \r
975             var queryContext = new QueryContext(this);\r
976             var directQuery = QueryBuilder.GetDirectQuery(query, queryContext);\r
977             return QueryRunner.ExecuteSelect(elementType, directQuery, parameters);\r
978         }\r
979 \r
980         /// <summary>\r
981         /// Gets or sets the load options\r
982         /// </summary>\r
983         [DbLinqToDo]\r
984                 public DataLoadOptions LoadOptions\r
985                 {\r
986                         get { throw new NotImplementedException(); }\r
987                         set { throw new NotImplementedException(); }\r
988                 }\r
989 \r
990         public DbTransaction Transaction { get; set; }\r
991 \r
992         /// <summary>\r
993         /// Runs the given reader and returns columns.\r
994         /// </summary>\r
995         /// <typeparam name="TResult">The type of the result.</typeparam>\r
996         /// <param name="reader">The reader.</param>\r
997         /// <returns></returns>\r
998         public IEnumerable<TResult> Translate<TResult>(DbDataReader reader)\r
999         {\r
1000             if (reader == null)\r
1001                 throw new ArgumentNullException("reader");\r
1002             return CreateTranslateIterator<TResult>(reader);\r
1003         }\r
1004 \r
1005         IEnumerable<TResult> CreateTranslateIterator<TResult>(DbDataReader reader)\r
1006         {\r
1007             foreach (TResult result in Translate(typeof(TResult), reader))\r
1008                 yield return result;\r
1009         }\r
1010 \r
1011         public IMultipleResults Translate(DbDataReader reader)\r
1012         {\r
1013             throw new NotImplementedException();\r
1014         }\r
1015 \r
1016         public IEnumerable Translate(Type elementType, DbDataReader reader)\r
1017         {\r
1018             if (elementType == null)\r
1019                 throw new ArgumentNullException("elementType");\r
1020             if (reader == null)\r
1021                 throw new ArgumentNullException("reader");\r
1022 \r
1023             return QueryRunner.EnumerateResult(elementType, reader, this);\r
1024         }\r
1025 \r
1026         public void Dispose()\r
1027         {\r
1028             //connection closing should not be done here.\r
1029             //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx\r
1030         }\r
1031 \r
1032         [DbLinqToDo]\r
1033         protected virtual void Dispose(bool disposing)\r
1034         {\r
1035             throw new NotImplementedException();\r
1036         }\r
1037 \r
1038         /// <summary>\r
1039         /// Creates a IDbDataAdapter. Used internally by Vendors\r
1040         /// </summary>\r
1041         /// <returns></returns>\r
1042         internal IDbDataAdapter CreateDataAdapter()\r
1043         {\r
1044             return DatabaseContext.CreateDataAdapter();\r
1045         }\r
1046 \r
1047         /// <summary>\r
1048         /// Sets a TextWriter where generated SQL commands are written\r
1049         /// </summary>\r
1050         public TextWriter Log { get; set; }\r
1051 \r
1052         /// <summary>\r
1053         /// Writes text on Log (if not null)\r
1054         /// Internal helper\r
1055         /// </summary>\r
1056         /// <param name="text"></param>\r
1057         internal void WriteLog(string text)\r
1058         {\r
1059             if (Log != null)\r
1060                 Log.WriteLine(text);\r
1061         }\r
1062 \r
1063         /// <summary>\r
1064         /// Write an IDbCommand to Log (if non null)\r
1065         /// </summary>\r
1066         /// <param name="command"></param>\r
1067         internal void WriteLog(IDbCommand command)\r
1068         {\r
1069             if (Log != null)\r
1070             {\r
1071                 Log.WriteLine(command.CommandText);\r
1072                 foreach (IDbDataParameter parameter in command.Parameters)\r
1073                     WriteLog(parameter);\r
1074                 Log.Write("--");\r
1075                 Log.Write(" Context: {0}", Vendor.VendorName);\r
1076                 Log.Write(" Model: {0}", Mapping.GetType().Name);\r
1077                 Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version);\r
1078                 Log.WriteLine();\r
1079             }\r
1080         }\r
1081 \r
1082         /// <summary>\r
1083         /// Writes and IDbDataParameter to Log (if non null)\r
1084         /// </summary>\r
1085         /// <param name="parameter"></param>\r
1086         internal void WriteLog(IDbDataParameter parameter)\r
1087         {\r
1088             if (Log != null)\r
1089             {\r
1090                 // -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2]\r
1091                 // -- <name>: <direction> <type> (...) [<value>]\r
1092                 Log.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]",\r
1093                     parameter.ParameterName, parameter.Direction, parameter.DbType,\r
1094                     parameter.Size, parameter.Precision, parameter.Scale, parameter.Value);\r
1095             }\r
1096         }\r
1097 \r
1098         public bool ObjectTrackingEnabled\r
1099         {\r
1100             get { return this.objectTrackingEnabled; }\r
1101             set \r
1102             {\r
1103                 if (this.currentTransactionEntities != null && value != this.objectTrackingEnabled)\r
1104                     throw new InvalidOperationException("Data context options cannot be modified after results have been returned from a query.");\r
1105                 this.objectTrackingEnabled = value;\r
1106             }\r
1107         }\r
1108 \r
1109         [DbLinqToDo]\r
1110         public int CommandTimeout\r
1111         {\r
1112             get { throw new NotImplementedException(); }\r
1113             set { throw new NotImplementedException(); }\r
1114         }\r
1115 \r
1116         public bool DeferredLoadingEnabled\r
1117         {\r
1118             get { return this.deferredLoadingEnabled; }\r
1119             set\r
1120             {\r
1121                 if (this.currentTransactionEntities != null && value != this.deferredLoadingEnabled)\r
1122                     throw new InvalidOperationException("Data context options cannot be modified after results have been returned from a query.");\r
1123                 this.deferredLoadingEnabled = value;\r
1124             }\r
1125         }\r
1126 \r
1127         [DbLinqToDo]\r
1128         public ChangeConflictCollection ChangeConflicts\r
1129         {\r
1130             get { throw new NotImplementedException(); }\r
1131         }\r
1132 \r
1133         [DbLinqToDo]\r
1134         public DbCommand GetCommand(IQueryable query)\r
1135         {\r
1136             DbCommand dbCommand = GetIDbCommand(query) as DbCommand;\r
1137             if (dbCommand == null)\r
1138                 throw new InvalidOperationException();\r
1139 \r
1140             return dbCommand;\r
1141         }\r
1142 \r
1143         [DBLinqExtended]\r
1144         public IDbCommand GetIDbCommand(IQueryable query)\r
1145         {\r
1146             if (query == null)\r
1147                 throw new ArgumentNullException("query");\r
1148 \r
1149             var qp = query.Provider as QueryProvider;\r
1150             if (qp == null)\r
1151                 throw new InvalidOperationException();\r
1152 \r
1153             if (qp.ExpressionChain.Expressions.Count == 0)\r
1154                 qp.ExpressionChain.Expressions.Add(CreateDefaultQuery(query));\r
1155 \r
1156             return qp.GetQuery(null).GetCommand().Command;\r
1157         }\r
1158 \r
1159         private Expression CreateDefaultQuery(IQueryable query)\r
1160         {\r
1161             // Manually create the expression tree for: IQueryable<TableType>.Select(e => e)\r
1162             var identityParameter = Expression.Parameter(query.ElementType, "e");\r
1163             var identityBody = Expression.Lambda(\r
1164                 typeof(Func<,>).MakeGenericType(query.ElementType, query.ElementType),\r
1165                 identityParameter,\r
1166                 new[] { identityParameter }\r
1167             );\r
1168 \r
1169             return Expression.Call(\r
1170                 typeof(Queryable),\r
1171                 "Select",\r
1172                 new[] { query.ElementType, query.ElementType },\r
1173                 query.Expression,\r
1174                 Expression.Quote(identityBody)\r
1175             );\r
1176         }\r
1177 \r
1178         [DbLinqToDo]\r
1179         public void Refresh(RefreshMode mode, IEnumerable entities)\r
1180         {\r
1181             throw new NotImplementedException();\r
1182         }\r
1183 \r
1184         [DbLinqToDo]\r
1185         public void Refresh(RefreshMode mode, params object[] entities)\r
1186         {\r
1187             throw new NotImplementedException();\r
1188         }\r
1189 \r
1190         [DbLinqToDo]\r
1191         public void Refresh(RefreshMode mode, object entity)\r
1192         {\r
1193             throw new NotImplementedException();\r
1194         }\r
1195 \r
1196         [DbLinqToDo]\r
1197         public void DeleteDatabase()\r
1198         {\r
1199             throw new NotImplementedException();\r
1200         }\r
1201 \r
1202         [DbLinqToDo]\r
1203         public void CreateDatabase()\r
1204         {\r
1205             throw new NotImplementedException();\r
1206         }\r
1207 \r
1208         [DbLinqToDo]\r
1209         protected internal IQueryable<TResult> CreateMethodCallQuery<TResult>(object instance, MethodInfo methodInfo, params object[] parameters)\r
1210         {\r
1211             throw new NotImplementedException();\r
1212         }\r
1213 \r
1214         [DbLinqToDo]\r
1215         protected internal void ExecuteDynamicDelete(object entity)\r
1216         {\r
1217             throw new NotImplementedException();\r
1218         }\r
1219 \r
1220         [DbLinqToDo]\r
1221         protected internal void ExecuteDynamicInsert(object entity)\r
1222         {\r
1223             throw new NotImplementedException();\r
1224         }\r
1225 \r
1226         [DbLinqToDo]\r
1227         protected internal void ExecuteDynamicUpdate(object entity)\r
1228         {\r
1229             throw new NotImplementedException();\r
1230         }\r
1231     }\r
1232 }\r