second step.
[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.Mapping;\r
32 using System.Diagnostics;\r
33 using System.Collections.Generic;\r
34 using System.Linq;\r
35 using System.Reflection;\r
36 \r
37 #if MONO_STRICT\r
38 using System.Data.Linq.Implementation;\r
39 using System.Data.Linq.Sugar;\r
40 using System.Data.Linq.Identity;\r
41 using DbLinq.Util;\r
42 using AttributeMappingSource = System.Data.Linq.Mapping.AttributeMappingSource;\r
43 using MappingContext = System.Data.Linq.Mapping.MappingContext;\r
44 using DbLinq;\r
45 #else\r
46 using DbLinq.Data.Linq.Implementation;\r
47 using DbLinq.Data.Linq.Sugar;\r
48 using DbLinq.Data.Linq.Identity;\r
49 using DbLinq.Util;\r
50 using AttributeMappingSource = DbLinq.Data.Linq.Mapping.AttributeMappingSource;\r
51 using MappingContext = DbLinq.Data.Linq.Mapping.MappingContext;\r
52 using System.Data.Linq;\r
53 #endif\r
54 \r
55 using DbLinq.Factory;\r
56 using DbLinq.Logging;\r
57 using DbLinq.Vendor;\r
58 using DbLinq.Data.Linq.Database;\r
59 using DbLinq.Data.Linq.Database.Implementation;\r
60 using System.Linq.Expressions;\r
61 \r
62 #if MONO_STRICT\r
63 namespace System.Data.Linq\r
64 #else\r
65 namespace DbLinq.Data.Linq\r
66 #endif\r
67 {\r
68     public partial class DataContext : IDisposable\r
69     {\r
70         private readonly Dictionary<string, ITable> _tableMap = new Dictionary<string, ITable>();\r
71 \r
72         public MetaModel Mapping { get; private set; }\r
73         // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection\r
74         //              WTF?\r
75         public DbConnection Connection { get { return DatabaseContext.Connection as DbConnection; } }\r
76 \r
77         // all properties below are set public to optionally be injected\r
78         internal IVendor Vendor { get; set; }\r
79         internal IQueryBuilder QueryBuilder { get; set; }\r
80         internal IQueryRunner QueryRunner { get; set; }\r
81         internal IMemberModificationHandler MemberModificationHandler { get; set; }\r
82         internal IDatabaseContext DatabaseContext { get; private set; }\r
83         internal ILogger Logger { get; set; }\r
84         // /all properties...\r
85 \r
86         // entities may be registered in 3 sets: InsertList, EntityMap and DeleteList\r
87         // InsertList is for new entities\r
88         // DeleteList is for entities to be deleted\r
89         // EntityMap is the cache: entities are alive in the DataContext, identified by their PK (IdentityKey)\r
90         // an entity can only live in one of the three caches, so the DataContext will provide 6 methods:\r
91         // 3 to register in each list, 3 to unregister\r
92         //internal IEntityMap EntityMap { get; set; }\r
93         //internal readonly EntityList InsertList = new EntityList();\r
94         //internal readonly EntityList DeleteList = new EntityList();\r
95         private readonly EntityTracker entityTracker = new EntityTracker();\r
96 \r
97         private IIdentityReaderFactory identityReaderFactory;\r
98         private readonly IDictionary<Type, IIdentityReader> identityReaders = new Dictionary<Type, IIdentityReader>();\r
99 \r
100 \r
101         /// <summary>\r
102         /// The default behavior creates one MappingContext.\r
103         /// </summary>\r
104         [DBLinqExtended]\r
105         internal virtual MappingContext _MappingContext { get; set; }\r
106 \r
107         [DBLinqExtended]\r
108         internal IVendorProvider _VendorProvider { get; set; }\r
109 \r
110         [DbLinqToDo]\r
111         public DataContext(IDbConnection connection, MappingSource mapping)\r
112         {\r
113             Init(new DatabaseContext(connection), mapping, null);\r
114         }\r
115 \r
116         [DbLinqToDo]\r
117         public DataContext(IDbConnection connection)\r
118         {\r
119             Init(new DatabaseContext(connection), null, null);\r
120         }\r
121 \r
122         [DbLinqToDo]\r
123         public DataContext(string fileOrServerOrConnection, MappingSource mapping)\r
124         {\r
125             throw new NotImplementedException();\r
126         }\r
127 \r
128         [DbLinqToDo]\r
129         public DataContext(string fileOrServerOrConnection)\r
130         {\r
131             throw new NotImplementedException();\r
132         }\r
133 \r
134         private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor)\r
135         {\r
136 \r
137             if (databaseContext == null)\r
138                 throw new ArgumentNullException("databaseContext");\r
139 \r
140             Logger = ObjectFactory.Get<ILogger>();\r
141 \r
142             _VendorProvider = ObjectFactory.Get<IVendorProvider>();\r
143             if (vendor == null)\r
144                 Vendor = _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider));\r
145             else\r
146                 Vendor = vendor;\r
147 \r
148             DatabaseContext = databaseContext;\r
149 \r
150             MemberModificationHandler = ObjectFactory.Create<IMemberModificationHandler>(); // not a singleton: object is stateful\r
151             QueryBuilder = ObjectFactory.Get<IQueryBuilder>();\r
152             QueryRunner = ObjectFactory.Get<IQueryRunner>();\r
153 \r
154             //EntityMap = ObjectFactory.Create<IEntityMap>();\r
155             identityReaderFactory = ObjectFactory.Get<IIdentityReaderFactory>();\r
156 \r
157             _MappingContext = new MappingContext();\r
158 \r
159             // initialize the mapping information\r
160             if (mappingSource == null)\r
161                 mappingSource = new AttributeMappingSource();\r
162             Mapping = mappingSource.GetModel(GetType());\r
163         }\r
164 \r
165 \r
166         public Table<TEntity> GetTable<TEntity>() where TEntity : class\r
167         {\r
168             return (Table<TEntity>)GetTable(typeof(TEntity));\r
169         }\r
170 \r
171         public ITable GetTable(Type type)\r
172         {\r
173             lock (_tableMap)\r
174             {\r
175                 string tableName = type.FullName;\r
176                 ITable tableExisting;\r
177                 if (_tableMap.TryGetValue(tableName, out tableExisting))\r
178                     return tableExisting;\r
179 \r
180                 var tableNew = Activator.CreateInstance(\r
181                                   typeof(Table<>).MakeGenericType(type)\r
182                                   , BindingFlags.NonPublic | BindingFlags.Instance\r
183                                   , null\r
184                                   , new object[] { this }\r
185                                   , System.Globalization.CultureInfo.CurrentCulture) as ITable;\r
186 \r
187                 _tableMap[tableName] = tableNew;\r
188                 return tableNew;\r
189             }\r
190         }\r
191 \r
192         public void SubmitChanges()\r
193         {\r
194             SubmitChanges(ConflictMode.FailOnFirstConflict);\r
195         }\r
196 \r
197         /// <summary>\r
198         /// Pings database\r
199         /// </summary>\r
200         /// <returns></returns>\r
201         public bool DatabaseExists()\r
202         {\r
203             try\r
204             {\r
205                 return Vendor.Ping(this);\r
206             }\r
207             catch (Exception)\r
208             {\r
209                 return false;\r
210             }\r
211         }\r
212 \r
213         /// <summary>\r
214         /// Commits all pending changes to database \r
215         /// </summary>\r
216         /// <param name="failureMode"></param>\r
217         public virtual void SubmitChanges(ConflictMode failureMode)\r
218         {\r
219             using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us\r
220             using (IDatabaseTransaction transaction = DatabaseContext.Transaction())\r
221             {\r
222                 var queryContext = new QueryContext(this);\r
223                 var entityTracks = entityTracker.EnumerateAll().ToList();\r
224                 foreach (var entityTrack in entityTracks)\r
225                 {\r
226                     switch (entityTrack.EntityState)\r
227                     {\r
228                     case EntityState.ToInsert:\r
229                         var insertQuery = QueryBuilder.GetInsertQuery(entityTrack.Entity, queryContext);\r
230                         QueryRunner.Insert(entityTrack.Entity, insertQuery);\r
231                         Register(entityTrack.Entity);\r
232                         break;\r
233                     case EntityState.ToWatch:\r
234                         if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
235                         {\r
236                             var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entityTrack.Entity, Mapping);\r
237                             var updateQuery = QueryBuilder.GetUpdateQuery(entityTrack.Entity, modifiedMembers, queryContext);\r
238                             QueryRunner.Update(entityTrack.Entity, updateQuery, modifiedMembers);\r
239 \r
240                             RegisterUpdateAgain(entityTrack.Entity);\r
241                         }\r
242                         break;\r
243                     case EntityState.ToDelete:\r
244                         var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext);\r
245                         QueryRunner.Delete(entityTrack.Entity, deleteQuery);\r
246 \r
247                         UnregisterDelete(entityTrack.Entity);\r
248                         break;\r
249                     default:\r
250                         throw new ArgumentOutOfRangeException();\r
251                     }\r
252                 }\r
253                 // TODO: handle conflicts (which can only occur when concurrency mode is implemented)\r
254                 transaction.Commit();\r
255             }\r
256         }\r
257 \r
258         /// <summary>\r
259         /// TODO - allow generated methods to call into stored procedures\r
260         /// </summary>\r
261         [DBLinqExtended]\r
262         internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams)\r
263         {\r
264             using (DatabaseContext.OpenConnection())\r
265             {\r
266                 System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams);\r
267                 return result;\r
268             }\r
269         }\r
270 \r
271         [DbLinqToDo]\r
272         protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters)\r
273         {\r
274             throw new NotImplementedException();\r
275         }\r
276 \r
277         #region Identity management\r
278 \r
279         [DBLinqExtended]\r
280         internal IIdentityReader _GetIdentityReader(Type t)\r
281         {\r
282             IIdentityReader identityReader;\r
283             lock (identityReaders)\r
284             {\r
285                 if (!identityReaders.TryGetValue(t, out identityReader))\r
286                 {\r
287                     identityReader = identityReaderFactory.GetReader(t, this);\r
288                     identityReaders[t] = identityReader;\r
289                 }\r
290             }\r
291             return identityReader;\r
292         }\r
293 \r
294         [DBLinqExtended]\r
295         internal object _GetRegisteredEntity(object entity)\r
296         {\r
297             // TODO: check what is faster: by identity or by ref\r
298             var identityReader = _GetIdentityReader(entity.GetType());\r
299             var identityKey = identityReader.GetIdentityKey(entity);\r
300             if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK\r
301                 return entity;\r
302             // even \r
303             var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);\r
304             if (registeredEntityTrack != null)\r
305                 return registeredEntityTrack.Entity;\r
306             return null;\r
307         }\r
308 \r
309         //internal object GetRegisteredEntityByKey(IdentityKey identityKey)\r
310         //{\r
311         //    return EntityMap[identityKey];\r
312         //}\r
313 \r
314         /// <summary>\r
315         /// Registers an entity in a watch state\r
316         /// </summary>\r
317         /// <param name="entity"></param>\r
318         /// <returns></returns>\r
319         [DBLinqExtended]\r
320         internal object _GetOrRegisterEntity(object entity)\r
321         {\r
322             var identityReader = _GetIdentityReader(entity.GetType());\r
323             var identityKey = identityReader.GetIdentityKey(entity);\r
324             SetEntitySetsQueries(entity);\r
325             SetEntityRefQueries(entity);\r
326 \r
327             // if we have no identity, we can't track it\r
328             if (identityKey == null)\r
329                 return entity;\r
330 \r
331             // try to find an already registered entity and return it\r
332             var registeredEntityTrack = entityTracker.FindByIdentity(identityKey);\r
333             if (registeredEntityTrack != null)\r
334                 return registeredEntityTrack.Entity;\r
335 \r
336             // otherwise, register and return\r
337             entityTracker.RegisterToWatch(entity, identityKey);\r
338             return entity;\r
339         }\r
340 \r
341         readonly IDataMapper DataMapper = ObjectFactory.Get<IDataMapper>();\r
342         private void SetEntityRefQueries(object entity)\r
343         {\r
344             Type thisType = entity.GetType();\r
345             IList<MemberInfo> properties = DataMapper.GetEntityRefAssociations(thisType);\r
346 \r
347 \r
348             foreach (PropertyInfo prop in properties)\r
349             {\r
350                 //example of entityRef:Order.Employee\r
351                 AssociationAttribute associationInfo = prop.GetAttribute<AssociationAttribute>();\r
352                 Type otherTableType = prop.PropertyType;\r
353                 IList<MemberInfo> otherPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(otherTableType));\r
354 \r
355                 if (otherPKs.Count > 1)\r
356                     throw new NotSupportedException("Multiple keys object not supported yet.");\r
357 \r
358                 var otherTable = GetTable(otherTableType);\r
359 \r
360                 //ie:EmployeeTerritories.EmployeeID\r
361 \r
362                 var thisForeignKeyProperty = thisType.GetProperty(associationInfo.ThisKey);\r
363                 object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null);\r
364 \r
365                 IEnumerable query = null;\r
366                 if (thisForeignKeyValue != null)\r
367                 {\r
368                     ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
369                     Expression predicate;\r
370                     if (!(thisForeignKeyProperty.PropertyType.IsNullable()))\r
371                     {\r
372                         predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),\r
373                                                                     Expression.Constant(thisForeignKeyValue));\r
374                     }\r
375                     else\r
376                     {\r
377                         var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value");\r
378                         predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()),\r
379                                                                  Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null)));\r
380                     }\r
381 \r
382                     query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable;\r
383                     //it would be interesting surround the above query with a .Take(1) expression for performance.\r
384                 }\r
385 \r
386 \r
387                 FieldInfo entityRefField = entity.GetType().GetField(associationInfo.Storage, BindingFlags.NonPublic | BindingFlags.Instance);\r
388                 object entityRefValue = null;\r
389                 if (query != null)\r
390                     entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query);\r
391                 else\r
392                     entityRefValue = Activator.CreateInstance(entityRefField.FieldType);\r
393                 entityRefField.SetValue(entity, entityRefValue);\r
394             }\r
395         }\r
396 \r
397         /// <summary>\r
398         /// 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
399         /// Here we set the query source of each EntitySetProperty\r
400         /// </summary>\r
401         /// <param name="entity"></param>\r
402         private void SetEntitySetsQueries(object entity)\r
403         {\r
404             IList<MemberInfo> properties = DataMapper.GetEntitySetAssociations(entity.GetType());\r
405             IList<MemberInfo> thisPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(entity.GetType()));\r
406 \r
407             if (thisPKs.Count > 1 && properties.Any())\r
408                 throw new NotSupportedException("Multiple keys object not supported yet.");\r
409 \r
410             object primaryKeyValue = (thisPKs.First() as PropertyInfo).GetValue(entity, null);\r
411 \r
412 \r
413             foreach (PropertyInfo prop in properties)\r
414             {\r
415                 //example of entitySet: Employee.EmployeeTerritories\r
416                 var associationInfo = prop.GetAttribute<AssociationAttribute>();\r
417                 Type otherTableType = prop.PropertyType.GetGenericArguments().First();\r
418 \r
419                 //other table:EmployeeTerritories\r
420                 var otherTable = GetTable(otherTableType);\r
421                 //other table member:EmployeeTerritories.EmployeeID\r
422                 var otherTableMember = otherTableType.GetProperty(associationInfo.OtherKey);\r
423 \r
424 \r
425                 ParameterExpression p = Expression.Parameter(otherTableType, "other");\r
426                 Expression predicate;\r
427                 if (!(otherTableMember.PropertyType.IsNullable()))\r
428                 {\r
429                     predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember),\r
430                                                                 Expression.Constant(primaryKeyValue));\r
431                 }\r
432                 else\r
433                 {\r
434                     var ValueProperty = otherTableMember.PropertyType.GetProperty("Value");\r
435                     predicate = Expression.Equal(Expression.MakeMemberAccess(\r
436                                                                 Expression.MakeMemberAccess(p, otherTableMember),\r
437                                                                 ValueProperty),\r
438                                                              Expression.Constant(primaryKeyValue));\r
439                 }\r
440 \r
441                 var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable);\r
442 \r
443                 var entitySetValue = prop.GetValue(entity, null);\r
444 \r
445                 if (entitySetValue == null)\r
446                 {\r
447                     entitySetValue = Activator.CreateInstance(prop.PropertyType);\r
448                     prop.SetValue(entity, entitySetValue, null);\r
449                 }\r
450 \r
451                 var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource");\r
452                 setSourceMethod.Invoke(entitySetValue, new[] { query });\r
453                 //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH"))\r
454             }\r
455         }\r
456 \r
457         private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable)\r
458         {\r
459             //predicate: other.EmployeeID== "WARTH"\r
460             Expression lambdaPredicate = Expression.Lambda(predicate, parameter);\r
461             //lambdaPredicate: other=>other.EmployeeID== "WARTH"\r
462 \r
463             var whereMethod = typeof(Queryable)\r
464                               .GetMethods().First(m => m.Name == "Where")\r
465                               .MakeGenericMethod(otherTableType);\r
466 \r
467 \r
468             Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate);\r
469             //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")\r
470 \r
471             return otherTable.Provider.CreateQuery(call);\r
472         }\r
473 \r
474         #endregion\r
475 \r
476         #region Insert/Update/Delete management\r
477 \r
478         /// <summary>\r
479         /// Registers an entity for insert\r
480         /// </summary>\r
481         /// <param name="entity"></param>\r
482         internal void RegisterInsert(object entity)\r
483         {\r
484             entityTracker.RegisterToInsert(entity);\r
485         }\r
486 \r
487         /// <summary>\r
488         /// Registers an entity for update\r
489         /// The entity will be updated only if some of its members have changed after the registration\r
490         /// </summary>\r
491         /// <param name="entity"></param>\r
492         internal void RegisterUpdate(object entity)\r
493         {\r
494             var identityReader = _GetIdentityReader(entity.GetType());\r
495             var identityKey = identityReader.GetIdentityKey(entity);\r
496             // if we have no key, we can not watch\r
497             if (identityKey == null)\r
498                 return;\r
499             // register entity\r
500             entityTracker.RegisterToWatch(entity, identityKey);\r
501         }\r
502 \r
503         /// <summary>\r
504         /// Registers or re-registers an entity and clears its state\r
505         /// </summary>\r
506         /// <param name="entity"></param>\r
507         /// <returns></returns>\r
508         internal object Register(object entity)\r
509         {\r
510             var registeredEntity = _GetOrRegisterEntity(entity);\r
511             // the fact of registering again clears the modified state, so we're... clear with that\r
512             MemberModificationHandler.Register(registeredEntity, Mapping);\r
513             return registeredEntity;\r
514         }\r
515 \r
516         /// <summary>\r
517         /// Registers an entity for update\r
518         /// The entity will be updated only if some of its members have changed after the registration\r
519         /// </summary>\r
520         /// <param name="entity"></param>\r
521         /// <param name="entityOriginalState"></param>\r
522         internal void RegisterUpdate(object entity, object entityOriginalState)\r
523         {\r
524             RegisterUpdate(entity);\r
525             MemberModificationHandler.Register(entity, entityOriginalState, Mapping);\r
526         }\r
527 \r
528         /// <summary>\r
529         /// Clears the current state, and marks the object as clean\r
530         /// </summary>\r
531         /// <param name="entity"></param>\r
532         internal void RegisterUpdateAgain(object entity)\r
533         {\r
534             MemberModificationHandler.ClearModified(entity, Mapping);\r
535         }\r
536 \r
537         /// <summary>\r
538         /// Registers an entity for delete\r
539         /// </summary>\r
540         /// <param name="entity"></param>\r
541         internal void RegisterDelete(object entity)\r
542         {\r
543             entityTracker.RegisterToDelete(entity);\r
544         }\r
545 \r
546         /// <summary>\r
547         /// Unregisters entity after deletion\r
548         /// </summary>\r
549         /// <param name="entity"></param>\r
550         internal void UnregisterDelete(object entity)\r
551         {\r
552             entityTracker.RegisterDeleted(entity);\r
553         }\r
554 \r
555         #endregion\r
556 \r
557         /// <summary>\r
558         /// Changed object determine \r
559         /// </summary>\r
560         /// <returns>Lists of inserted, updated, deleted objects</returns>\r
561         public ChangeSet GetChangeSet()\r
562         {\r
563             var inserts = new List<object>();\r
564             var updates = new List<object>();\r
565             var deletes = new List<object>();\r
566             foreach (var entityTrack in entityTracker.EnumerateAll())\r
567             {\r
568                 switch (entityTrack.EntityState)\r
569                 {\r
570                 case EntityState.ToInsert:\r
571                     inserts.Add(entityTrack.Entity);\r
572                     break;\r
573                 case EntityState.ToWatch:\r
574                     if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping))\r
575                         updates.Add(entityTrack.Entity);\r
576                     break;\r
577                 case EntityState.ToDelete:\r
578                     deletes.Add(entityTrack.Entity);\r
579                     break;\r
580                 default:\r
581                     throw new ArgumentOutOfRangeException();\r
582                 }\r
583             }\r
584             return new ChangeSet(inserts, updates, deletes);\r
585         }\r
586 \r
587         /// <summary>\r
588         /// use ExecuteCommand to call raw SQL\r
589         /// </summary>\r
590         public int ExecuteCommand(string command, params object[] parameters)\r
591         {\r
592             var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this));\r
593             return QueryRunner.Execute(directQuery, parameters);\r
594         }\r
595 \r
596         /// <summary>\r
597         /// Execute raw SQL query and return object\r
598         /// </summary>\r
599         public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters) where TResult : class, new()\r
600         {\r
601             GetTable<TResult>();\r
602             foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters))\r
603                 yield return result;\r
604         }\r
605 \r
606         public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters)\r
607         {\r
608             var queryContext = new QueryContext(this);\r
609             var directQuery = QueryBuilder.GetDirectQuery(query, queryContext);\r
610             return QueryRunner.ExecuteSelect(elementType, directQuery, parameters);\r
611         }\r
612 \r
613         /// <summary>\r
614         /// TODO: DataLoadOptions ds = new DataLoadOptions(); ds.LoadWith<Customer>(p => p.Orders);\r
615         /// </summary>\r
616         [DbLinqToDo]\r
617         public DataLoadOptions LoadOptions\r
618         {\r
619             get;\r
620             set;\r
621         }\r
622 \r
623         public DbTransaction Transaction { get; set; }\r
624 \r
625         public IEnumerable<TResult> Translate<TResult>(DbDataReader reader)\r
626         {\r
627             foreach (TResult result in Translate(typeof(TResult), reader))\r
628                 yield return result;\r
629         }\r
630 \r
631         public IMultipleResults Translate(DbDataReader reader)\r
632         {\r
633             throw new NotImplementedException();\r
634         }\r
635 \r
636         public IEnumerable Translate(Type elementType, DbDataReader reader)\r
637         {\r
638             return QueryRunner.EnumerateResult(elementType, reader, this);\r
639         }\r
640 \r
641         public void Dispose()\r
642         {\r
643             //connection closing should not be done here.\r
644             //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx\r
645         }\r
646 \r
647         [DbLinqToDo]\r
648         protected virtual void Dispose(bool disposing)\r
649         {\r
650             throw new NotImplementedException();\r
651         }\r
652 \r
653         /// <summary>\r
654         /// Creates a IDbDataAdapter. Used internally by Vendors\r
655         /// </summary>\r
656         /// <returns></returns>\r
657         internal IDbDataAdapter CreateDataAdapter()\r
658         {\r
659             return DatabaseContext.CreateDataAdapter();\r
660         }\r
661 \r
662         /// <summary>\r
663         /// Sets a TextWriter where generated SQL commands are written\r
664         /// </summary>\r
665         public System.IO.TextWriter Log { get; set; }\r
666 \r
667         /// <summary>\r
668         /// Writes the generated SQL on Log (if not null)\r
669         /// Internal helper\r
670         /// </summary>\r
671         /// <param name="sql"></param>\r
672         internal void WriteLog(string sql)\r
673         {\r
674             if (Log != null)\r
675             {\r
676                 // Log example:\r
677                 //SELECT [t0].[FirstName] AS [Name], [t1].[FirstName] AS [ReportsTo]\r
678                 //FROM [dbo].[Employees] AS [t0]\r
679                 //LEFT OUTER JOIN [dbo].[Employees] AS [t1] ON [t1].[EmployeeID] = [t0].[ReportsTo]\r
680                 //-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.1\r
681                 Log.WriteLine(sql);\r
682                 Log.Write("--");\r
683                 Log.Write(" Context: {0}", Vendor.VendorName);\r
684                 Log.Write(" Model: {0}", Mapping.GetType().Name);\r
685                 Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version);\r
686                 Log.WriteLine();\r
687             }\r
688         }\r
689 \r
690         [DbLinqToDo]\r
691         public bool ObjectTrackingEnabled\r
692         {\r
693             get { throw new NotImplementedException(); }\r
694             set { throw new NotImplementedException(); }\r
695         }\r
696 \r
697         [DbLinqToDo]\r
698         public int CommandTimeout\r
699         {\r
700             get { throw new NotImplementedException(); }\r
701             set { throw new NotImplementedException(); }\r
702         }\r
703 \r
704         [DbLinqToDo]\r
705         public bool DeferredLoadingEnabled\r
706         {\r
707             get { throw new NotImplementedException(); }\r
708             set { throw new NotImplementedException(); }\r
709         }\r
710 \r
711         [DbLinqToDo]\r
712         public ChangeConflictCollection ChangeConflicts\r
713         {\r
714             get { throw new NotImplementedException(); }\r
715         }\r
716 \r
717         [DbLinqToDo]\r
718         public DbCommand GetCommand(IQueryable query)\r
719         {\r
720             throw new NotImplementedException();\r
721         }\r
722 \r
723         [DbLinqToDo]\r
724         public void Refresh(System.Data.Linq.RefreshMode mode, System.Collections.IEnumerable entities)\r
725         {\r
726             throw new NotImplementedException();\r
727         }\r
728 \r
729         [DbLinqToDo]\r
730         public void Refresh(RefreshMode mode, params object[] entities)\r
731         {\r
732             throw new NotImplementedException();\r
733         }\r
734 \r
735         [DbLinqToDo]\r
736         public void Refresh(RefreshMode mode, object entity)\r
737         {\r
738             throw new NotImplementedException();\r
739         }\r
740 \r
741         [DbLinqToDo]\r
742         public void DeleteDatabase()\r
743         {\r
744             throw new NotImplementedException();\r
745         }\r
746 \r
747         [DbLinqToDo]\r
748         public void CreateDatabase()\r
749         {\r
750             throw new NotImplementedException();\r
751         }\r
752 \r
753         [DbLinqToDo]\r
754         protected internal IQueryable<TResult> CreateMethodCallQuery<TResult>(object instance, MethodInfo methodInfo, params object[] parameters)\r
755         {\r
756             throw new NotImplementedException();\r
757         }\r
758 \r
759         [DbLinqToDo]\r
760         protected internal void ExecuteDynamicDelete(object entity)\r
761         {\r
762             throw new NotImplementedException();\r
763         }\r
764 \r
765         [DbLinqToDo]\r
766         protected internal void ExecuteDynamicInsert(object entity)\r
767         {\r
768             throw new NotImplementedException();\r
769         }\r
770 \r
771         [DbLinqToDo]\r
772         protected internal void ExecuteDynamicUpdate(object entity)\r
773         {\r
774             throw new NotImplementedException();\r
775         }\r
776     }\r
777 }\r