using Dapper; using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Linq.Expressions; using System.Text; using Tiobon.Core.Common.DB.Dapper.Const; using Tiobon.Core.Common.DB.Dapper.DBManager; using Tiobon.Core.Common.DB.Dapper.Enums; using Tiobon.Core.Common.DB.Dapper.Extensions; using Tiobon.Core.Common.DB.Dapper.Utilities; using System.Threading.Tasks; namespace Tiobon.Core.Common.DB.Dapper; public class SqlDapper : ISqlDapper { private string _connectionString; private IDbConnection _connection { get; set; } public IDbConnection Connection { get { if (_connection == null || _connection.State == ConnectionState.Closed) { _connection = DBServerProvider.GetDbConnection(_connectionString); } return _connection; } } public SqlDapper() { _connectionString = DBServerProvider.GetConnectionString(); } /// /// string mySql = "Data Source=132.232.2.109;Database=mysql;User /// ID=root;Password=mysql;pooling=true;CharSet=utf8;port=3306;sslmode=none"; /// this.conn = new MySql.Data.MySqlClient.MySqlConnection(mySql); /// /// public SqlDapper(string connKeyName) { _connectionString = DBServerProvider.GetConnectionString(connKeyName); } /// /// var p = new object(); // p.Add("@a", 11); //p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output); //p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue); // /// /// /// /// /// /// public List QueryList(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) where T : class { return Execute((conn, dbTransaction) => { return conn.Query(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text).ToList(); }, beginTransaction); } public async Task> QueryListAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class { return await Execute(async (conn, dbTransaction) => { return (await conn.QueryAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text)).ToList(); }, beginTransaction); } public T QueryFirst(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false) where T : class { List list = QueryList(cmd, param, commandType: commandType ?? CommandType.Text, beginTransaction: beginTransaction).ToList(); return list.Count == 0 ? null : list[0]; } public async Task QueryFirstAsync(string cmd, object param = null, CommandType? commandType = null, bool beginTransaction = false, int? commandTimeout = null) where T : class { var data = await QueryListAsync(cmd, param, commandType, beginTransaction, commandTimeout); List list = data.ToList(); return !list.Any() ? null : list[0]; } public object ExecuteScalar(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { try { return Execute((conn, dbTransaction) => { return conn.ExecuteScalar(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, beginTransaction); } catch (Exception) { throw; } } public async Task ExecuteScalarAsync(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { try { return await Execute(async (conn, dbTransaction) => { return await conn.ExecuteScalarAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, beginTransaction); } catch (Exception) { throw; } } public int ExcuteNonQuery(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { return Execute((conn, dbTransaction) => { return conn.Execute(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, beginTransaction); } public IDataReader ExecuteReader(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { return Execute((conn, dbTransaction) => { return conn.ExecuteReader(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, beginTransaction, false); } public SqlMapper.GridReader QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { return Execute((conn, dbTransaction) => { return conn.QueryMultiple(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, beginTransaction, false); } /// /// 获取output值 param.Get("@b"); /// /// /// /// /// /// /// public (List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { using (SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType, beginTransaction)) { return (reader.Read().ToList(), reader.Read().ToList()); } } public (List, List, List) QueryMultiple(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { using (SqlMapper.GridReader reader = QueryMultiple(cmd, param, commandType, beginTransaction)) { return (reader.Read().ToList(), reader.Read().ToList(), reader.Read().ToList()); } } public T Execute(Func func, bool beginTransaction = false, bool disposeConn = true) { IDbTransaction dbTransaction = null; if (beginTransaction) { Connection.Open(); dbTransaction = Connection.BeginTransaction(); } try { T reslutT = func(Connection, dbTransaction); dbTransaction?.Commit(); return reslutT; } catch (Exception) { dbTransaction?.Rollback(); Connection.Dispose(); throw; } finally { if (disposeConn) { Connection.Dispose(); } } } public async Task ExecuteAsync(Func> funcAsync, bool beginTransaction = false, bool disposeConn = true) { IDbTransaction dbTransaction = null; if (beginTransaction) { Connection.Open(); dbTransaction = Connection.BeginTransaction(); } try { T reslutT = await funcAsync(Connection, dbTransaction); dbTransaction?.Commit(); return reslutT; } catch (Exception) { dbTransaction?.Rollback(); Connection.Dispose(); throw; } finally { if (disposeConn) { Connection.Dispose(); } } } public int ExecuteDML(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) { return ExecuteDML((conn, dbTransaction) => { return conn.Execute(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, dbTransaction, false); } public async Task ExecuteDMLAsync(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) { return await ExecuteDML(async (conn, dbTransaction) => { return await conn.ExecuteAsync(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, dbTransaction, false); } public IDataReader ExecuteDMLReader(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) { return ExecuteDML((conn, dbTransaction) => { return conn.ExecuteReader(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text); }, dbTransaction, false); } /// /// 执行数据库 /// /// /// /// /// /// public T ExecuteDML(Func func, IDbTransaction dbTransaction = null, bool disposeConn = false) { if (dbTransaction == null) { try { T reslutT = func(Connection, dbTransaction); dbTransaction?.Commit(); return reslutT; } catch (Exception) { dbTransaction?.Rollback(); Connection.Dispose(); throw; } finally { if (disposeConn) Connection.Dispose(); } } else { //Connection.Open(); try { if (_connection == null) _connection = dbTransaction.Connection; T reslutT = func(Connection, dbTransaction); return reslutT; } catch (Exception) { Connection.Dispose(); throw; } finally { if (disposeConn) Connection.Dispose(); } } } /// /// /// /// /// /// 指定插入的字段 /// 是否开启事务 /// public int Add(T entity, Expression> updateFileds = null, bool beginTransaction = false) { return AddRange(new T[] { entity }, updateFileds, beginTransaction); } /// /// /// /// /// /// 指定插入的字段 /// 是否开启事务 /// public int AddRange(IEnumerable entities, Expression> addFileds = null, bool beginTransaction = true) { Type entityType = typeof(T); var key = entityType.GetKeyProperty(); if (key == null) { throw new Exception("实体必须包括主键才能批量更新"); } string[] columns; //指定插入的字段 if (addFileds != null) { columns = addFileds.GetExpressionToArray(); } else { var properties = entityType.GetGenericProperties(); if (key.PropertyType != typeof(Guid)) { properties = properties.Where(x => x.Name != key.Name).ToArray(); } columns = properties.Select(x => x.Name).ToArray(); } string sql = null; bool mysql = DBType.Name == DbCurrentType.MySql.ToString(); if (mysql) { //mysql批量写入待优化 sql = $"insert into {entityType.GetEntityTableName()}({string.Join(",", columns)})" + $"values(@{string.Join(",@", columns)});"; } else { //sqlserver通过临时表批量写入 sql = $"insert into {entityType.GetEntityTableName()}({string.Join(",", columns)})" + $"select * from {EntityToSqlTempName.TempInsert};"; sql = entities.GetEntitySql(entityType == typeof(Guid), sql, null, addFileds, null); } return Execute((conn, dbTransaction) => { return conn.Execute(sql, mysql ? entities.ToList() : null); }, beginTransaction); } /// /// sqlserver使用的临时表参数化批量更新,mysql批量更新待发开 /// /// /// 实体必须带主键 /// 指定更新的字段x=new {x.a,x.b} /// 是否开启事务 /// public int Update(T entity, Expression> updateFileds = null, bool beginTransaction = true) { return UpdateRange(new T[] { entity }, updateFileds, beginTransaction); } /// ///(根据主键批量更新实体) sqlserver使用的临时表参数化批量更新,mysql待优化 /// /// /// 实体必须带主键 /// 批定更新字段 /// /// public int UpdateRange(IEnumerable entities, Expression> updateFileds = null, bool beginTransaction = true) { Type entityType = typeof(T); var key = entityType.GetKeyProperty(); if (key == null) { throw new Exception("实体必须包括主键才能批量更新"); } var properties = entityType.GetGenericProperties() .Where(x => x.Name != key.Name); if (updateFileds != null) { properties = properties.Where(x => updateFileds.GetExpressionToArray().Contains(x.Name)); } if (DBType.Name == DbCurrentType.MySql.ToString()) { List paramsList = new List(); foreach (var item in properties) { paramsList.Add(item.Name + "=@" + item.Name); } string sqltext = $@"UPDATE {entityType.GetEntityTableName()} SET {string.Join(",", paramsList)} WHERE {entityType.GetKeyName()} = @{entityType.GetKeyName()} ;"; return ExcuteNonQuery(sqltext, entities, CommandType.Text, true); // throw new Exception("mysql批量更新未实现"); } string fileds = string.Join(",", properties.Select(x => $" a.{x.Name}=b.{x.Name}").ToArray()); string sql = $"update a set {fileds} from {entityType.GetEntityTableName()} as a inner join {EntityToSqlTempName.TempInsert.ToString()} as b on a.{key.Name}=b.{key.Name}"; sql = entities.ToList().GetEntitySql(true, sql, null, updateFileds, null); return ExcuteNonQuery(sql, null, CommandType.Text, true); } public int DelWithKey(object[] keys, bool beginTransaction = false) { Type entityType = typeof(T); var keyProperty = entityType.GetKeyProperty(); if (keyProperty == null || keys == null || keys.Length == 0) return 0; IEnumerable<(bool, string, object)> validation = keyProperty.ValidationValueForDbType(keys); if (validation.Any(x => !x.Item1)) { throw new Exception($"主键类型【{validation.Where(x => !x.Item1).Select(s => s.Item3).FirstOrDefault()}】不正确"); } string tKey = entityType.GetKeyProperty().Name; FieldType fieldType = entityType.GetFieldType(); string joinKeys = fieldType == FieldType.Int || fieldType == FieldType.BigInt ? string.Join(",", keys) : $"'{string.Join("','", keys)}'"; string sql = $"DELETE FROM {entityType.GetEntityTableName()} where {tKey} in ({joinKeys});"; return (int)ExecuteScalar(sql, null); } /// /// 使用key批量删除 /// /// /// /// public int DelWithKey(object[] keys) { return DelWithKey(keys, false); } #region 批量插入 /// /// 通过Bulk批量插入 /// /// /// /// /// /// private int MSSqlBulkInsert(DataTable table, string tableName, SqlBulkCopyOptions sqlBulkCopyOptions = SqlBulkCopyOptions.UseInternalTransaction, string dbKeyName = null) { if (!string.IsNullOrEmpty(dbKeyName)) { Connection.ConnectionString = DBServerProvider.GetConnectionString(dbKeyName); } using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(Connection.ConnectionString, sqlBulkCopyOptions)) { sqlBulkCopy.DestinationTableName = tableName; sqlBulkCopy.BatchSize = table.Rows.Count; for (int i = 0; i < table.Columns.Count; i++) { sqlBulkCopy.ColumnMappings.Add(table.Columns[i].ColumnName, table.Columns[i].ColumnName); } sqlBulkCopy.WriteToServer(table); return table.Rows.Count; } } public int BulkInsert(List entities, string tableName = null, Expression> columns = null, SqlBulkCopyOptions? sqlBulkCopyOptions = null) { DataTable table = entities.ToDataTable(columns, false); return BulkInsert(table, tableName ?? typeof(T).GetEntityTableName(), sqlBulkCopyOptions); } public int BulkInsert(DataTable table, string tableName, SqlBulkCopyOptions? sqlBulkCopyOptions = null, string fileName = null, string tmpPath = null) { if (!string.IsNullOrEmpty(tmpPath)) { tmpPath = tmpPath.ReplacePath(); } if (Connection.GetType().Name == "MySqlConnection") return MySqlBulkInsert(table, tableName, fileName, tmpPath); return MSSqlBulkInsert(table, tableName, sqlBulkCopyOptions ?? SqlBulkCopyOptions.KeepIdentity); } #endregion #region 大批量数据插入,返回成功插入行数 /// ///大批量数据插入,返回成功插入行数 /// /// 数据库连接字符串 /// 数据表 /// 返回成功插入行数 private int MySqlBulkInsert(DataTable table, string tableName, string fileName = null, string tmpPath = null) { if (table.Rows.Count == 0) return 0; tmpPath = tmpPath ?? FileHelper.GetCurrentDownLoadPath(); fileName = fileName ?? $"{DateTime.Now.ToString("yyyyMMddHHmmss")}.csv"; int insertCount = 0; string csv = DataTableToCsv(table); FileHelper.WriteFile(tmpPath, fileName, csv); string path = tmpPath + fileName; try { if (Connection.State == ConnectionState.Closed) Connection.Open(); using (IDbTransaction tran = Connection.BeginTransaction()) { MySqlBulkLoader bulk = new MySqlBulkLoader(Connection as MySqlConnection) { FieldTerminator = ",", FieldQuotationCharacter = '"', EscapeCharacter = '"', LineTerminator = "\r\n", FileName = path.ReplacePath(), NumberOfLinesToSkip = 0, TableName = tableName, }; bulk.Columns.AddRange(table.Columns.Cast().Select(colum => colum.ColumnName).ToList()); insertCount = bulk.Load(); tran.Commit(); } } catch (Exception) { throw; } finally { Connection?.Dispose(); Connection?.Close(); } return insertCount; // File.Delete(path); } #endregion #region 将DataTable转换为标准的CSV /// ///将DataTable转换为标准的CSV /// /// 数据表 /// 返回标准的CSV private string DataTableToCsv(DataTable table) { //以半角逗号(即,)作分隔符,列为空也要表达其存在。 //列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。 //列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。 StringBuilder sb = new StringBuilder(); DataColumn colum; Type typeString = typeof(string); Type typeDate = typeof(DateTime); foreach (DataRow row in table.Rows) { for (int i = 0; i < table.Columns.Count; i++) { colum = table.Columns[i]; if (i != 0) sb.Append(","); if (colum.DataType == typeString && row[colum].ToString().Contains(",")) { sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\""); } else if (colum.DataType == typeDate) { //centos系统里把datatable里的日期转换成了10/18/18 3:26:15 PM格式 bool b = DateTime.TryParse(row[colum].ToString(), out DateTime dt); sb.Append(b ? dt.ToString("yyyy-MM-dd HH:mm:ss") : ""); } else sb.Append(row[colum].ToString()); } sb.AppendLine(); } return sb.ToString(); } #endregion #region 返回DataTable /// /// 返回DataTable /// /// /// /// /// /// public DataTable GetDataTable(string cmd, object param, CommandType? commandType = null, bool beginTransaction = false) { DataTable table = new DataTable("MyTable"); var reader = ExecuteReader(cmd, param, commandType, beginTransaction); table.Load(reader); return table; } public DataTable GetTransDataTable(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) { DataTable table = new DataTable("MyTable"); var reader = ExecuteDMLReader(cmd, param, commandType, dbTransaction); table.Load(reader); return table; } public async Task GetDataTableAsync(string cmd, object param = null, IDbTransaction transaction = null, CommandType? commandType = null, int? commandTimeout = null) { DataTable table = new DataTable("MyTable"); var reader = await Connection.ExecuteReaderAsync(cmd, param, transaction, commandTimeout, commandType: commandType ?? CommandType.Text); ; table.Load(reader); return table; } #endregion #region Transaction public IDbTransaction GetNewTransaction() { Connection.Open(); IDbTransaction trans = Connection.BeginTransaction(); return trans; } public void CommitTransaction(IDbTransaction dbTransaction) { dbTransaction?.Commit(); } public void RollbackTransaction(IDbTransaction dbTransaction) { dbTransaction?.Rollback(); } #endregion Transaction #region 查询按实体返回 public List QueryTransList(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) where T : class { return ExecuteDML((conn, dbTransaction) => { return conn.Query(cmd, param, dbTransaction, commandType: commandType ?? CommandType.Text).ToList(); }, dbTransaction, false); } public T QueryTransFirst(string cmd, object param, CommandType? commandType = null, IDbTransaction dbTransaction = null) where T : class { List list = QueryTransList(cmd, param, commandType: commandType ?? CommandType.Text, dbTransaction: dbTransaction).ToList(); return list.Count == 0 ? null : list[0]; } #endregion }