diff --git a/DynamORM.Tests/DynamORM.Tests.csproj b/DynamORM.Tests/DynamORM.Tests.csproj index b34fc80..2772e40 100644 --- a/DynamORM.Tests/DynamORM.Tests.csproj +++ b/DynamORM.Tests/DynamORM.Tests.csproj @@ -77,7 +77,6 @@ - diff --git a/DynamORM.Tests/Modify/DynamicTypeSchemaModificationTests.cs b/DynamORM.Tests/Modify/DynamicTypeSchemaModificationTests.cs index 6e1ef5f..f29d559 100644 --- a/DynamORM.Tests/Modify/DynamicTypeSchemaModificationTests.cs +++ b/DynamORM.Tests/Modify/DynamicTypeSchemaModificationTests.cs @@ -42,25 +42,28 @@ namespace DynamORM.Tests.Modify { return Database.Table(); } - + + /// + /// Tests the bulk insert. + /// [Test] public void TestBulkInsert() { - Assert.AreEqual(2, Database.Insert(new List - { + Assert.AreEqual(2, Database.Insert(new List + { new users { - id = 1001, - login = "a", + id = 1001, + login = "a", }, new users { - id = 1002, - login = "b", + id = 1002, + login = "b", } - })); - - Assert.AreEqual(2, Database.Delete().Where(u => u.users.id.In(1001, 1002)).Execute()); + })); + + Assert.AreEqual(2, Database.Delete().Where(u => u.users.id.In(1001, 1002)).Execute()); } } } \ No newline at end of file diff --git a/DynamORM/DynamicDatabase.cs b/DynamORM/DynamicDatabase.cs index c26ed97..fa3157a 100644 --- a/DynamORM/DynamicDatabase.cs +++ b/DynamORM/DynamicDatabase.cs @@ -286,59 +286,106 @@ namespace DynamORM { return new DynamicInsertQueryBuilder(this).Table(typeof(T)); } - + + /// Bulk insert objects into database. + /// Type of objects to insert. + /// Enumerable containing instances of objects to insert. + /// Number of inserted rows. public virtual int Insert(IEnumerable e) where T : class { - int affected = 0; - var mapper = DynamicMapperCache.GetMapper(typeof(T)); - - if (mapper != null) - using (var con = Open()) - using (var tra = con.BeginTransaction()) - using (var cmd = con.CreateCommand()) - { - try - { - DynamicPropertyInvoker currentprop = null; - var temp = new Dictionary(); - var parameters = new Dictionary(); - - var ib = Insert() - .SetVirtualMode(true) - .CreateTemporaryParameterAction(p => temp[p.Name] = currentprop) - .CreateParameterAction((p, cp) => parameters[cp] = temp[p.Name]); - - foreach (var prop in mapper.PropertyMap) - if (!mapper.Ignored.Contains(prop.Key)) - { - var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key; - currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower()); - - if (currentprop.Get != null) - ib.Insert(col, null); - } - - ib.FillCommand(cmd); - - foreach (var o in e) - { - foreach (var m in parameters) - m.Key.Value = m.Value.Get(o); - - affected += cmd.ExecuteNonQuery(); - } - - tra.Commit(); - } - catch - { - if (tra != null) - tra.Rollback(); - - throw; - } - } - + int affected = 0; + var mapper = DynamicMapperCache.GetMapper(typeof(T)); + + if (mapper != null) + { + using (var con = Open()) + using (var tra = con.BeginTransaction()) + using (var cmd = con.CreateCommand()) + { + try + { + var parameters = new Dictionary(); + + if (!string.IsNullOrEmpty(mapper.InsertCommandText)) + { + cmd.CommandText = mapper.InsertCommandText; + + foreach (var col in mapper.ColumnsMap.Values + .Where(di => !di.Ignore && di.InsertCommandParameter != null) + .OrderBy(di => di.InsertCommandParameter.Ordinal)) + { + var para = cmd.CreateParameter(); + para.ParameterName = col.InsertCommandParameter.Name; + para.DbType = col.InsertCommandParameter.Type; + cmd.Parameters.Add(para); + + parameters[para] = col; + } + } + else + { + DynamicPropertyInvoker currentprop = null; + var temp = new Dictionary(); + int ord = 0; + + var ib = Insert() + .SetVirtualMode(true) + .CreateTemporaryParameterAction(p => temp[p.Name] = currentprop) + .CreateParameterAction((p, cp) => + { + parameters[cp] = temp[p.Name]; + parameters[cp].InsertCommandParameter = new DynamicPropertyInvoker.ParameterSpec + { + Name = cp.ParameterName, + Type = cp.DbType, + Ordinal = ord++, + }; + }); + + foreach (var prop in mapper.PropertyMap) + if (!mapper.Ignored.Contains(prop.Key)) + { + var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key; + currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower()); + + if (currentprop.Ignore) + continue; + + if (currentprop.Get != null) + ib.Insert(col, null); + } + + ib.FillCommand(cmd); + + // Cache command + mapper.InsertCommandText = cmd.CommandText; + } + + foreach (var o in e) + { + foreach (var m in parameters) + m.Key.Value = m.Value.Get(o); + + affected += cmd.ExecuteNonQuery(); + } + + tra.Commit(); + } + catch (Exception ex) + { + if (tra != null) + tra.Rollback(); + + affected = 0; + + var exCmd = new StringBuilder(); + cmd.Dump(exCmd); + + throw new InvalidOperationException(exCmd.ToString(), ex); + } + } + } + return affected; } @@ -366,6 +413,113 @@ namespace DynamORM return new DynamicUpdateQueryBuilder(this).Table(typeof(T)); } + /// Bulk update objects into database. + /// Type of objects to update. + /// Enumerable containing instances of objects to update. + /// Number of updated rows. + public virtual int Update(IEnumerable e) where T : class + { + int affected = 0; + var mapper = DynamicMapperCache.GetMapper(typeof(T)); + + if (mapper != null) + { + using (var con = Open()) + using (var tra = con.BeginTransaction()) + using (var cmd = con.CreateCommand()) + { + try + { + var parameters = new Dictionary(); + + if (!string.IsNullOrEmpty(mapper.UpdateCommandText)) + { + cmd.CommandText = mapper.UpdateCommandText; + + foreach (var col in mapper.ColumnsMap.Values + .Where(di => !di.Ignore && di.UpdateCommandParameter != null) + .OrderBy(di => di.UpdateCommandParameter.Ordinal)) + { + var para = cmd.CreateParameter(); + para.ParameterName = col.UpdateCommandParameter.Name; + para.DbType = col.UpdateCommandParameter.Type; + cmd.Parameters.Add(para); + + parameters[para] = col; + } + } + else + { + DynamicPropertyInvoker currentprop = null; + var temp = new Dictionary(); + int ord = 0; + + var ib = Update() + .SetVirtualMode(true) + .CreateTemporaryParameterAction(p => temp[p.Name] = currentprop) + .CreateParameterAction((p, cp) => + { + parameters[cp] = temp[p.Name]; + parameters[cp].UpdateCommandParameter = new DynamicPropertyInvoker.ParameterSpec + { + Name = cp.ParameterName, + Type = cp.DbType, + Ordinal = ord++, + }; + }); + + foreach (var prop in mapper.PropertyMap) + if (!mapper.Ignored.Contains(prop.Key)) + { + var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key; + currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower()); + + if (currentprop.Ignore) + continue; + + if (currentprop.Get != null) + { + if (currentprop.Column != null && currentprop.Column.IsKey) + ib.Where(col, null); + else + ib.Values(col, null); + } + } + + ib.FillCommand(cmd); + + // Cache command + mapper.UpdateCommandText = cmd.CommandText; + } + + foreach (var o in e) + { + foreach (var m in parameters) + m.Key.Value = m.Value.Get(o); + + affected += cmd.ExecuteNonQuery(); + } + + tra.Commit(); + } + catch (Exception ex) + { + if (tra != null) + tra.Rollback(); + + affected = 0; + + var exCmd = new StringBuilder(); + cmd.Dump(exCmd); + + throw new InvalidOperationException(exCmd.ToString(), ex); + } + } + } + + return affected; + } + /// /// Adds to the DELETE FROM clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: diff --git a/DynamORM/Mapper/DynamicPropertyInvoker.cs b/DynamORM/Mapper/DynamicPropertyInvoker.cs index 13fd773..ebeb599 100644 --- a/DynamORM/Mapper/DynamicPropertyInvoker.cs +++ b/DynamORM/Mapper/DynamicPropertyInvoker.cs @@ -27,6 +27,7 @@ */ using System; +using System.Data; using System.Linq.Expressions; using System.Reflection; @@ -35,6 +36,15 @@ namespace DynamORM.Mapper /// Dynamic property invoker. public class DynamicPropertyInvoker { + internal class ParameterSpec + { + public string Name { get; set; } + + public DbType Type { get; set; } + + public int Ordinal { get; set; } + } + /// Gets the type of property. public Type Type { get; private set; } @@ -105,5 +115,13 @@ namespace DynamORM.Mapper Expression.Convert(valueParm, property.PropertyType)), objParm, valueParm).Compile(); } + + #region Type command cache + + internal ParameterSpec InsertCommandParameter { get; set; } + + internal ParameterSpec UpdateCommandParameter { get; set; } + + #endregion Type command cache } } \ No newline at end of file diff --git a/DynamORM/Mapper/DynamicTypeMap.cs b/DynamORM/Mapper/DynamicTypeMap.cs index b3b2eb9..f6ade3f 100644 --- a/DynamORM/Mapper/DynamicTypeMap.cs +++ b/DynamORM/Mapper/DynamicTypeMap.cs @@ -28,7 +28,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; namespace DynamORM.Mapper @@ -92,9 +91,9 @@ namespace DynamORM.Mapper columnMap.Add(col.ToLower(), val); propertyMap.Add(pi.Name, col); - + if (val.Ignore) - ignored.Add(pi.Name); + ignored.Add(pi.Name); } ColumnsMap = columnMap; @@ -136,5 +135,13 @@ namespace DynamORM.Mapper return destination; } + + #region Type command cache + + internal string InsertCommandText { get; set; } + + internal string UpdateCommandText { get; set; } + + #endregion Type command cache } } \ No newline at end of file