diff --git a/AmalgamationTool/DynamORM.Amalgamation.cs b/AmalgamationTool/DynamORM.Amalgamation.cs index ec9e88b..b1f55a9 100644 --- a/AmalgamationTool/DynamORM.Amalgamation.cs +++ b/AmalgamationTool/DynamORM.Amalgamation.cs @@ -805,11 +805,12 @@ namespace DynamORM /// Begins a database transaction. /// One of the values. + /// Custom parameter describing transaction options. /// This action is invoked when transaction is disposed. /// Returns representation. - internal DynamicTransaction BeginTransaction(IsolationLevel? il, Action disposed) + internal DynamicTransaction BeginTransaction(IsolationLevel? il, object custom, Action disposed) { - return new DynamicTransaction(_db, this, _singleTransaction, il, disposed); + return new DynamicTransaction(_db, this, _singleTransaction, il, disposed, null); } #region IDbConnection Members @@ -825,7 +826,7 @@ namespace DynamORM /// Returns representation. public IDbTransaction BeginTransaction() { - return BeginTransaction(null, null); + return BeginTransaction(null, null, null); } /// Begins a database transaction with the specified @@ -834,7 +835,16 @@ namespace DynamORM /// Returns representation. public IDbTransaction BeginTransaction(IsolationLevel il) { - return BeginTransaction(il, null); + return BeginTransaction(il, null, null); + } + + /// Begins a database transaction with the specified + /// value. + /// Custom parameter describing transaction options. + /// Returns representation. + public IDbTransaction BeginTransaction(object custom) + { + return BeginTransaction(null, custom, null); } /// Changes the current database for an open Connection object. @@ -1733,6 +1743,31 @@ namespace DynamORM #region Schema + /// Builds query cache if necessary and returns it. + /// The builder containing query to read schema from. + /// Query schema. + public Dictionary GetQuerySchema(IDynamicSelectQueryBuilder builder) + { + using (var con = Open()) + using (var cmd = con.CreateCommand().SetCommand(builder)) + return ReadSchema(cmd) + .Distinct() + .ToDictionary(k => k.Name.ToLower(), k => k); + } + + /// Builds query cache if necessary and returns it. + /// Sql query from which read schema. + /// Sql query argumants. + /// Query schema. + public Dictionary GetQuerySchema(string sql, params object[] args) + { + using (var con = Open()) + using (var cmd = con.CreateCommand().SetCommand(sql, args)) + return ReadSchema(cmd) + .Distinct() + .ToDictionary(k => k.Name.ToLower(), k => k); + } + /// Builds table cache if necessary and returns it. /// Name of table for which build schema. /// Owner of table for which build schema. @@ -1790,29 +1825,35 @@ namespace DynamORM protected virtual IEnumerable ReadSchema(string table, string owner) { using (var con = Open()) - using (var cmd = con.CreateCommand()) - { - using (var rdr = cmd - .SetCommand(string.Format("SELECT * FROM {0}{1} WHERE 1 = 0", - !string.IsNullOrEmpty(owner) ? string.Format("{0}.", DecorateName(owner)) : string.Empty, - DecorateName(table))) - .ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo)) - foreach (DataRow col in rdr.GetSchemaTable().Rows) - { - var c = col.RowToDynamicUpper(); + using (var cmd = con.CreateCommand() + .SetCommand(string.Format("SELECT * FROM {0}{1} WHERE 1 = 0", + !string.IsNullOrEmpty(owner) ? string.Format("{0}.", DecorateName(owner)) : string.Empty, + DecorateName(table)))) + return ReadSchema(cmd); + } - yield return new DynamicSchemaColumn - { - Name = c.COLUMNNAME, - Type = DynamicExtensions.TypeMap.TryGetNullable((Type)c.DATATYPE) ?? DbType.String, - IsKey = c.ISKEY ?? false, - IsUnique = c.ISUNIQUE ?? false, - Size = (int)(c.COLUMNSIZE ?? 0), - Precision = (byte)(c.NUMERICPRECISION ?? 0), - Scale = (byte)(c.NUMERICSCALE ?? 0) - }; - } - } + /// Get schema describing objects from reader. + /// Command containing query to execute. + /// List of objects . + /// If your database doesn't get those values in upper case (like most of the databases) you should override this method. + protected virtual IEnumerable ReadSchema(IDbCommand cmd) + { + using (var rdr = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo)) + foreach (DataRow col in rdr.GetSchemaTable().Rows) + { + var c = col.RowToDynamicUpper(); + + yield return new DynamicSchemaColumn + { + Name = c.COLUMNNAME, + Type = DynamicExtensions.TypeMap.TryGetNullable((Type)c.DATATYPE) ?? DbType.String, + IsKey = c.ISKEY ?? false, + IsUnique = c.ISUNIQUE ?? false, + Size = (int)(c.COLUMNSIZE ?? 0), + Precision = (byte)(c.NUMERICPRECISION ?? 0), + Scale = (byte)(c.NUMERICSCALE ?? 0) + }; + } } private Dictionary BuildAndCacheSchema(string tableName, DynamicTypeMap mapper, string owner = null) @@ -1832,6 +1873,7 @@ namespace DynamORM if (databaseSchemaSupport && !Schema.ContainsKey(tableName.ToLower())) { schema = ReadSchema(tableName, owner) + .Distinct() .ToDictionary(k => k.Name.ToLower(), k => k); Schema[tableName.ToLower()] = schema; @@ -2110,7 +2152,47 @@ namespace DynamORM { _tempConn = Open() as DynamicConnection; - return _tempConn.BeginTransaction(null, () => + return _tempConn.BeginTransaction(null, null, () => + { + var t = TransactionPool.TryGetValue(_tempConn.Connection); + + if (t == null | t.Count == 0) + { + _tempConn.Dispose(); + _tempConn = null; + } + }); + } + + /// Begins a database transaction with the specified + /// value. + /// One of the values. + /// Returns representation. + public IDbTransaction BeginTransaction(IsolationLevel il) + { + _tempConn = Open() as DynamicConnection; + + return _tempConn.BeginTransaction(il, null, () => + { + var t = TransactionPool.TryGetValue(_tempConn.Connection); + + if (t == null | t.Count == 0) + { + _tempConn.Dispose(); + _tempConn = null; + } + }); + } + + /// Begins a database transaction with the specified + /// value. + /// Custom parameter describing transaction options. + /// Returns representation. + public IDbTransaction BeginTransaction(object custom) + { + _tempConn = Open() as DynamicConnection; + + return _tempConn.BeginTransaction(null, custom, () => { var t = TransactionPool.TryGetValue(_tempConn.Connection); @@ -4658,7 +4740,8 @@ namespace DynamORM /// Are we using single transaction mode? I so... act correctly. /// One of the values. /// This action is invoked when transaction is disposed. - internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed) + /// Pass custom transaction parameters. + internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed, object customParams) { _db = db; _con = con; @@ -4673,8 +4756,19 @@ namespace DynamORM _operational = false; else { - _db.TransactionPool[_con.Connection] - .Push(il.HasValue ? _con.Connection.BeginTransaction(il.Value) : _con.Connection.BeginTransaction()); + if (customParams != null) + { + var mi = _con.Connection.GetType().GetMethods().Where(m => m.GetParameters().Count() == 1 && m.GetParameters().First().ParameterType == customParams.GetType()).FirstOrDefault(); + if (mi != null) + _db.TransactionPool[_con.Connection].Push((IDbTransaction)mi.Invoke(_con.Connection, new object[] { customParams, })); + else + throw new MissingMethodException(string.Format("Method 'BeginTransaction' accepting parameter of type '{0}' in '{1}' not found.", + customParams.GetType().FullName, _con.Connection.GetType().FullName)); + } + else + _db.TransactionPool[_con.Connection] + .Push(il.HasValue ? _con.Connection.BeginTransaction(il.Value) : _con.Connection.BeginTransaction()); + _db.PoolStamp = DateTime.Now.Ticks; _operational = true; } diff --git a/DynamORM.Tests/DynamORM.Tests.csproj b/DynamORM.Tests/DynamORM.Tests.csproj index 2772e40..384ca3d 100644 --- a/DynamORM.Tests/DynamORM.Tests.csproj +++ b/DynamORM.Tests/DynamORM.Tests.csproj @@ -105,6 +105,7 @@ ResXFileCodeGenerator Resources.Designer.cs + Designer diff --git a/DynamORM/DynamicConnection.cs b/DynamORM/DynamicConnection.cs index 2e4cb0a..dd84cd0 100644 --- a/DynamORM/DynamicConnection.cs +++ b/DynamORM/DynamicConnection.cs @@ -57,11 +57,12 @@ namespace DynamORM /// Begins a database transaction. /// One of the values. + /// Custom parameter describing transaction options. /// This action is invoked when transaction is disposed. /// Returns representation. - internal DynamicTransaction BeginTransaction(IsolationLevel? il, Action disposed) + internal DynamicTransaction BeginTransaction(IsolationLevel? il, object custom, Action disposed) { - return new DynamicTransaction(_db, this, _singleTransaction, il, disposed); + return new DynamicTransaction(_db, this, _singleTransaction, il, disposed, null); } #region IDbConnection Members @@ -77,7 +78,7 @@ namespace DynamORM /// Returns representation. public IDbTransaction BeginTransaction() { - return BeginTransaction(null, null); + return BeginTransaction(null, null, null); } /// Begins a database transaction with the specified @@ -86,7 +87,16 @@ namespace DynamORM /// Returns representation. public IDbTransaction BeginTransaction(IsolationLevel il) { - return BeginTransaction(il, null); + return BeginTransaction(il, null, null); + } + + /// Begins a database transaction with the specified + /// value. + /// Custom parameter describing transaction options. + /// Returns representation. + public IDbTransaction BeginTransaction(object custom) + { + return BeginTransaction(null, custom, null); } /// Changes the current database for an open Connection object. diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index f31632c..9f54c2c 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -860,6 +860,31 @@ namespace DynamORM #region Schema + /// Builds query cache if necessary and returns it. + /// The builder containing query to read schema from. + /// Query schema. + public Dictionary GetQuerySchema(IDynamicSelectQueryBuilder builder) + { + using (var con = Open()) + using (var cmd = con.CreateCommand().SetCommand(builder)) + return ReadSchema(cmd) + .Distinct() + .ToDictionary(k => k.Name.ToLower(), k => k); + } + + /// Builds query cache if necessary and returns it. + /// Sql query from which read schema. + /// Sql query argumants. + /// Query schema. + public Dictionary GetQuerySchema(string sql, params object[] args) + { + using (var con = Open()) + using (var cmd = con.CreateCommand().SetCommand(sql, args)) + return ReadSchema(cmd) + .Distinct() + .ToDictionary(k => k.Name.ToLower(), k => k); + } + /// Builds table cache if necessary and returns it. /// Name of table for which build schema. /// Owner of table for which build schema. @@ -917,29 +942,35 @@ namespace DynamORM protected virtual IEnumerable ReadSchema(string table, string owner) { using (var con = Open()) - using (var cmd = con.CreateCommand()) - { - using (var rdr = cmd - .SetCommand(string.Format("SELECT * FROM {0}{1} WHERE 1 = 0", - !string.IsNullOrEmpty(owner) ? string.Format("{0}.", DecorateName(owner)) : string.Empty, - DecorateName(table))) - .ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo)) - foreach (DataRow col in rdr.GetSchemaTable().Rows) - { - var c = col.RowToDynamicUpper(); + using (var cmd = con.CreateCommand() + .SetCommand(string.Format("SELECT * FROM {0}{1} WHERE 1 = 0", + !string.IsNullOrEmpty(owner) ? string.Format("{0}.", DecorateName(owner)) : string.Empty, + DecorateName(table)))) + return ReadSchema(cmd); + } - yield return new DynamicSchemaColumn - { - Name = c.COLUMNNAME, - Type = DynamicExtensions.TypeMap.TryGetNullable((Type)c.DATATYPE) ?? DbType.String, - IsKey = c.ISKEY ?? false, - IsUnique = c.ISUNIQUE ?? false, - Size = (int)(c.COLUMNSIZE ?? 0), - Precision = (byte)(c.NUMERICPRECISION ?? 0), - Scale = (byte)(c.NUMERICSCALE ?? 0) - }; - } - } + /// Get schema describing objects from reader. + /// Command containing query to execute. + /// List of objects . + /// If your database doesn't get those values in upper case (like most of the databases) you should override this method. + protected virtual IEnumerable ReadSchema(IDbCommand cmd) + { + using (var rdr = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo)) + foreach (DataRow col in rdr.GetSchemaTable().Rows) + { + var c = col.RowToDynamicUpper(); + + yield return new DynamicSchemaColumn + { + Name = c.COLUMNNAME, + Type = DynamicExtensions.TypeMap.TryGetNullable((Type)c.DATATYPE) ?? DbType.String, + IsKey = c.ISKEY ?? false, + IsUnique = c.ISUNIQUE ?? false, + Size = (int)(c.COLUMNSIZE ?? 0), + Precision = (byte)(c.NUMERICPRECISION ?? 0), + Scale = (byte)(c.NUMERICSCALE ?? 0) + }; + } } private Dictionary BuildAndCacheSchema(string tableName, DynamicTypeMap mapper, string owner = null) @@ -959,6 +990,7 @@ namespace DynamORM if (databaseSchemaSupport && !Schema.ContainsKey(tableName.ToLower())) { schema = ReadSchema(tableName, owner) + .Distinct() .ToDictionary(k => k.Name.ToLower(), k => k); Schema[tableName.ToLower()] = schema; @@ -1237,7 +1269,47 @@ namespace DynamORM { _tempConn = Open() as DynamicConnection; - return _tempConn.BeginTransaction(null, () => + return _tempConn.BeginTransaction(null, null, () => + { + var t = TransactionPool.TryGetValue(_tempConn.Connection); + + if (t == null | t.Count == 0) + { + _tempConn.Dispose(); + _tempConn = null; + } + }); + } + + /// Begins a database transaction with the specified + /// value. + /// One of the values. + /// Returns representation. + public IDbTransaction BeginTransaction(IsolationLevel il) + { + _tempConn = Open() as DynamicConnection; + + return _tempConn.BeginTransaction(il, null, () => + { + var t = TransactionPool.TryGetValue(_tempConn.Connection); + + if (t == null | t.Count == 0) + { + _tempConn.Dispose(); + _tempConn = null; + } + }); + } + + /// Begins a database transaction with the specified + /// value. + /// Custom parameter describing transaction options. + /// Returns representation. + public IDbTransaction BeginTransaction(object custom) + { + _tempConn = Open() as DynamicConnection; + + return _tempConn.BeginTransaction(null, custom, () => { var t = TransactionPool.TryGetValue(_tempConn.Connection); diff --git a/DynamORM/DynamicTransaction.cs b/DynamORM/DynamicTransaction.cs index 75b947f..29aadae 100644 --- a/DynamORM/DynamicTransaction.cs +++ b/DynamORM/DynamicTransaction.cs @@ -28,6 +28,7 @@ using System; using System.Data; +using System.Linq; using DynamORM.Helpers; namespace DynamORM @@ -47,7 +48,8 @@ namespace DynamORM /// Are we using single transaction mode? I so... act correctly. /// One of the values. /// This action is invoked when transaction is disposed. - internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed) + /// Pass custom transaction parameters. + internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed, object customParams) { _db = db; _con = con; @@ -62,8 +64,19 @@ namespace DynamORM _operational = false; else { - _db.TransactionPool[_con.Connection] - .Push(il.HasValue ? _con.Connection.BeginTransaction(il.Value) : _con.Connection.BeginTransaction()); + if (customParams != null) + { + var mi = _con.Connection.GetType().GetMethods().Where(m => m.GetParameters().Count() == 1 && m.GetParameters().First().ParameterType == customParams.GetType()).FirstOrDefault(); + if (mi != null) + _db.TransactionPool[_con.Connection].Push((IDbTransaction)mi.Invoke(_con.Connection, new object[] { customParams, })); + else + throw new MissingMethodException(string.Format("Method 'BeginTransaction' accepting parameter of type '{0}' in '{1}' not found.", + customParams.GetType().FullName, _con.Connection.GetType().FullName)); + } + else + _db.TransactionPool[_con.Connection] + .Push(il.HasValue ? _con.Connection.BeginTransaction(il.Value) : _con.Connection.BeginTransaction()); + _db.PoolStamp = DateTime.Now.Ticks; _operational = true; }