using System; #if !NETSTANDARD using System.Configuration; #endif using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Diagnostics; using System.Globalization; using System.Threading; #if !NETSTANDARD using System.Transactions; #endif using AntData.ORM.Common.Util; using AntData.ORM.Dao; using AntData.ORM.Dao.Common; using AntData.ORM.DbEngine.Connection; using AntData.ORM.DbEngine.ConnectionString; using AntData.ORM.DbEngine.Providers; using AntData.ORM.Enums; using StatementType = AntData.ORM.Enums.StatementType; namespace AntData.ORM.DbEngine.DB { /// <summary> /// 物理数据库对象 /// </summary> public sealed class Database { /// <summary> /// 数据库链接 /// </summary> private String m_ConnectionString; /// <summary> /// 数据库提供者对象 /// </summary> private readonly IDatabaseProvider m_DatabaseProvider; #region properties /// <summary> /// 数据库链接 /// </summary> public String ConnectionString { get { // 原来是 重新读取All In One中的连接串 return m_ConnectionString; } } /// <summary> /// 真正的数据库名 /// </summary> public volatile String ActualDatabaseName; /// <summary> /// 对应的AllInOne中的Key /// </summary> public String AllInOneKey { get; private set; } /// <summary> /// DalConfig中配置的DatabaseName /// </summary> public String DatabaseName { get; private set; } /// <summary> /// 数据库的类型,读库/写库 /// </summary> public DatabaseType DatabaseRWType { get; set; } /// <summary> /// 一个DataBaseSet可以是由多个db构成 给这个组起的名称 万一出错了可以打印这个名称来排错 /// </summary> public String DatabaseSetName { get; private set; } /// <summary> /// 当前数据库是否处于可用状态 /// </summary> public Boolean Available { get { return true; } } #endregion #region construction /// <summary> /// 构造方法 /// </summary> /// <param name="databaseSetName"></param> /// <param name="databaseName"></param> /// <param name="connectionStringName">数据库链接名称</param> /// <param name="databaseProvider">数据库提供者</param> public Database(String databaseSetName, String databaseName, String connectionStringName, IDatabaseProvider databaseProvider) { if (String.IsNullOrEmpty(connectionStringName)) throw new ArgumentNullException("connectionStringName"); if (String.IsNullOrEmpty(databaseSetName)) throw new ArgumentNullException("databaseSet"); if (String.IsNullOrEmpty(databaseName)) throw new ArgumentNullException("databaseName"); if (databaseProvider == null) throw new ArgumentNullException("databaseProvider"); DatabaseSetName = databaseSetName; DatabaseName = databaseName; AllInOneKey = connectionStringName; m_DatabaseProvider = databaseProvider; LoadActualConnectionString(); _closeTransaction = false; } /// <summary> /// 重新读取All In One中的连接串 这里为了方便直接用了配置文件里面的 /// </summary> private void LoadActualConnectionString() { var connectionStringSetting = ConnectionLocatorManager.Instance.GetConnectionString(AllInOneKey); //读取不到的话就 m_ConnectionString = connectionStringSetting == null ? AllInOneKey : connectionStringSetting.ConnectionString; } #endregion #region helper methods /// <summary> /// 获取打开的数据库链接 /// </summary> /// <param name="disposeInnerConnection">是否释放数据库链接对象</param> /// <returns></returns> private ConnectionWrapper GetOpenConnection(Boolean disposeInnerConnection) { var connection = TransactionConnectionManager.GetConnection(this); if (connection != null) { //#if DEBUG // Debug.WriteLine(connection.ConnectionString); //#endif return new ConnectionWrapper(connection, false); } try { connection = CreateConnection(); Interlocked.CompareExchange(ref ActualDatabaseName, connection.Database, null); connection.Open(); } catch { if (connection != null) connection.Close(); throw; } return new ConnectionWrapper(connection, disposeInnerConnection); } #region Transactions bool _closeTransaction; public IDbTransaction Transactions { get; internal set; } public DataConnectionTransaction BeginTransaction(Statement statement) { // If transaction is open, we dispose it, it will rollback all changes. // if (Transactions != null) Transactions.Dispose(); // Create new transaction object. DbConnection connection = null; try { connection = CreateConnection(); connection.Open(); } catch { if (connection != null) connection.Close(); throw; } if (statement.Hints != null && statement.Hints.Contains(DALExtStatementConstant.ISOLATION_LEVEL)) { var level = (System.Data.IsolationLevel)statement.Hints[DALExtStatementConstant.ISOLATION_LEVEL]; Transactions = connection.BeginTransaction(level); } else { Transactions = connection.BeginTransaction(); } _closeTransaction = true; return new DataConnectionTransaction(new ConnectionWrapper(connection, this)); } public void CommitTransaction() { if (Transactions != null) { Transactions.Commit(); if (_closeTransaction) { Transactions.Dispose(); Transactions = null; } } } public void RollbackTransaction() { if (Transactions != null) { Transactions.Rollback(); if (_closeTransaction) { Transactions.Dispose(); Transactions = null; } } } #endregion /// <summary> /// 创建数据库链接 /// </summary> /// <returns></returns> public DbConnection CreateConnection() { if (String.IsNullOrEmpty(ConnectionString)) throw new DalException(String.Format("ConnectionString:{0} can't be found!", AllInOneKey)); //#if DEBUG // Debug.WriteLine(ConnectionString); //#endif var connection = m_DatabaseProvider.CreateConnection(); connection.ConnectionString = ConnectionString; return connection; } /// <summary> /// 准备数据库指令 /// </summary> /// <param name="statement">上层指令</param> /// <returns>数据库指令</returns> private DbCommand PrepareCommand(Statement statement) { DbCommand command = m_DatabaseProvider.CreateCommand(); command.CommandText = statement.StatementText; command.CommandType = statement.StatementType == StatementType.Sql ? CommandType.Text : CommandType.StoredProcedure; command.CommandTimeout = statement.Timeout; String providerName = m_DatabaseProvider.GetType().Name; foreach (var p in statement.Parameters) { if (p.ExtendType == 1) { var parameter = (SqlParameter)command.CreateParameter(); parameter.ParameterName = m_DatabaseProvider.CreateParameterName(p.Name); parameter.SqlDbType = (SqlDbType)p.ExtendTypeValue; if (!String.IsNullOrEmpty(p.TypeName)) parameter.TypeName = p.TypeName; parameter.Size = p.Size; parameter.Value = p.Value ?? DBNull.Value; parameter.Direction = p.Direction; parameter.IsNullable = p.IsNullable; command.Parameters.Add(parameter); } else { if (p.DbType == DbType.Time && providerName == "SqlDatabaseProvider") { var parameter = (SqlParameter)command.CreateParameter(); parameter.ParameterName = m_DatabaseProvider.CreateParameterName(p.Name); parameter.SqlDbType = SqlDbType.Time; parameter.Size = p.Size; parameter.Value = p.Value ?? DBNull.Value; parameter.Direction = p.Direction; parameter.IsNullable = p.IsNullable; command.Parameters.Add(parameter); } else { var parameter = command.CreateParameter(); parameter.ParameterName = m_DatabaseProvider.CreateParameterName(p.Name); parameter.DbType = p.DbType; parameter.Size = p.Size; parameter.Value = p.Value ?? DBNull.Value; parameter.Direction = p.Direction; parameter.IsNullable = p.IsNullable; if (providerName.Equals("MySqlDatabaseProvider")) { command.Parameters.Insert(-1, parameter); //work around for legacy mysql driver versions } else { command.Parameters.Add(parameter); } } } } return command; } /// <summary> /// 更新执行后的参数 /// </summary> /// <param name="statement">指令</param> /// <param name="command">数据库指令</param> private void UpdateStatementParamenters(Statement statement, DbCommand command) { foreach (var p in statement.Parameters) { if (p.Direction != ParameterDirection.Input) p.Value = command.Parameters[m_DatabaseProvider.CreateParameterName(p.Name)].Value; } } #if !NETSTANDARD /// <summary> /// 加载程序集 /// </summary> /// <param name="statement">statement</param> /// <param name="command">指令</param> /// <param name="dataSet">程序集</param> /// <param name="tableNames">表名称</param> private void LoadDataSet(Statement statement, DbCommand command, DataSet dataSet, params String[] tableNames) { Boolean schemaRequired = false; Boolean disableConstraints = false; if (statement.Hints != null && statement.Hints.Contains(DALExtStatementConstant.RETRIEVE_SCHEMA)) { schemaRequired = true; if (statement.Hints.Contains(DALExtStatementConstant.DISABLE_CONSTRAINTS)) disableConstraints = true; } if (tableNames == null || tableNames.Length == 0) tableNames = new[] { "Table" }; for (Int32 i = 0; i < tableNames.Length; i++) { if (String.IsNullOrEmpty(tableNames[i])) throw new ArgumentException(String.Concat("tableNames[", i, "]")); } using (var adapter = m_DatabaseProvider.CreateDataAdapter()) { adapter.SelectCommand = command; for (Int32 i = 0; i < tableNames.Length; i++) { String tableName = (i == 0) ? "Table" : "Table" + i; adapter.TableMappings.Add(tableName, tableNames[i]); } if (schemaRequired) { adapter.FillSchema(dataSet, SchemaType.Mapped); if (disableConstraints) dataSet.EnforceConstraints = false; } adapter.Fill(dataSet); } } #endif #endregion #if !NETSTANDARD /// <summary> /// 执行返回数据集指令 /// </summary> /// <param name="statement">指令</param> /// <returns>数据集</returns> public DataSet ExecuteDataSet(Statement statement) { var watch = new Stopwatch(); watch.Start(); try { DataSet dataSet = new DataSet { Locale = CultureInfo.InvariantCulture }; statement.PreProcess(AllInOneKey, ActualDatabaseName, DatabaseRWType, m_DatabaseProvider, ConnectionString); var trans_wrapper = GetConnectionInTransaction(statement); using (IDbCommand command = PrepareCommand(statement)) { using (var wrapper = trans_wrapper ?? GetOpenConnection(true)) { command.Connection = wrapper.Connection; if (trans_wrapper != null) { command.Transaction = trans_wrapper.Database.Transactions; } try { LoadDataSet(statement, (DbCommand) command, dataSet); } finally { UpdateStatementParamenters(statement, (DbCommand)command); SetRuntimeDetail(statement, wrapper); } } } statement.ExecStatus = DALState.Success; return dataSet; } catch (Exception ex) { statement.ExecStatus = DALState.Fail; throw ex; } finally { watch.Stop(); statement.Duration = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds); } } #endif /// <summary> /// 执行非查询指令 /// </summary> /// <param name="statement">指令</param> /// <returns>影响行数</returns> public Int32 ExecuteNonQuery(Statement statement) { var watch = new Stopwatch(); watch.Start(); try { Int32 result; statement.PreProcess(AllInOneKey, ActualDatabaseName, DatabaseRWType, m_DatabaseProvider, ConnectionString); var trans_wrapper = GetConnectionInTransaction(statement); using (IDbCommand command = PrepareCommand(statement)) { using (var wrapper = trans_wrapper ?? GetOpenConnection(true)) { command.Connection = wrapper.Connection; if (trans_wrapper!=null) { command.Transaction = trans_wrapper.Database.Transactions; } try { result = command.ExecuteNonQuery(); } finally { UpdateStatementParamenters(statement, (DbCommand)command); SetRuntimeDetail(statement, wrapper); } } } statement.ExecStatus = DALState.Success; return result; } catch (Exception ex) { statement.ExecStatus = DALState.Fail; throw ex; } finally { watch.Stop(); statement.Duration = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds); } } /// <summary> /// 执行返回单向只读数据集的指令 /// </summary> /// <param name="statement">指令</param> /// <returns>单向只读DataReader对象</returns> public IDataReader ExecuteReader(Statement statement) { var watch = new Stopwatch(); watch.Start(); try { IDataReader reader; statement.PreProcess(AllInOneKey, ActualDatabaseName, DatabaseRWType, m_DatabaseProvider, ConnectionString); var trans_wrapper = GetConnectionInTransaction(statement); using (IDbCommand command = PrepareCommand(statement)) { using (var wrapper = trans_wrapper ?? GetOpenConnection(false)) { command.Connection = wrapper.Connection; if (trans_wrapper != null) { command.Transaction = trans_wrapper.Database.Transactions; } try { #if !NETSTANDARD reader = command.ExecuteReader(Transaction.Current != null ? CommandBehavior.Default : CommandBehavior.CloseConnection); #else reader = command.ExecuteReader(trans_wrapper!=null ?CommandBehavior.Default :CommandBehavior.CloseConnection); #endif } finally { UpdateStatementParamenters(statement, (DbCommand)command); SetRuntimeDetail(statement, wrapper); } } } statement.ExecStatus = DALState.Success; return reader; } catch (Exception ex) { statement.ExecStatus = DALState.Fail; throw ex; } finally { watch.Stop(); statement.Duration = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds); } } /// <summary> /// 判断 hints里面是否已有连接 /// </summary> /// <param name="statement"></param> /// <returns></returns> public ConnectionWrapper GetConnectionInTransaction(Statement statement) { ConnectionWrapper trans_wrapper = null; if (statement.Hints != null && statement.Hints.Contains(DALExtStatementConstant.TRANSACTION_CONNECTION)) { trans_wrapper = statement.Hints[DALExtStatementConstant.TRANSACTION_CONNECTION] as ConnectionWrapper; } return trans_wrapper; } /// <summary> /// 执行单返回值聚集查询指令 /// </summary> /// <param name="statement">指令</param> /// <returns>聚集结果</returns> public Object ExecuteScalar(Statement statement) { var watch = new Stopwatch(); watch.Start(); try { Object result; statement.PreProcess(AllInOneKey, ActualDatabaseName, DatabaseRWType, m_DatabaseProvider, ConnectionString); var trans_wrapper = GetConnectionInTransaction(statement); using (IDbCommand command = PrepareCommand(statement)) { using (var wrapper = trans_wrapper ?? GetOpenConnection(true)) { command.Connection = wrapper.Connection; if (trans_wrapper != null) { command.Transaction = trans_wrapper.Database.Transactions; } try { result = command.ExecuteScalar(); } finally { UpdateStatementParamenters(statement, (DbCommand)command); SetRuntimeDetail(statement, wrapper); } } } statement.ExecStatus = DALState.Success; return result; } catch (Exception ex) { statement.ExecStatus = DALState.Fail; throw ex; } finally { watch.Stop(); statement.Duration = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds); } } /// <summary> /// 执行返回单向只读数据集的指令 /// </summary> /// <param name="statement">指令</param> /// <returns>单向只读DataReader对象</returns> public IDataReader InnnerExecuteReader(Statement statement) { var watch = new Stopwatch(); watch.Start(); try { IDataReader reader; statement.SQLHash = CommonUtil.GetHashCodeOfSQL(statement.StatementText); if (statement.StatementType == StatementType.Sql) statement.StatementText = CommonUtil.GetTaggedAppIDSql(statement.StatementText); watch.Start(); var trans_wrapper = GetConnectionInTransaction(statement); using (IDbCommand command = PrepareCommand(statement)) { using (var wrapper = trans_wrapper ?? GetOpenConnection(false)) { command.Connection = wrapper.Connection; if (trans_wrapper != null) { command.Transaction = trans_wrapper.Database.Transactions; } try { #if !NETSTANDARD reader = command.ExecuteReader(Transaction.Current != null ? CommandBehavior.Default : CommandBehavior.CloseConnection); #else reader = command.ExecuteReader(trans_wrapper !=null ?CommandBehavior.Default : CommandBehavior.CloseConnection); #endif } finally { UpdateStatementParamenters(statement, (DbCommand)command); SetRuntimeDetail(statement, wrapper); } } } statement.ExecStatus = DALState.Success; return reader; } catch { statement.ExecStatus = DALState.Fail; return null; } finally { watch.Stop(); statement.Duration = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds); } } /// <summary> /// 执行单返回值聚集查询指令 /// </summary> /// <param name="statement">指令</param> /// <returns>聚集结果</returns> public Object InnnerExecuteScalar(Statement statement) { var watch = new Stopwatch(); watch.Start(); try { statement.SQLHash = CommonUtil.GetHashCodeOfSQL(statement.StatementText); if (statement.StatementType == StatementType.Sql) statement.StatementText = CommonUtil.GetTaggedAppIDSql(statement.StatementText); watch.Start(); Object result; var trans_wrapper = GetConnectionInTransaction(statement); using (IDbCommand command = PrepareCommand(statement)) { using (var wrapper = trans_wrapper ?? GetOpenConnection(true)) { if (trans_wrapper != null) { command.Transaction = trans_wrapper.Database.Transactions; } command.Connection = wrapper.Connection; try { result = command.ExecuteScalar(); } finally { UpdateStatementParamenters(statement, (DbCommand)command); SetRuntimeDetail(statement, wrapper); } } } statement.ExecStatus = DALState.Success; statement.RecordCount = result == null ? 0 : 1; return result; } catch { statement.ExecStatus = DALState.Fail; return null; } finally { watch.Stop(); statement.Duration = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds); } } private void SetRuntimeDetail(Statement statement, ConnectionWrapper wrapper) { String providerName = m_DatabaseProvider.GetType().Name; if (providerName.Equals("OracleDatabaseProvider")) { if (!string.IsNullOrEmpty(wrapper.DataSource) && string.IsNullOrEmpty(wrapper.DBName) && wrapper.DataSource.Contains("/")) { statement.DbName = wrapper.DataSource.Split('/')[1]; statement.HostName = wrapper.DataSource.Split('/')[0]; } else { statement.DbName = wrapper.DBName; statement.HostName = wrapper.DataSource; } } else { statement.DbName = wrapper.DBName;//真正物理的db名称 statement.HostName = wrapper.DataSource; } } } }