X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Data.Linq%2Fsrc%2FDbLinq%2FData%2FLinq%2FDataContext.cs;h=de96cc50e3202f0efa1d34d226b0f761409e8e71;hb=a3f972be9e1edf4ec9ae836ad5a6cf35519ec180;hp=568dc358bde775e9415daa62f5c77fdd1042a624;hpb=987bef88b545cf92ab64068e0deba5fecea765fd;p=mono.git diff --git a/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs b/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs index 568dc358bde..de96cc50e32 100644 --- a/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs +++ b/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs @@ -1,929 +1,929 @@ -#region MIT license -// -// MIT license -// -// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -#endregion - -using System; -using System.Collections; -using System.Data; -using System.Data.Common; -using System.Data.Linq.Mapping; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; - -#if MONO_STRICT -using System.Data.Linq.Implementation; -using System.Data.Linq.Sugar; -using System.Data.Linq.Identity; -using DbLinq.Util; -using AttributeMappingSource = System.Data.Linq.Mapping.AttributeMappingSource; -using MappingContext = System.Data.Linq.Mapping.MappingContext; -using DbLinq; -#else -using DbLinq.Data.Linq.Implementation; -using DbLinq.Data.Linq.Sugar; -using DbLinq.Data.Linq.Identity; -using DbLinq.Util; -using AttributeMappingSource = DbLinq.Data.Linq.Mapping.AttributeMappingSource; -using MappingContext = DbLinq.Data.Linq.Mapping.MappingContext; -using System.Data.Linq; -#endif - -using DbLinq.Factory; -using DbLinq.Vendor; -using DbLinq.Data.Linq.Database; -using DbLinq.Data.Linq.Database.Implementation; -using System.Linq.Expressions; -using System.Reflection.Emit; - -#if MONO_STRICT -namespace System.Data.Linq -#else -namespace DbLinq.Data.Linq -#endif -{ - public partial class DataContext : IDisposable - { - //private readonly Dictionary _tableMap = new Dictionary(); - private readonly Dictionary _tableMap = new Dictionary(); - - public MetaModel Mapping { get; private set; } - // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection - // WTF? - public DbConnection Connection { get { return DatabaseContext.Connection as DbConnection; } } - - // all properties below are set public to optionally be injected - internal IVendor Vendor { get; set; } - internal IQueryBuilder QueryBuilder { get; set; } - internal IQueryRunner QueryRunner { get; set; } - internal IMemberModificationHandler MemberModificationHandler { get; set; } - internal IDatabaseContext DatabaseContext { get; private set; } - // /all properties... - - private readonly EntityTracker entityTracker = new EntityTracker(); - - private IIdentityReaderFactory identityReaderFactory; - private readonly IDictionary identityReaders = new Dictionary(); - - /// - /// The default behavior creates one MappingContext. - /// - [DBLinqExtended] - internal virtual MappingContext _MappingContext { get; set; } - - [DBLinqExtended] - internal IVendorProvider _VendorProvider { get; set; } - - public DataContext(IDbConnection connection, MappingSource mapping) - { - Init(new DatabaseContext(connection), mapping, null); - } - - public DataContext(IDbConnection connection) - { - Init(new DatabaseContext(connection), null, null); - } - - [DbLinqToDo] - public DataContext(string fileOrServerOrConnection, MappingSource mapping) - { - throw new NotImplementedException(); - } - - /// - /// Construct DataContext, given a connectionString. - /// To determine which DB type to go against, we look for 'DbLinqProvider=xxx' substring. - /// If not found, we assume that we are dealing with MS Sql Server. - /// - /// Valid values are names of provider DLLs (or any other DLL containing an IVendor implementation) - /// DbLinqProvider=Mysql - /// DbLinqProvider=Oracle etc. - /// - /// specifies file or server connection - [DbLinqToDo] - public DataContext(string connectionString) - { - #region DataContext connectionString ctor - if (connectionString == null) - throw new ArgumentNullException("connectionString"); - - System.Text.RegularExpressions.Regex reProvider - = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+)"); - - int startPos = connectionString.IndexOf("DbLinqProvider="); - string assemblyToLoad; - string vendorClassToLoad; - if (!reProvider.IsMatch(connectionString)) - { - assemblyToLoad = "DbLinq.SqlServer.dll"; - vendorClassToLoad = "SqlServerVendor"; - } - else - { - System.Text.RegularExpressions.Match match = reProvider.Match(connectionString); -#if MONO_STRICT - //Pascal says on the forum: - //[in MONO] "all vendors are (will be) embedded in the System.Data.Linq assembly" - assemblyToLoad = "System.Data.Linq.dll"; - vendorClassToLoad = match.Groups[1].Value; //eg. "MySql" -#else - //plain DbLinq - non MONO: - //IVendor classes are in DLLs such as "DbLinq.MySql.dll" - assemblyToLoad = match.Groups[1].Value; //eg. assemblyToLoad="DbLinq.MySql.dll" - if (assemblyToLoad.Contains(".")) - { - //already fully qualified DLL name? - throw new ArgumentException("Please provide a short name, such as 'MySql', not '" + assemblyToLoad + "'"); - } - else - { - //we were given short name, such as MySql - vendorClassToLoad = assemblyToLoad + "Vendor"; //eg. MySqlVendor - assemblyToLoad = "DbLinq." + assemblyToLoad + ".dll"; //eg. DbLinq.MySql.dll - } -#endif - //shorten: "DbLinqProvider=X;Server=Y" -> ";Server=Y" - string shortenedConnStr = reProvider.Replace(connectionString, ""); - connectionString = shortenedConnStr; - } - - Assembly assy; - try - { -#if MONO_STRICT - assy = typeof (DataContext).Assembly; // System.Data.Linq.dll -#else - //TODO: check if DLL is already loaded? - assy = Assembly.LoadFrom(assemblyToLoad); -#endif - } - catch (Exception ex) - { - //TODO: add proper logging here - Console.WriteLine("DataContext ctor: Assembly load failed for " + assemblyToLoad + ": " + ex); - throw ex; - } - - //find IDbProvider class in this assembly: - var ctors = (from mod in assy.GetModules() - from cls in mod.GetTypes() - where cls.GetInterfaces().Contains(typeof(IVendor)) - && cls.Name.ToLower() == vendorClassToLoad.ToLower() - let ctorInfo = cls.GetConstructor(Type.EmptyTypes) - where ctorInfo != null - select ctorInfo).ToList(); - if (ctors.Count == 0) - { - string msg = "Found no IVendor class in assembly " + assemblyToLoad + " having a string ctor"; - throw new ArgumentException(msg); - } - else if (ctors.Count > 1) - { - string msg = "Found more than one IVendor class in assembly " + assemblyToLoad + " having a string ctor"; - throw new ArgumentException(msg); - } - ConstructorInfo ctorInfo2 = ctors[0]; - - object ivendorObject; - try - { - ivendorObject = ctorInfo2.Invoke(new object[]{}); - } - catch (Exception ex) - { - //TODO: add proper logging here - Console.WriteLine("DataContext ctor: Failed to invoke IVendor ctor " + ctorInfo2.Name + ": " + ex); - throw ex; - } - IVendor ivendor = (IVendor)ivendorObject; - IDbConnection dbConnection = ivendor.CreateDbConnection(connectionString); - Init(new DatabaseContext(dbConnection), null, ivendor); - #endregion - } - - private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor) - { - if (databaseContext == null) - throw new ArgumentNullException("databaseContext"); - - _VendorProvider = ObjectFactory.Get(); - if (vendor == null) - Vendor = _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider)); - else - Vendor = vendor; - - DatabaseContext = databaseContext; - - MemberModificationHandler = ObjectFactory.Create(); // not a singleton: object is stateful - QueryBuilder = ObjectFactory.Get(); - QueryRunner = ObjectFactory.Get(); - - //EntityMap = ObjectFactory.Create(); - identityReaderFactory = ObjectFactory.Get(); - - _MappingContext = new MappingContext(); - - // initialize the mapping information - if (mappingSource == null) - mappingSource = new AttributeMappingSource(); - Mapping = mappingSource.GetModel(GetType()); - } - - /// - /// Checks if the table is allready mapped or maps it if not. - /// - /// Type of the table. - /// Thrown if the table is not mappable. - private void CheckTableMapping(Type tableType) - { - //This will throw an exception if the table is not found - if(Mapping.GetTable(tableType) == null) - { - throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table."); - } - } - - /// - /// Returns a Table for the type TEntity. - /// - /// If the type TEntity is not mappable as a Table. - /// The table type. - public Table GetTable() where TEntity : class - { - return (Table)GetTable(typeof(TEntity)); - } - - /// - /// Returns a Table for the given type. - /// - /// The table type. - /// If the type is not mappable as a Table. - public ITable GetTable(Type type) - { - lock (_tableMap) - { - ITable tableExisting; - if (_tableMap.TryGetValue(type, out tableExisting)) - return tableExisting; - - //Check for table mapping - CheckTableMapping(type); - - var tableNew = Activator.CreateInstance( - typeof(Table<>).MakeGenericType(type) - , BindingFlags.NonPublic | BindingFlags.Instance - , null - , new object[] { this } - , System.Globalization.CultureInfo.CurrentCulture) as ITable; - - _tableMap[type] = tableNew; - return tableNew; - } - } - - public void SubmitChanges() - { - SubmitChanges(ConflictMode.FailOnFirstConflict); - } - - /// - /// Pings database - /// - /// - public bool DatabaseExists() - { - try - { - return Vendor.Ping(this); - } - catch (Exception) - { - return false; - } - } - - /// - /// Commits all pending changes to database - /// - /// - public virtual void SubmitChanges(ConflictMode failureMode) - { - using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us - using (IDatabaseTransaction transaction = DatabaseContext.Transaction()) - { - var queryContext = new QueryContext(this); - var entityTracks = entityTracker.EnumerateAll().ToList(); - foreach (var entityTrack in entityTracks) - { - switch (entityTrack.EntityState) - { - case EntityState.ToInsert: - var insertQuery = QueryBuilder.GetInsertQuery(entityTrack.Entity, queryContext); - QueryRunner.Insert(entityTrack.Entity, insertQuery); - Register(entityTrack.Entity); - break; - case EntityState.ToWatch: - if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping)) - { - var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entityTrack.Entity, Mapping); - var updateQuery = QueryBuilder.GetUpdateQuery(entityTrack.Entity, modifiedMembers, queryContext); - QueryRunner.Update(entityTrack.Entity, updateQuery, modifiedMembers); - - RegisterUpdateAgain(entityTrack.Entity); - } - break; - case EntityState.ToDelete: - var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext); - QueryRunner.Delete(entityTrack.Entity, deleteQuery); - - UnregisterDelete(entityTrack.Entity); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - // TODO: handle conflicts (which can only occur when concurrency mode is implemented) - transaction.Commit(); - } - } - - /// - /// TODO - allow generated methods to call into stored procedures - /// - [DBLinqExtended] - internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams) - { - using (DatabaseContext.OpenConnection()) - { - System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams); - return result; - } - } - - [DbLinqToDo] - protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters) - { - throw new NotImplementedException(); - } - - #region Identity management - - [DBLinqExtended] - internal IIdentityReader _GetIdentityReader(Type t) - { - IIdentityReader identityReader; - lock (identityReaders) - { - if (!identityReaders.TryGetValue(t, out identityReader)) - { - identityReader = identityReaderFactory.GetReader(t, this); - identityReaders[t] = identityReader; - } - } - return identityReader; - } - - [DBLinqExtended] - internal object _GetRegisteredEntity(object entity) - { - // TODO: check what is faster: by identity or by ref - var identityReader = _GetIdentityReader(entity.GetType()); - var identityKey = identityReader.GetIdentityKey(entity); - if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK - return entity; - // even - var registeredEntityTrack = entityTracker.FindByIdentity(identityKey); - if (registeredEntityTrack != null) - return registeredEntityTrack.Entity; - return null; - } - - //internal object GetRegisteredEntityByKey(IdentityKey identityKey) - //{ - // return EntityMap[identityKey]; - //} - - /// - /// Registers an entity in a watch state - /// - /// - /// - [DBLinqExtended] - internal object _GetOrRegisterEntity(object entity) - { - var identityReader = _GetIdentityReader(entity.GetType()); - var identityKey = identityReader.GetIdentityKey(entity); - SetEntitySetsQueries(entity); - SetEntityRefQueries(entity); - - // if we have no identity, we can't track it - if (identityKey == null) - return entity; - - // try to find an already registered entity and return it - var registeredEntityTrack = entityTracker.FindByIdentity(identityKey); - if (registeredEntityTrack != null) - return registeredEntityTrack.Entity; - - // otherwise, register and return - entityTracker.RegisterToWatch(entity, identityKey); - return entity; - } - - readonly IDataMapper DataMapper = ObjectFactory.Get(); - private void SetEntityRefQueries(object entity) - { - Type thisType = entity.GetType(); - IList properties = DataMapper.GetEntityRefAssociations(thisType); - - - foreach (PropertyInfo prop in properties) - { - //example of entityRef:Order.Employee - AssociationAttribute associationInfo = prop.GetAttribute(); - Type otherTableType = prop.PropertyType; - IList otherPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(otherTableType)); - - if (otherPKs.Count > 1) - throw new NotSupportedException("Multiple keys object not supported yet."); - - var otherTable = GetTable(otherTableType); - - //ie:EmployeeTerritories.EmployeeID - - var thisForeignKeyProperty = thisType.GetProperty(associationInfo.ThisKey); - object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null); - - IEnumerable query = null; - if (thisForeignKeyValue != null) - { - ParameterExpression p = Expression.Parameter(otherTableType, "other"); - Expression predicate; - if (!(thisForeignKeyProperty.PropertyType.IsNullable())) - { - predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()), - Expression.Constant(thisForeignKeyValue)); - } - else - { - var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value"); - predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()), - Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null))); - } - - query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable; - //it would be interesting surround the above query with a .Take(1) expression for performance. - } - - - FieldInfo entityRefField = entity.GetType().GetField(associationInfo.Storage, BindingFlags.NonPublic | BindingFlags.Instance); - object entityRefValue = null; - if (query != null) - entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query); - else - entityRefValue = Activator.CreateInstance(entityRefField.FieldType); - entityRefField.SetValue(entity, entityRefValue); - } - } - - /// - /// 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. - /// Here we set the query source of each EntitySetProperty - /// - /// - private void SetEntitySetsQueries(object entity) - { - IList properties = DataMapper.GetEntitySetAssociations(entity.GetType()); - - if (properties.Any()) { - IList thisPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(entity.GetType())); - - if (thisPKs.Count > 1) - throw new NotSupportedException("Multiple keys object not supported yet."); - - object primaryKeyValue = (thisPKs.First() as PropertyInfo).GetValue(entity, null); - - - foreach (PropertyInfo prop in properties) - { - //example of entitySet: Employee.EmployeeTerritories - var associationInfo = prop.GetAttribute(); - Type otherTableType = prop.PropertyType.GetGenericArguments().First(); - - //other table:EmployeeTerritories - var otherTable = GetTable(otherTableType); - //other table member:EmployeeTerritories.EmployeeID - var otherTableMember = otherTableType.GetProperty(associationInfo.OtherKey); - - - ParameterExpression p = Expression.Parameter(otherTableType, "other"); - Expression predicate; - if (!(otherTableMember.PropertyType.IsNullable())) - { - predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember), - Expression.Constant(primaryKeyValue)); - } - else - { - var ValueProperty = otherTableMember.PropertyType.GetProperty("Value"); - predicate = Expression.Equal(Expression.MakeMemberAccess( - Expression.MakeMemberAccess(p, otherTableMember), - ValueProperty), - Expression.Constant(primaryKeyValue)); - } - - var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable); - - var entitySetValue = prop.GetValue(entity, null); - - if (entitySetValue == null) - { - entitySetValue = Activator.CreateInstance(prop.PropertyType); - prop.SetValue(entity, entitySetValue, null); - } - - var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource"); - setSourceMethod.Invoke(entitySetValue, new[] { query }); - //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")) - } - } - } - - private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable) - { - //predicate: other.EmployeeID== "WARTH" - Expression lambdaPredicate = Expression.Lambda(predicate, parameter); - //lambdaPredicate: other=>other.EmployeeID== "WARTH" - - var whereMethod = typeof(Queryable) - .GetMethods().First(m => m.Name == "Where") - .MakeGenericMethod(otherTableType); - - - Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate); - //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH") - - return otherTable.Provider.CreateQuery(call); - } - - #endregion - - #region Insert/Update/Delete management - - /// - /// Registers an entity for insert - /// - /// - internal void RegisterInsert(object entity) - { - entityTracker.RegisterToInsert(entity); - } - - /// - /// Registers an entity for update - /// The entity will be updated only if some of its members have changed after the registration - /// - /// - internal void RegisterUpdate(object entity) - { - var identityReader = _GetIdentityReader(entity.GetType()); - var identityKey = identityReader.GetIdentityKey(entity); - // if we have no key, we can not watch - if (identityKey == null) - return; - // register entity - entityTracker.RegisterToWatch(entity, identityKey); - } - - /// - /// Registers or re-registers an entity and clears its state - /// - /// - /// - internal object Register(object entity) - { - var registeredEntity = _GetOrRegisterEntity(entity); - // the fact of registering again clears the modified state, so we're... clear with that - MemberModificationHandler.Register(registeredEntity, Mapping); - return registeredEntity; - } - - /// - /// Registers an entity for update - /// The entity will be updated only if some of its members have changed after the registration - /// - /// - /// - internal void RegisterUpdate(object entity, object entityOriginalState) - { - RegisterUpdate(entity); - MemberModificationHandler.Register(entity, entityOriginalState, Mapping); - } - - /// - /// Clears the current state, and marks the object as clean - /// - /// - internal void RegisterUpdateAgain(object entity) - { - MemberModificationHandler.ClearModified(entity, Mapping); - } - - /// - /// Registers an entity for delete - /// - /// - internal void RegisterDelete(object entity) - { - entityTracker.RegisterToDelete(entity); - } - - /// - /// Unregisters entity after deletion - /// - /// - internal void UnregisterDelete(object entity) - { - entityTracker.RegisterDeleted(entity); - } - - #endregion - - /// - /// Changed object determine - /// - /// Lists of inserted, updated, deleted objects - public ChangeSet GetChangeSet() - { - var inserts = new List(); - var updates = new List(); - var deletes = new List(); - foreach (var entityTrack in entityTracker.EnumerateAll()) - { - switch (entityTrack.EntityState) - { - case EntityState.ToInsert: - inserts.Add(entityTrack.Entity); - break; - case EntityState.ToWatch: - if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping)) - updates.Add(entityTrack.Entity); - break; - case EntityState.ToDelete: - deletes.Add(entityTrack.Entity); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - return new ChangeSet(inserts, updates, deletes); - } - - /// - /// use ExecuteCommand to call raw SQL - /// - public int ExecuteCommand(string command, params object[] parameters) - { - var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this)); - return QueryRunner.Execute(directQuery, parameters); - } - - /// - /// Execute raw SQL query and return object - /// - public IEnumerable ExecuteQuery(string query, params object[] parameters) where TResult : class, new() - { - //GetTable(); - foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters)) - yield return result; - } - - public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters) - { - var queryContext = new QueryContext(this); - var directQuery = QueryBuilder.GetDirectQuery(query, queryContext); - return QueryRunner.ExecuteSelect(elementType, directQuery, parameters); - } - - /// - /// Gets or sets the load options - /// - [DbLinqToDo] - public DataLoadOptions LoadOptions { get; set; } - - public DbTransaction Transaction { get; set; } - - /// - /// Runs the given reader and returns columns. - /// - /// The type of the result. - /// The reader. - /// - public IEnumerable Translate(DbDataReader reader) - { - foreach (TResult result in Translate(typeof(TResult), reader)) - yield return result; - } - - public IMultipleResults Translate(DbDataReader reader) - { - throw new NotImplementedException(); - } - - public IEnumerable Translate(Type elementType, DbDataReader reader) - { - return QueryRunner.EnumerateResult(elementType, reader, this); - } - - public void Dispose() - { - //connection closing should not be done here. - //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx - } - - [DbLinqToDo] - protected virtual void Dispose(bool disposing) - { - throw new NotImplementedException(); - } - - /// - /// Creates a IDbDataAdapter. Used internally by Vendors - /// - /// - internal IDbDataAdapter CreateDataAdapter() - { - return DatabaseContext.CreateDataAdapter(); - } - - /// - /// Sets a TextWriter where generated SQL commands are written - /// - public TextWriter Log { get; set; } - - /// - /// Writes text on Log (if not null) - /// Internal helper - /// - /// - internal void WriteLog(string text) - { - if (Log != null) - Log.WriteLine(text); - } - - /// - /// Write an IDbCommand to Log (if non null) - /// - /// - internal void WriteLog(IDbCommand command) - { - if (Log != null) - { - Log.WriteLine(command.CommandText); - foreach (IDbDataParameter parameter in command.Parameters) - WriteLog(parameter); - Log.Write("--"); - Log.Write(" Context: {0}", Vendor.VendorName); - Log.Write(" Model: {0}", Mapping.GetType().Name); - Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version); - Log.WriteLine(); - } - } - - /// - /// Writes and IDbDataParameter to Log (if non null) - /// - /// - internal void WriteLog(IDbDataParameter parameter) - { - if (Log != null) - { - // -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2] - // -- : (...) [] - Log.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]", - parameter.ParameterName, parameter.Direction, parameter.DbType, - parameter.Size, parameter.Precision, parameter.Scale, parameter.Value); - } - } - - - [DbLinqToDo] - public bool ObjectTrackingEnabled - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - [DbLinqToDo] - public int CommandTimeout - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - [DbLinqToDo] - public bool DeferredLoadingEnabled - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - [DbLinqToDo] - public ChangeConflictCollection ChangeConflicts - { - get { throw new NotImplementedException(); } - } - - [DbLinqToDo] - public DbCommand GetCommand(IQueryable query) - { - var qp = query.Provider as QueryProvider; - if (qp == null) - throw new InvalidOperationException(); - - IDbCommand dbCommand = qp.GetQuery(null).GetCommand().Command; - if (!(dbCommand is DbCommand)) - throw new InvalidOperationException(); - - return (DbCommand)dbCommand; - } - - [DbLinqToDo] - public void Refresh(RefreshMode mode, IEnumerable entities) - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - public void Refresh(RefreshMode mode, params object[] entities) - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - public void Refresh(RefreshMode mode, object entity) - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - public void DeleteDatabase() - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - public void CreateDatabase() - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - protected internal IQueryable CreateMethodCallQuery(object instance, MethodInfo methodInfo, params object[] parameters) - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - protected internal void ExecuteDynamicDelete(object entity) - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - protected internal void ExecuteDynamicInsert(object entity) - { - throw new NotImplementedException(); - } - - [DbLinqToDo] - protected internal void ExecuteDynamicUpdate(object entity) - { - throw new NotImplementedException(); - } - } -} +#region MIT license +// +// MIT license +// +// Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#endregion + +using System; +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Data.Linq.Mapping; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +#if MONO_STRICT +using System.Data.Linq.Implementation; +using System.Data.Linq.Sugar; +using System.Data.Linq.Identity; +using DbLinq.Util; +using AttributeMappingSource = System.Data.Linq.Mapping.AttributeMappingSource; +using MappingContext = System.Data.Linq.Mapping.MappingContext; +using DbLinq; +#else +using DbLinq.Data.Linq.Implementation; +using DbLinq.Data.Linq.Sugar; +using DbLinq.Data.Linq.Identity; +using DbLinq.Util; +using AttributeMappingSource = DbLinq.Data.Linq.Mapping.AttributeMappingSource; +using MappingContext = DbLinq.Data.Linq.Mapping.MappingContext; +using System.Data.Linq; +#endif + +using DbLinq.Factory; +using DbLinq.Vendor; +using DbLinq.Data.Linq.Database; +using DbLinq.Data.Linq.Database.Implementation; +using System.Linq.Expressions; +using System.Reflection.Emit; + +#if MONO_STRICT +namespace System.Data.Linq +#else +namespace DbLinq.Data.Linq +#endif +{ + public partial class DataContext : IDisposable + { + //private readonly Dictionary _tableMap = new Dictionary(); + private readonly Dictionary _tableMap = new Dictionary(); + + public MetaModel Mapping { get; private set; } + // PC question: at ctor, we get a IDbConnection and the Connection property exposes a DbConnection + // WTF? + public DbConnection Connection { get { return DatabaseContext.Connection as DbConnection; } } + + // all properties below are set public to optionally be injected + internal IVendor Vendor { get; set; } + internal IQueryBuilder QueryBuilder { get; set; } + internal IQueryRunner QueryRunner { get; set; } + internal IMemberModificationHandler MemberModificationHandler { get; set; } + internal IDatabaseContext DatabaseContext { get; private set; } + // /all properties... + + private readonly EntityTracker entityTracker = new EntityTracker(); + + private IIdentityReaderFactory identityReaderFactory; + private readonly IDictionary identityReaders = new Dictionary(); + + /// + /// The default behavior creates one MappingContext. + /// + [DBLinqExtended] + internal virtual MappingContext _MappingContext { get; set; } + + [DBLinqExtended] + internal IVendorProvider _VendorProvider { get; set; } + + public DataContext(IDbConnection connection, MappingSource mapping) + { + Init(new DatabaseContext(connection), mapping, null); + } + + public DataContext(IDbConnection connection) + { + Init(new DatabaseContext(connection), null, null); + } + + [DbLinqToDo] + public DataContext(string fileOrServerOrConnection, MappingSource mapping) + { + throw new NotImplementedException(); + } + + /// + /// Construct DataContext, given a connectionString. + /// To determine which DB type to go against, we look for 'DbLinqProvider=xxx' substring. + /// If not found, we assume that we are dealing with MS Sql Server. + /// + /// Valid values are names of provider DLLs (or any other DLL containing an IVendor implementation) + /// DbLinqProvider=Mysql + /// DbLinqProvider=Oracle etc. + /// + /// specifies file or server connection + [DbLinqToDo] + public DataContext(string connectionString) + { + #region DataContext connectionString ctor + if (connectionString == null) + throw new ArgumentNullException("connectionString"); + + System.Text.RegularExpressions.Regex reProvider + = new System.Text.RegularExpressions.Regex(@"DbLinqProvider=([\w\.]+)"); + + int startPos = connectionString.IndexOf("DbLinqProvider="); + string assemblyToLoad; + string vendorClassToLoad; + if (!reProvider.IsMatch(connectionString)) + { + assemblyToLoad = "DbLinq.SqlServer.dll"; + vendorClassToLoad = "SqlServerVendor"; + } + else + { + System.Text.RegularExpressions.Match match = reProvider.Match(connectionString); +#if MONO_STRICT + //Pascal says on the forum: + //[in MONO] "all vendors are (will be) embedded in the System.Data.Linq assembly" + assemblyToLoad = "System.Data.Linq.dll"; + vendorClassToLoad = match.Groups[1].Value; //eg. "MySql" +#else + //plain DbLinq - non MONO: + //IVendor classes are in DLLs such as "DbLinq.MySql.dll" + assemblyToLoad = match.Groups[1].Value; //eg. assemblyToLoad="DbLinq.MySql.dll" + if (assemblyToLoad.Contains(".")) + { + //already fully qualified DLL name? + throw new ArgumentException("Please provide a short name, such as 'MySql', not '" + assemblyToLoad + "'"); + } + else + { + //we were given short name, such as MySql + vendorClassToLoad = assemblyToLoad + "Vendor"; //eg. MySqlVendor + assemblyToLoad = "DbLinq." + assemblyToLoad + ".dll"; //eg. DbLinq.MySql.dll + } +#endif + //shorten: "DbLinqProvider=X;Server=Y" -> ";Server=Y" + string shortenedConnStr = reProvider.Replace(connectionString, ""); + connectionString = shortenedConnStr; + } + + Assembly assy; + try + { +#if MONO_STRICT + assy = typeof (DataContext).Assembly; // System.Data.Linq.dll +#else + //TODO: check if DLL is already loaded? + assy = Assembly.LoadFrom(assemblyToLoad); +#endif + } + catch (Exception ex) + { + //TODO: add proper logging here + Console.WriteLine("DataContext ctor: Assembly load failed for " + assemblyToLoad + ": " + ex); + throw ex; + } + + //find IDbProvider class in this assembly: + var ctors = (from mod in assy.GetModules() + from cls in mod.GetTypes() + where cls.GetInterfaces().Contains(typeof(IVendor)) + && cls.Name.ToLower() == vendorClassToLoad.ToLower() + let ctorInfo = cls.GetConstructor(Type.EmptyTypes) + where ctorInfo != null + select ctorInfo).ToList(); + if (ctors.Count == 0) + { + string msg = "Found no IVendor class in assembly " + assemblyToLoad + " having a string ctor"; + throw new ArgumentException(msg); + } + else if (ctors.Count > 1) + { + string msg = "Found more than one IVendor class in assembly " + assemblyToLoad + " having a string ctor"; + throw new ArgumentException(msg); + } + ConstructorInfo ctorInfo2 = ctors[0]; + + object ivendorObject; + try + { + ivendorObject = ctorInfo2.Invoke(new object[]{}); + } + catch (Exception ex) + { + //TODO: add proper logging here + Console.WriteLine("DataContext ctor: Failed to invoke IVendor ctor " + ctorInfo2.Name + ": " + ex); + throw ex; + } + IVendor ivendor = (IVendor)ivendorObject; + IDbConnection dbConnection = ivendor.CreateDbConnection(connectionString); + Init(new DatabaseContext(dbConnection), null, ivendor); + #endregion + } + + private void Init(IDatabaseContext databaseContext, MappingSource mappingSource, IVendor vendor) + { + if (databaseContext == null) + throw new ArgumentNullException("databaseContext"); + + _VendorProvider = ObjectFactory.Get(); + if (vendor == null) + Vendor = _VendorProvider.FindVendorByProviderType(typeof(SqlClient.Sql2005Provider)); + else + Vendor = vendor; + + DatabaseContext = databaseContext; + + MemberModificationHandler = ObjectFactory.Create(); // not a singleton: object is stateful + QueryBuilder = ObjectFactory.Get(); + QueryRunner = ObjectFactory.Get(); + + //EntityMap = ObjectFactory.Create(); + identityReaderFactory = ObjectFactory.Get(); + + _MappingContext = new MappingContext(); + + // initialize the mapping information + if (mappingSource == null) + mappingSource = new AttributeMappingSource(); + Mapping = mappingSource.GetModel(GetType()); + } + + /// + /// Checks if the table is allready mapped or maps it if not. + /// + /// Type of the table. + /// Thrown if the table is not mappable. + private void CheckTableMapping(Type tableType) + { + //This will throw an exception if the table is not found + if(Mapping.GetTable(tableType) == null) + { + throw new InvalidOperationException("The type '" + tableType.Name + "' is not mapped as a Table."); + } + } + + /// + /// Returns a Table for the type TEntity. + /// + /// If the type TEntity is not mappable as a Table. + /// The table type. + public Table GetTable() where TEntity : class + { + return (Table)GetTable(typeof(TEntity)); + } + + /// + /// Returns a Table for the given type. + /// + /// The table type. + /// If the type is not mappable as a Table. + public ITable GetTable(Type type) + { + lock (_tableMap) + { + ITable tableExisting; + if (_tableMap.TryGetValue(type, out tableExisting)) + return tableExisting; + + //Check for table mapping + CheckTableMapping(type); + + var tableNew = Activator.CreateInstance( + typeof(Table<>).MakeGenericType(type) + , BindingFlags.NonPublic | BindingFlags.Instance + , null + , new object[] { this } + , System.Globalization.CultureInfo.CurrentCulture) as ITable; + + _tableMap[type] = tableNew; + return tableNew; + } + } + + public void SubmitChanges() + { + SubmitChanges(ConflictMode.FailOnFirstConflict); + } + + /// + /// Pings database + /// + /// + public bool DatabaseExists() + { + try + { + return Vendor.Ping(this); + } + catch (Exception) + { + return false; + } + } + + /// + /// Commits all pending changes to database + /// + /// + public virtual void SubmitChanges(ConflictMode failureMode) + { + using (DatabaseContext.OpenConnection()) //ConnMgr will close connection for us + using (IDatabaseTransaction transaction = DatabaseContext.Transaction()) + { + var queryContext = new QueryContext(this); + var entityTracks = entityTracker.EnumerateAll().ToList(); + foreach (var entityTrack in entityTracks) + { + switch (entityTrack.EntityState) + { + case EntityState.ToInsert: + var insertQuery = QueryBuilder.GetInsertQuery(entityTrack.Entity, queryContext); + QueryRunner.Insert(entityTrack.Entity, insertQuery); + Register(entityTrack.Entity); + break; + case EntityState.ToWatch: + if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping)) + { + var modifiedMembers = MemberModificationHandler.GetModifiedProperties(entityTrack.Entity, Mapping); + var updateQuery = QueryBuilder.GetUpdateQuery(entityTrack.Entity, modifiedMembers, queryContext); + QueryRunner.Update(entityTrack.Entity, updateQuery, modifiedMembers); + + RegisterUpdateAgain(entityTrack.Entity); + } + break; + case EntityState.ToDelete: + var deleteQuery = QueryBuilder.GetDeleteQuery(entityTrack.Entity, queryContext); + QueryRunner.Delete(entityTrack.Entity, deleteQuery); + + UnregisterDelete(entityTrack.Entity); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + // TODO: handle conflicts (which can only occur when concurrency mode is implemented) + transaction.Commit(); + } + } + + /// + /// TODO - allow generated methods to call into stored procedures + /// + [DBLinqExtended] + internal IExecuteResult _ExecuteMethodCall(DataContext context, System.Reflection.MethodInfo method, params object[] sqlParams) + { + using (DatabaseContext.OpenConnection()) + { + System.Data.Linq.IExecuteResult result = Vendor.ExecuteMethodCall(context, method, sqlParams); + return result; + } + } + + [DbLinqToDo] + protected IExecuteResult ExecuteMethodCall(object instance, System.Reflection.MethodInfo methodInfo, params object[] parameters) + { + throw new NotImplementedException(); + } + + #region Identity management + + [DBLinqExtended] + internal IIdentityReader _GetIdentityReader(Type t) + { + IIdentityReader identityReader; + lock (identityReaders) + { + if (!identityReaders.TryGetValue(t, out identityReader)) + { + identityReader = identityReaderFactory.GetReader(t, this); + identityReaders[t] = identityReader; + } + } + return identityReader; + } + + [DBLinqExtended] + internal object _GetRegisteredEntity(object entity) + { + // TODO: check what is faster: by identity or by ref + var identityReader = _GetIdentityReader(entity.GetType()); + var identityKey = identityReader.GetIdentityKey(entity); + if (identityKey == null) // if we don't have an entitykey here, it means that the entity has no PK + return entity; + // even + var registeredEntityTrack = entityTracker.FindByIdentity(identityKey); + if (registeredEntityTrack != null) + return registeredEntityTrack.Entity; + return null; + } + + //internal object GetRegisteredEntityByKey(IdentityKey identityKey) + //{ + // return EntityMap[identityKey]; + //} + + /// + /// Registers an entity in a watch state + /// + /// + /// + [DBLinqExtended] + internal object _GetOrRegisterEntity(object entity) + { + var identityReader = _GetIdentityReader(entity.GetType()); + var identityKey = identityReader.GetIdentityKey(entity); + SetEntitySetsQueries(entity); + SetEntityRefQueries(entity); + + // if we have no identity, we can't track it + if (identityKey == null) + return entity; + + // try to find an already registered entity and return it + var registeredEntityTrack = entityTracker.FindByIdentity(identityKey); + if (registeredEntityTrack != null) + return registeredEntityTrack.Entity; + + // otherwise, register and return + entityTracker.RegisterToWatch(entity, identityKey); + return entity; + } + + readonly IDataMapper DataMapper = ObjectFactory.Get(); + private void SetEntityRefQueries(object entity) + { + Type thisType = entity.GetType(); + IList properties = DataMapper.GetEntityRefAssociations(thisType); + + + foreach (PropertyInfo prop in properties) + { + //example of entityRef:Order.Employee + AssociationAttribute associationInfo = prop.GetAttribute(); + Type otherTableType = prop.PropertyType; + IList otherPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(otherTableType)); + + if (otherPKs.Count > 1) + throw new NotSupportedException("Multiple keys object not supported yet."); + + var otherTable = GetTable(otherTableType); + + //ie:EmployeeTerritories.EmployeeID + + var thisForeignKeyProperty = thisType.GetProperty(associationInfo.ThisKey); + object thisForeignKeyValue = thisForeignKeyProperty.GetValue(entity, null); + + IEnumerable query = null; + if (thisForeignKeyValue != null) + { + ParameterExpression p = Expression.Parameter(otherTableType, "other"); + Expression predicate; + if (!(thisForeignKeyProperty.PropertyType.IsNullable())) + { + predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()), + Expression.Constant(thisForeignKeyValue)); + } + else + { + var ValueProperty = thisForeignKeyProperty.PropertyType.GetProperty("Value"); + predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherPKs.First()), + Expression.Constant(ValueProperty.GetValue(thisForeignKeyValue, null))); + } + + query = GetOtherTableQuery(predicate, p, otherTableType, otherTable) as IEnumerable; + //it would be interesting surround the above query with a .Take(1) expression for performance. + } + + + FieldInfo entityRefField = entity.GetType().GetField(associationInfo.Storage, BindingFlags.NonPublic | BindingFlags.Instance); + object entityRefValue = null; + if (query != null) + entityRefValue = Activator.CreateInstance(entityRefField.FieldType, query); + else + entityRefValue = Activator.CreateInstance(entityRefField.FieldType); + entityRefField.SetValue(entity, entityRefValue); + } + } + + /// + /// 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. + /// Here we set the query source of each EntitySetProperty + /// + /// + private void SetEntitySetsQueries(object entity) + { + IList properties = DataMapper.GetEntitySetAssociations(entity.GetType()); + + if (properties.Any()) { + IList thisPKs = DataMapper.GetPrimaryKeys(Mapping.GetTable(entity.GetType())); + + if (thisPKs.Count > 1) + throw new NotSupportedException("Multiple keys object not supported yet."); + + object primaryKeyValue = (thisPKs.First() as PropertyInfo).GetValue(entity, null); + + + foreach (PropertyInfo prop in properties) + { + //example of entitySet: Employee.EmployeeTerritories + var associationInfo = prop.GetAttribute(); + Type otherTableType = prop.PropertyType.GetGenericArguments().First(); + + //other table:EmployeeTerritories + var otherTable = GetTable(otherTableType); + //other table member:EmployeeTerritories.EmployeeID + var otherTableMember = otherTableType.GetProperty(associationInfo.OtherKey); + + + ParameterExpression p = Expression.Parameter(otherTableType, "other"); + Expression predicate; + if (!(otherTableMember.PropertyType.IsNullable())) + { + predicate = Expression.Equal(Expression.MakeMemberAccess(p, otherTableMember), + Expression.Constant(primaryKeyValue)); + } + else + { + var ValueProperty = otherTableMember.PropertyType.GetProperty("Value"); + predicate = Expression.Equal(Expression.MakeMemberAccess( + Expression.MakeMemberAccess(p, otherTableMember), + ValueProperty), + Expression.Constant(primaryKeyValue)); + } + + var query = GetOtherTableQuery(predicate, p, otherTableType, otherTable); + + var entitySetValue = prop.GetValue(entity, null); + + if (entitySetValue == null) + { + entitySetValue = Activator.CreateInstance(prop.PropertyType); + prop.SetValue(entity, entitySetValue, null); + } + + var setSourceMethod = entitySetValue.GetType().GetMethod("SetSource"); + setSourceMethod.Invoke(entitySetValue, new[] { query }); + //employee.EmployeeTerritories.SetSource(Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH")) + } + } + } + + private object GetOtherTableQuery(Expression predicate, ParameterExpression parameter, Type otherTableType, IQueryable otherTable) + { + //predicate: other.EmployeeID== "WARTH" + Expression lambdaPredicate = Expression.Lambda(predicate, parameter); + //lambdaPredicate: other=>other.EmployeeID== "WARTH" + + var whereMethod = typeof(Queryable) + .GetMethods().First(m => m.Name == "Where") + .MakeGenericMethod(otherTableType); + + + Expression call = Expression.Call(whereMethod, otherTable.Expression, lambdaPredicate); + //Table[EmployeesTerritories].Where(other=>other.employeeID="WARTH") + + return otherTable.Provider.CreateQuery(call); + } + + #endregion + + #region Insert/Update/Delete management + + /// + /// Registers an entity for insert + /// + /// + internal void RegisterInsert(object entity) + { + entityTracker.RegisterToInsert(entity); + } + + /// + /// Registers an entity for update + /// The entity will be updated only if some of its members have changed after the registration + /// + /// + internal void RegisterUpdate(object entity) + { + var identityReader = _GetIdentityReader(entity.GetType()); + var identityKey = identityReader.GetIdentityKey(entity); + // if we have no key, we can not watch + if (identityKey == null) + return; + // register entity + entityTracker.RegisterToWatch(entity, identityKey); + } + + /// + /// Registers or re-registers an entity and clears its state + /// + /// + /// + internal object Register(object entity) + { + var registeredEntity = _GetOrRegisterEntity(entity); + // the fact of registering again clears the modified state, so we're... clear with that + MemberModificationHandler.Register(registeredEntity, Mapping); + return registeredEntity; + } + + /// + /// Registers an entity for update + /// The entity will be updated only if some of its members have changed after the registration + /// + /// + /// + internal void RegisterUpdate(object entity, object entityOriginalState) + { + RegisterUpdate(entity); + MemberModificationHandler.Register(entity, entityOriginalState, Mapping); + } + + /// + /// Clears the current state, and marks the object as clean + /// + /// + internal void RegisterUpdateAgain(object entity) + { + MemberModificationHandler.ClearModified(entity, Mapping); + } + + /// + /// Registers an entity for delete + /// + /// + internal void RegisterDelete(object entity) + { + entityTracker.RegisterToDelete(entity); + } + + /// + /// Unregisters entity after deletion + /// + /// + internal void UnregisterDelete(object entity) + { + entityTracker.RegisterDeleted(entity); + } + + #endregion + + /// + /// Changed object determine + /// + /// Lists of inserted, updated, deleted objects + public ChangeSet GetChangeSet() + { + var inserts = new List(); + var updates = new List(); + var deletes = new List(); + foreach (var entityTrack in entityTracker.EnumerateAll()) + { + switch (entityTrack.EntityState) + { + case EntityState.ToInsert: + inserts.Add(entityTrack.Entity); + break; + case EntityState.ToWatch: + if (MemberModificationHandler.IsModified(entityTrack.Entity, Mapping)) + updates.Add(entityTrack.Entity); + break; + case EntityState.ToDelete: + deletes.Add(entityTrack.Entity); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + return new ChangeSet(inserts, updates, deletes); + } + + /// + /// use ExecuteCommand to call raw SQL + /// + public int ExecuteCommand(string command, params object[] parameters) + { + var directQuery = QueryBuilder.GetDirectQuery(command, new QueryContext(this)); + return QueryRunner.Execute(directQuery, parameters); + } + + /// + /// Execute raw SQL query and return object + /// + public IEnumerable ExecuteQuery(string query, params object[] parameters) where TResult : class, new() + { + //GetTable(); + foreach (TResult result in ExecuteQuery(typeof(TResult), query, parameters)) + yield return result; + } + + public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters) + { + var queryContext = new QueryContext(this); + var directQuery = QueryBuilder.GetDirectQuery(query, queryContext); + return QueryRunner.ExecuteSelect(elementType, directQuery, parameters); + } + + /// + /// Gets or sets the load options + /// + [DbLinqToDo] + public DataLoadOptions LoadOptions { get; set; } + + public DbTransaction Transaction { get; set; } + + /// + /// Runs the given reader and returns columns. + /// + /// The type of the result. + /// The reader. + /// + public IEnumerable Translate(DbDataReader reader) + { + foreach (TResult result in Translate(typeof(TResult), reader)) + yield return result; + } + + public IMultipleResults Translate(DbDataReader reader) + { + throw new NotImplementedException(); + } + + public IEnumerable Translate(Type elementType, DbDataReader reader) + { + return QueryRunner.EnumerateResult(elementType, reader, this); + } + + public void Dispose() + { + //connection closing should not be done here. + //read: http://msdn2.microsoft.com/en-us/library/bb292288.aspx + } + + [DbLinqToDo] + protected virtual void Dispose(bool disposing) + { + throw new NotImplementedException(); + } + + /// + /// Creates a IDbDataAdapter. Used internally by Vendors + /// + /// + internal IDbDataAdapter CreateDataAdapter() + { + return DatabaseContext.CreateDataAdapter(); + } + + /// + /// Sets a TextWriter where generated SQL commands are written + /// + public TextWriter Log { get; set; } + + /// + /// Writes text on Log (if not null) + /// Internal helper + /// + /// + internal void WriteLog(string text) + { + if (Log != null) + Log.WriteLine(text); + } + + /// + /// Write an IDbCommand to Log (if non null) + /// + /// + internal void WriteLog(IDbCommand command) + { + if (Log != null) + { + Log.WriteLine(command.CommandText); + foreach (IDbDataParameter parameter in command.Parameters) + WriteLog(parameter); + Log.Write("--"); + Log.Write(" Context: {0}", Vendor.VendorName); + Log.Write(" Model: {0}", Mapping.GetType().Name); + Log.Write(" Build: {0}", Assembly.GetExecutingAssembly().GetName().Version); + Log.WriteLine(); + } + } + + /// + /// Writes and IDbDataParameter to Log (if non null) + /// + /// + internal void WriteLog(IDbDataParameter parameter) + { + if (Log != null) + { + // -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2] + // -- : (...) [] + Log.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]", + parameter.ParameterName, parameter.Direction, parameter.DbType, + parameter.Size, parameter.Precision, parameter.Scale, parameter.Value); + } + } + + + [DbLinqToDo] + public bool ObjectTrackingEnabled + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + [DbLinqToDo] + public int CommandTimeout + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + [DbLinqToDo] + public bool DeferredLoadingEnabled + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + [DbLinqToDo] + public ChangeConflictCollection ChangeConflicts + { + get { throw new NotImplementedException(); } + } + + [DbLinqToDo] + public DbCommand GetCommand(IQueryable query) + { + var qp = query.Provider as QueryProvider; + if (qp == null) + throw new InvalidOperationException(); + + IDbCommand dbCommand = qp.GetQuery(null).GetCommand().Command; + if (!(dbCommand is DbCommand)) + throw new InvalidOperationException(); + + return (DbCommand)dbCommand; + } + + [DbLinqToDo] + public void Refresh(RefreshMode mode, IEnumerable entities) + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + public void Refresh(RefreshMode mode, params object[] entities) + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + public void Refresh(RefreshMode mode, object entity) + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + public void DeleteDatabase() + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + public void CreateDatabase() + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + protected internal IQueryable CreateMethodCallQuery(object instance, MethodInfo methodInfo, params object[] parameters) + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + protected internal void ExecuteDynamicDelete(object entity) + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + protected internal void ExecuteDynamicInsert(object entity) + { + throw new NotImplementedException(); + } + + [DbLinqToDo] + protected internal void ExecuteDynamicUpdate(object entity) + { + throw new NotImplementedException(); + } + } +}