diff --git a/src/Dapper.Contrib.BulkInsert/Dapper.Contrib.BulkInsert.csproj b/src/Dapper.Contrib.BulkInsert/Dapper.Contrib.BulkInsert.csproj index 2b646ea..8d33190 100644 --- a/src/Dapper.Contrib.BulkInsert/Dapper.Contrib.BulkInsert.csproj +++ b/src/Dapper.Contrib.BulkInsert/Dapper.Contrib.BulkInsert.csproj @@ -8,7 +8,7 @@ https://github.com/YahuiWong/Dapper.Contrib.BulkInsert.git git clickhouse,dapper,bulk,mysql,sqlserver - 0.1.5 + 0.1.7 true https://github.com/YahuiWong/Dapper.Contrib.BulkInsert/blob/master/LICENSE Dapper.Contrib.BulkInsert diff --git a/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.Async.cs b/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.Async.cs index 31ff3b5..69e5f18 100644 --- a/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.Async.cs +++ b/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.Async.cs @@ -27,7 +27,7 @@ public static async Task InsertBulkAsync(this IDbConnection connection, IEnum var wasClosed = connection.State == ConnectionState.Closed; if (wasClosed) connection.Open(); - await connection.ExecuteAsync(cmd.Item1,cmd.Item2, null, commandTimeout); + await connection.ExecuteAsync(cmd.Item1,cmd.Item2, null, commandTimeout, CommandType.Text); if (wasClosed) connection.Close(); } /// @@ -127,8 +127,19 @@ public static async Task InsertBulkAsync(this ClickHouse.Client.ADO.ClickHous if (wasClosed) connection.Close(); + } + /// + /// Inserts an entity into table "Ts" and returns identity id or number of inserted rows if inserting a list. + /// + /// Open SqlConnection + /// Entity to insert, can be list of entities + /// Identity of inserted entity, or number of inserted rows if inserting a list + public static async Task InsertBulkAsync(this ClickHouse.Ado.ClickHouseConnection connection, IEnumerable entityToInsert, int? commandTimeout = null) + { + await Task.Run(() => InsertBulk(connection, entityToInsert, commandTimeout) + ); + } - } diff --git a/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.cs b/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.cs index bd1f2e8..5e99067 100644 --- a/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.cs +++ b/src/Dapper.Contrib.BulkInsert/SqlMapperExtensions.cs @@ -323,6 +323,18 @@ private static (string, DynamicParameters) GenerateBulkSql(IDbConnection conn return (cmd,parameters); } + + /// + /// Inserts an entity into table "Ts" and returns identity id or number of inserted rows if inserting a list. + /// + /// Open SqlConnection + /// Entity to insert, can be list of entities + /// Identity of inserted entity, or number of inserted rows if inserting a list + public static void InsertBulk(this ClickHouse.Client.ADO.ClickHouseConnection connection, IEnumerable entityToInsert, int? commandTimeout = null) + { + InsertBulkAsync(connection, entityToInsert, commandTimeout).GetAwaiter().GetResult(); + } + /// /// Inserts an entity into table "Ts" and returns identity id or number of inserted rows if inserting a list. /// @@ -339,17 +351,133 @@ public static void InsertBulk(this IDbConnection connection, IEnumerable e connection.Execute(cmd.Item1, cmd.Item2, null, commandTimeout); if (wasClosed) connection.Close(); } + private static (string, List) GenerateCHBulkSql(IDbConnection connection, IEnumerable entityToInsert) + { + var type = entityToInsert.GetType(); + var typeInfo = type.GetTypeInfo(); + bool implementsGenericIEnumerableOrIsGenericIEnumerable = + typeInfo.ImplementedInterfaces.Any(ti => + ti.IsGenericType() && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>)) || + typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>); + + if (implementsGenericIEnumerableOrIsGenericIEnumerable) + { + type = type.GetGenericArguments()[0]; + } + + var name = GetTableName(type); + var sbColumnList = new StringBuilder(null); + var allProperties = TypePropertiesCache(type); + var keyProperties = KeyPropertiesCache(type); + var computedProperties = ComputedPropertiesCache(type); + var allPropertiesExceptKeyAndComputed = + allProperties.Except(keyProperties.Union(computedProperties)).ToList(); + + for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count; i++) + { + var property = allPropertiesExceptKeyAndComputed[i]; + sbColumnList.AppendFormat("{0}", GetColumnName(property)); + if (i < allPropertiesExceptKeyAndComputed.Count - 1) + sbColumnList.Append(", "); + } + + //var sbParameterList = new StringBuilder(null); + List dynamics = new List(); + + for (int j = 0, length = Enumerable.Count(entityToInsert); j < length; j++) + { + var item = Enumerable.ElementAt(entityToInsert, j); + { + List dynamicsParams = new List(); + //sbParameterList.Append("("); + for (int i = 0,count= allPropertiesExceptKeyAndComputed.Count; i < count; i++) + { + + var property = allPropertiesExceptKeyAndComputed[i]; + + var val = property.GetValue(item); + //var columnName = $"@{GetColumnName(property)}{j}{i}"; + //sbParameterList.Append(columnName); + if (property.PropertyType.IsValueType) + { + if (property.PropertyType == typeof(DateTime)) + { + var datetimevalue = Convert.ToDateTime(val); + if (property.GetCustomAttribute() != null) + { + //parameters.Add(columnName, datetimevalue.Date, DbType.Date); + dynamicsParams.Add(datetimevalue.Date); + //sbParameterList.AppendFormat("'{0:yyyy-MM-dd}'", val); + } + else + { + dynamicsParams.Add(datetimevalue); + //parameters.Add(columnName, datetimevalue, DbType.DateTime); + //sbParameterList.AppendFormat("'{0:yyyy-MM-dd HH:mm:ss}'", val); + } + } + else if (property.PropertyType == typeof(Boolean)) + { + var boolvalue = Convert.ToBoolean(val); + + { + //parameters.Add(columnName, boolvalue ? 1 : 0, DbType.Int32); + dynamicsParams.Add(boolvalue); + //sbParameterList.AppendFormat("'{0:yyyy-MM-dd HH:mm:ss}'", val); + } + } + else + { + dynamicsParams.Add(val); + //sbParameterList.AppendFormat("{0}", FixValue(val)); + } + } + else + { + dynamicsParams.Add(val); + ///sbParameterList.AppendFormat("'{0}'", FixValue(val)); + } + + //if (i < allPropertiesExceptKeyAndComputed.Count - 1) + // sbParameterList.Append(", "); + + } + dynamics.Add(dynamicsParams.ToArray()); + //sbParameterList.Append("),"); + } + } + + + //sbParameterList.Remove(sbParameterList.Length - 1, 1); + //sbParameterList.Append(";"); + //insert list of entities + var cmd = $"insert into {name} ({sbColumnList}) values @bulk; "; + return (cmd, dynamics); + } /// /// Inserts an entity into table "Ts" and returns identity id or number of inserted rows if inserting a list. /// /// Open SqlConnection /// Entity to insert, can be list of entities /// Identity of inserted entity, or number of inserted rows if inserting a list - public static void InsertBulk(this ClickHouse.Client.ADO.ClickHouseConnection connection, IEnumerable entityToInsert, int? commandTimeout = null) + public static void InsertBulk(this ClickHouse.Ado.ClickHouseConnection connection, IEnumerable entityToInsert, int? commandTimeout = null) { - InsertBulkAsync(connection, entityToInsert, commandTimeout).GetAwaiter().GetResult(); - } + var cmd = GenerateCHBulkSql(connection, entityToInsert); + + var wasClosed = connection.State == ConnectionState.Closed; + if (wasClosed) connection.Open(); + + var command = connection.CreateCommand(); + command.CommandText = cmd.Item1; + command.Parameters.Add(new ClickHouse.Ado.ClickHouseParameter + { + ParameterName = "bulk", + Value = cmd.Item2 + }); + command.ExecuteNonQuery(); + if (wasClosed) connection.Close(); + } /// /// Specifies a custom callback that detects the database type instead of relying on the default strategy (the name of the connection type object).