using System; using System.Data.Common; using System.Data; using System.Diagnostics; using CYQ.Data.SQL; using System.Collections.Generic; using CYQ.Data.Tool; using System.Data.SqlTypes; using System.Threading; using CYQ.Data.Orm; using System.IO; using CYQ.Data.Json; namespace CYQ.Data { /// /// 数据库操作基类 (模板模式:Template Method) /// 属性管理 /// internal abstract partial class DalBase : IDisposable { #region 对外公开的属性 /// /// 记录SQL语句信息 /// public System.Text.StringBuilder DebugInfo = new System.Text.StringBuilder(); /// /// 执行命令时所受影响的行数(-2为发生异常)。 /// public int RecordsAffected = 0; /// /// 当前是否开启事务 /// public bool IsOpenTrans = false; /// /// 原生态传进来的配置名(或链接) /// public string ConnName { get { return !ConnObj.Master.IsBackup ? ConnObj.Master.ConnName : ConnObj.BackUp.ConnName; } } /// /// 数据库主从备链接管理对象; /// public ConnObject ConnObj; /// /// 当前使用中的链接对象 /// public ConnBean UsingConnBean; /// /// 获得链接的数据库名称 /// public virtual string DataBaseName { get { if (!string.IsNullOrEmpty(_con.Database)) { return _con.Database; } else { return System.IO.Path.GetFileNameWithoutExtension(_con.DataSource); } } } /// /// 获得链接的模式名称 /// public virtual string SchemaName { get { return DataBaseName; } } private static MDictionary _VersionCache = new MDictionary(); private string _Version = string.Empty; /// /// 数据库的版本号 /// public string Version { get { if (string.IsNullOrEmpty(_Version)) { if (_VersionCache.ContainsKey(ConnName)) { _Version = _VersionCache[ConnName]; } else { if (IsOpenTrans && UsingConnBean.IsSlave)// && 事务操作时,如果在从库,切回主库 { ResetConn(ConnObj.Master); } if (OpenCon(UsingConnBean, AllowConnLevel.MaterBackupSlave))//这里可能切换链接 { _Version = _con.ServerVersion; if (!_VersionCache.ContainsKey(ConnName)) { _VersionCache.Set(ConnName, _Version); } if (!IsOpenTrans)//避免把事务给关闭了。 { CloseCon(); } } } } return _Version; } } /// /// 当前操作的数据库类型 /// public DataBaseType DataBaseType { get { return ConnObj.Master.ConnDataBaseType; } } #endregion /// /// 是否允许进入写日志中模块(默认true) /// public bool IsWriteLogOnError = true; protected bool isUseUnsafeModeOnSqlite = false; //private bool isAllowResetConn = true;//如果执行了非查询之后,为了数据的一致性,不允许切换到Slave数据库链接 private string tempSql = string.Empty;//附加信息,包括调试信息 private IsolationLevel _TranLevel = IsolationLevel.ReadCommitted; internal IsolationLevel TranLevel { get { if (_tran != null && _com != null && _com.Transaction != null) { return _com.Transaction.IsolationLevel; } return _TranLevel; } set { if (_tran != null && _com != null && _com.Transaction != null) { Error.Throw("IsolationLevel is readonly when transaction is begining!"); } else { _TranLevel = value; } } } protected DbProviderFactory _fac = null; protected DbConnection _con = null; protected DbCommand _com; internal DbTransaction _tran; private Stopwatch _watch; public DbConnection Con { get { return _con; } } public DbCommand Com { get { return _com; } } private bool _IsRecordDebugInfo = true; /// /// 是否允许记录SQL语句 (内部操作会关掉此值为False) /// public bool IsRecordDebugInfo { get { return _IsRecordDebugInfo && (AppConfig.Debug.IsEnable || AppConfig.DB.PrintSql >= 0 || AppDebug.IsRecording); } set { _IsRecordDebugInfo = value; } } public DalBase(ConnObject co) { this.ConnObj = co; this.UsingConnBean = co.Master; _fac = GetFactory(); _con = _fac.CreateConnection(); try { _con.ConnectionString = co.Master.ConnString; //if (DataBaseName.Contains("dm")) //{ //} } catch (Exception err) { Error.Throw("check the connectionstring is be ok!" + AppConst.BR + "error:" + err.Message + AppConst.BR + ConnName); } _com = _con.CreateCommand(); if (_com != null)//Txt| Xml 时返回Null { _com.Connection = _con; _com.CommandTimeout = AppConfig.DB.CommandTimeout; } if (IsRecordDebugInfo)//开启秒表计算 { _watch = new Stopwatch(); } //if (AppConfig.DB.LockOnDbExe && dalType == DalType.Access) //{ // string dbName = DataBase; // if (!_dbOperator.ContainsKey(dbName)) // { // try // { // _dbOperator.Add(dbName, false); // } // catch // { // } // } //} //_com.CommandTimeout = 1; } protected abstract DbProviderFactory GetFactory(); #region 拿表、视图、存储过程等元数据。 public virtual Dictionary GetTables() { return GetSchemaDic(GetUVPSql("U"), "U", false); } public virtual Dictionary GetTables(bool isIgnoreCache) { return GetSchemaDic(GetUVPSql("U"), "U", isIgnoreCache); } public virtual Dictionary GetViews() { return GetSchemaDic(GetUVPSql("V"), "V", false); } public virtual Dictionary GetViews(bool isIgnoreCache) { return GetSchemaDic(GetUVPSql("V"), "V", isIgnoreCache); } public virtual Dictionary GetProcs() { return GetSchemaDic(GetUVPSql("P"), "P", false); } public virtual Dictionary GetProcs(bool isIgnoreCache) { return GetSchemaDic(GetUVPSql("P"), "P", isIgnoreCache); } protected Dictionary GetSchemaDic(string sql, string type, bool isIgnoreCache) { if (string.IsNullOrEmpty(sql)) { return null; } Dictionary dic = null; string key = ConnBean.GetHashKey(ConnName) + "_" + DataBaseName + "_" + type + "_" + StaticTool.GetHashKey(sql); #region 内存短暂缓存检测 dic = Cache.DistributedCache.Local.Get(key) as Dictionary;//缓存3秒,避免后台线程和代码重复读取 if (dic != null) { return dic; } #endregion #region 硬盘长久缓存检测 if (!isIgnoreCache) { if (!string.IsNullOrEmpty(AppConfig.DB.SchemaMapPath)) { string fullPath = AppConfig.WebRootPath + AppConfig.DB.SchemaMapPath.Trim('/', '\\') + "/" + key + ".json"; if (System.IO.File.Exists(fullPath)) { string json = IOHelper.ReadAllText(fullPath); if (!string.IsNullOrEmpty(json)) { dic = JsonHelper.ToEntity>(json); if (dic != null && dic.Count > 0) { return dic; } } } } } #endregion lock (sql) { //锁住后,读取缓存,避免后台线程和代码重复读取 dic = Cache.DistributedCache.Local.Get(key) as Dictionary; if (dic != null) { return dic; } dic = new Dictionary(StringComparer.OrdinalIgnoreCase); IsRecordDebugInfo = false || AppDebug.IsContainSysSql; DbDataReader sdr = ExeDataReader(sql, false); IsRecordDebugInfo = true; if (sdr != null) { while (sdr.Read()) { string tableName = Convert.ToString(sdr["TableName"]).Trim(); if (!dic.ContainsKey(tableName)) { dic.Add(tableName, Convert.ToString(sdr["Description"])); } } sdr.Close(); sdr = null; } #region 内存短暂缓存 if (dic != null) { Cache.DistributedCache.Local.Set(key, dic, 0.05);//缓存3秒,避免后台线程和代码重复读取 } #endregion } #region 硬盘长久缓存 if (dic != null && dic.Count > 0) { if (!string.IsNullOrEmpty(AppConfig.DB.SchemaMapPath)) { //缓存写入硬盘 Thread thread = new Thread(() => { string fullPath = AppConfig.WebRootPath + AppConfig.DB.SchemaMapPath.Trim('/', '\\') + "/" + key + ".json"; string folder = Path.GetDirectoryName(fullPath); if (!System.IO.Directory.Exists(folder)) { System.IO.Directory.CreateDirectory(folder); } string json = JsonHelper.ToJson(dic); IOHelper.Write(fullPath, json); }); thread.Start(); } } #endregion return dic; } /// /// 读取表名、视图、存储过程列表 /// /// U、V、P /// protected virtual string GetUVPSql(string type) { return ""; } #endregion #region 数据库链接切换相关逻辑 /// /// 切换数据库(修改数据库链接) /// internal DBResetResult ChangeDatabase(string dbName) { if (_con.State == ConnectionState.Closed && !IsOpenTrans)//事务中。。不允许切换 { try { if (IsExistsDbNameWithCache(dbName))//新的数据库不存在。。不允许切换 { string newConnString = GetConnString(dbName); _con.ConnectionString = newConnString; ConnObj = ConnObject.Create(dbName + "Conn"); ConnObj.Master.ConnName = dbName + "Conn"; ConnObj.Master.ConnString = newConnString; return DBResetResult.Yes; } else { return DBResetResult.No_DBNoExists; } } catch (Exception err) { Log.Write(err, LogType.DataBase); } } return DBResetResult.No_Transationing; } //检测并切换数据库链接。 internal DBResetResult ChangeDatabaseWithCheck(string dbTableName)//---------- { if (IsOwnerOtherDb(dbTableName))//数据库名称变化了。 { string dbName = dbTableName.Split('.')[0]; return ChangeDatabase(dbName); } return DBResetResult.No_SaveDbName; } internal DalBase ResetDalBase(string dbTableName) { if (IsOwnerOtherDb(dbTableName))//是其它数据库名称。 { if (_con.State != ConnectionState.Closed)//事务中。。创建新链接切换 { string dbName = dbTableName.Split('.')[0]; return DalCreate.CreateDal(GetConnString(dbName)); } } return this; } /// /// 是否数据库名称变化了 /// /// /// private bool IsOwnerOtherDb(string dbTableName) { int index = dbTableName.IndexOf('.');//DBName.TableName if (index > 0 && !dbTableName.Contains(" ")) //排除视图语句 { string dbName = dbTableName.Split('.')[0]; if (string.Compare(DataBaseName, dbName, StringComparison.OrdinalIgnoreCase) != 0 && ConnName.IndexOf(DataBaseName) == ConnName.LastIndexOf(DataBaseName)) { return true; } } return false; } protected string GetConnString(string dbName) { string newConn = AppConfig.GetConn(dbName + "Conn"); if (!string.IsNullOrEmpty(newConn)) { ConnBean bean = ConnBean.Create(newConn); if (bean != null) { return bean.ConnString; } return ""; } return UsingConnBean.ConnString.Replace(DataBaseName, dbName); } static MDictionary dbList = new MDictionary(3); private bool IsExistsDbNameWithCache(string dbName) { try { string key = DataBaseType.ToString() + "." + dbName; if (dbList.ContainsKey(key)) { return dbList[key]; } bool result = IsExistsDbName(dbName); dbList.Add(key, result); return result; } catch { return true; } } protected abstract bool IsExistsDbName(string dbName); #endregion private int returnValue = -1; /// /// 存储过程返回值。 /// public int ReturnValue { get { if (returnValue == -1 && _com != null && _com.Parameters != null && _com.Parameters.Count > 0) { for (int i = _com.Parameters.Count - 1; i >= 0; i--) { if (_com.Parameters[i].Direction == ParameterDirection.ReturnValue) { int.TryParse(Convert.ToString(_com.Parameters[i].Value), out returnValue); break; } } } return returnValue; } set { returnValue = value; } } /// /// 存储过程OutPut输出参数值。 /// 如果只有一个输出,则为值; /// 如果有多个输出,则为Dictionary。 /// public object OutPutValue { get { if (_com != null && _com.Parameters != null && _com.Parameters.Count > 0) { Dictionary opValues = new Dictionary(StringComparer.OrdinalIgnoreCase); object outPutValue = null; foreach (DbParameter para in _com.Parameters) { if (para.Direction == ParameterDirection.Output) { opValues.Add(para.ParameterName, para.Value); outPutValue = para.Value; } } if (opValues.Count < 2) { opValues = null; return outPutValue; } return opValues; } return null; } } public virtual char Pre { get { return '@'; } } #region 参数化管理 public bool AddParameters(string parameterName, object value) { return AddParameters(parameterName, value, DbType.String, -1, ParameterDirection.Input); } public virtual bool AddParameters(string parameterName, object value, DbType dbType, int size, ParameterDirection direction) { if (DataBaseType == DataBaseType.Oracle) { parameterName = parameterName.Replace(":", "").Replace("@", ""); if (dbType == DbType.String && size > 4000) { AddCustomePara(parameterName, size == int.MaxValue ? ParaType.CLOB : ParaType.NCLOB, value, null); return true; } } else { parameterName = parameterName.Substring(0, 1) == Pre.ToString() ? parameterName : Pre + parameterName; } if (Com == null) { return false; } if (Com.Parameters.Contains(parameterName))//已经存在,不添加 { return false; } DbParameter para = _com.CreateParameter(); para.ParameterName = parameterName; para.Value = value == null ? DBNull.Value : value; if (dbType == DbType.Time)// && dalType != DalType.MySql { para.DbType = DbType.String; } else { if (dbType == DbType.DateTime && value != null) { string time = Convert.ToString(value); if (DataBaseType == DataBaseType.MsSql && time == DateTime.MinValue.ToString()) { para.Value = SqlDateTime.MinValue; } else if (DataBaseType == DataBaseType.MySql && (time == SqlDateTime.MinValue.ToString() || time == DateTime.MinValue.ToString())) { para.Value = DateTime.MinValue; } } para.DbType = dbType; } if (dbType == DbType.Binary && DataBaseType == DataBaseType.MySql)//(mysql不能设定长度,否则会报索引超出了数组界限错误【已过时,旧版本的MySql.Data.dll不能指定长度】)。 { if (value != null) { byte[] bytes = value as byte[]; para.Size = bytes.Length;//新版本的MySql.Data.dll 修正了长度指定(不指定就没数据进去),所以又要指定长度,Shit } else { para.Size = -1; } } else if (dbType != DbType.Binary && size > 0) { if (size != para.Size) { para.Size = size; } } para.Direction = direction; if (para.DbType == DbType.String && para.Value != null) { para.Value = Convert.ToString(para.Value); } Com.Parameters.Add(para); return true; } internal virtual void AddCustomePara(string paraName, ParaType paraType, object value, string typeName) { switch (paraType) { case ParaType.OutPut: AddParameters(paraName, null, DbType.String, 2000, ParameterDirection.Output); break; case ParaType.InputOutput: AddParameters(paraName, null, DbType.String, 2000, ParameterDirection.InputOutput); break; case ParaType.ReturnValue: AddParameters(paraName, null, DbType.Int32, 32, ParameterDirection.ReturnValue); break; } } //internal virtual void AddCustomePara(string paraName, ParaType paraType, object value) //{ //} //public abstract DbParameter GetNewParameter(); public void ClearParameters() { if (_com != null && _com.Parameters != null) { _com.Parameters.Clear(); } } /// /// 处理内置的MSSQL和Oracle两种存储过程分页 /// protected virtual void AddReturnPara() { } private void SetCommandText(string commandText, bool isProc) { if (_com == null && DebugInfo.Length > 0) { throw new Exception("DalBase.SetCommandText stop running because : " + DebugInfo.ToString()); } if (OracleDal.clientType > 0) { Type t = _com.GetType(); System.Reflection.PropertyInfo pi = t.GetProperty("BindByName"); if (pi != null) { pi.SetValue(_com, true, null); } } _com.CommandText = isProc ? commandText : SqlFormat.Compatible(commandText, DataBaseType, false); if (!isProc) { if (DataBaseType == DataBaseType.SQLite && _com.CommandText.Contains("charindex")) { _com.CommandText += " COLLATE NOCASE";//忽略大小写 } //else if (DataBaseType == DataBaseType.DaMeng && !string.IsNullOrEmpty(DataBaseName)) //{ // _com.CommandText = "set schema " + DataBaseName + ";" + _com.CommandText; //} } _com.CommandType = isProc ? CommandType.StoredProcedure : CommandType.Text; //if (isProc) //{ // if (commandText.Contains("SelectBase") && !_com.Parameters.Contains("ReturnValue")) // { // AddReturnPara(); // //检测是否存在分页存储过程,若不存在,则创建。 // Tool.DBTool.CreateSelectBaseProc(DataBaseType, ConnName);//内部分检测是否已创建过。 // } //} //else //{ //上面if代码被注释了,下面代码忘了加!isProc判断,现补上。 取消多余的参数,新加的小贴心,过滤掉用户不小心写多的参数。 if (!isProc && _com != null && _com.Parameters != null && _com.Parameters.Count > 0) { bool needToReplace = (DataBaseType == DataBaseType.Oracle || DataBaseType == DataBaseType.MySql || DataBaseType == Data.DataBaseType.PostgreSQL) && _com.CommandText.Contains("@"); string paraName; for (int i = 0; i < _com.Parameters.Count; i++) { paraName = _com.Parameters[i].ParameterName.TrimStart(Pre);//默认自带前缀的,取消再判断 if (needToReplace && _com.CommandText.IndexOf("@" + paraName) > -1) { //兼容多数据库的参数(虽然提供了=:?"为兼容语法,但还是贴心的再处理一下) switch (DataBaseType) { case DataBaseType.Oracle: case DataBaseType.MySql: case Data.DataBaseType.PostgreSQL: _com.CommandText = _com.CommandText.Replace("@" + paraName, Pre + paraName); break; } } if (_com.CommandText.IndexOf(Pre + paraName, StringComparison.OrdinalIgnoreCase) == -1) { _com.Parameters.RemoveAt(i); i--; } } } // } //else //{ // string checkText = commandText.ToLower(); // //int index= // //if (checkText.IndexOf("table") > -1 && (checkText.IndexOf("delete") > -1 || checkText.IndexOf("drop") > -1 || checkText.IndexOf("truncate") > -1)) // //{ // // Log.WriteLog(commandText); // //} //} if (IsRecordDebugInfo) { tempSql = GetParaInfo(_com.CommandText) + AppConst.BR + "execute time is: "; } } #endregion #region 调试信息管理 private string GetParaInfo(string commandText) { string paraInfo = DataBaseType + "." + DataBaseName + ".SQL: " + AppConst.BR + commandText; foreach (DbParameter item in _com.Parameters) { paraInfo += AppConst.BR + "Para: " + item.ParameterName + "-> " + (item.Value == DBNull.Value ? "DBNull.Value" : item.Value); } return paraInfo; } /// /// 记录执行时间 /// private void WriteTime() { if (IsRecordDebugInfo && _watch != null) { _watch.Stop(); double ms = _watch.Elapsed.TotalMilliseconds; _watch.Reset(); tempSql += ms + " (ms)" + AppConst.HR; if (AppConfig.Debug.IsEnable) { DebugInfo.Append(tempSql); } if (AppDebug.IsRecording && ms >= AppDebug.InfoFilter) { AppDebug.Add(tempSql); } if (AppConfig.DB.PrintSql >= 0 && ms >= AppConfig.DB.PrintSql) { Log.WriteLogToTxt(tempSql, LogType.Debug + "_PrintSql"); } tempSql = null; } } #endregion #region 事务管理 public bool EndTransaction() { IsOpenTrans = false; if (_tran != null) { try { if (_tran.Connection == null) { return false;//上一个执行语句发生了异常(特殊情况在ExeReader guid='xxx' 但不抛异常) } _tran.Commit(); return true; } catch (Exception err) { RollBack(); WriteError("EndTransaction():" + err.Message); } finally { _tran = null; CloseCon(); } } return false; } /// /// 事务(有则)回滚 /// /// public bool RollBack() { if (_tran != null) { try { if (_tran.Connection != null) { _tran.Rollback(); return true; } } catch (Exception err) { WriteError("RollBack():" + err.Message); } finally { _tran = null;//以便重启事务,避免无法二次回滚。 } } return false; } #endregion #region 异常处理 internal delegate void OnException(string msg); internal event OnException OnExceptionEvent; internal bool IsOnExceptionEventNull { get { return OnExceptionEvent == null; } } /// /// 输出错误(若事务中,回滚事务) /// /// internal void WriteError(string err) { err = DataBaseType + " Call Function::" + err; if (_watch != null && _watch.IsRunning) { _watch.Stop(); _watch.Reset(); } RollBack(); if (IsWriteLogOnError) { Log.Write(err + AppConst.BR + DebugInfo, LogType.DataBase); } if (OnExceptionEvent != null) { try { OnExceptionEvent(err); } catch { } } if (IsOpenTrans) { Dispose();//事务中发生语句语法错误,直接关掉资源,避免因后续代码继续执行。 } } #endregion public void Dispose() { string key = StaticTool.GetTransationKey(UsingConnBean.ConnName); if (DBFast.HasTransation(key)) { return;//全局事务由全局控制(全局事务会在移除key后重新调用)。 } if (_con != null) { CloseCon(); _con = null; } if (_com != null) { _com.Dispose(); _com = null; } if (_watch != null) { _watch = null; } } } /// /// 执行管理 /// internal abstract partial class DalBase { private DbDataReader ExeDataReaderSQL(string cmdText, bool isProc) { RecordsAffected = -2; DbDataReader sdr = null; ConnBean coSlave = null; if (!IsOpenTrans)// && _IsAllowRecordSql { coSlave = ConnObj.GetSlave(); } else if (UsingConnBean.IsSlave)// && 事务操作时,如果在从库,切回主库 { ResetConn(ConnObj.Master); } if (OpenCon(coSlave, AllowConnLevel.MaterBackupSlave)) { try { CommandBehavior cb = CommandBehavior.CloseConnection; if (_IsRecordDebugInfo)//外部SQL,带表结构返回 { cb = IsOpenTrans ? CommandBehavior.KeyInfo : CommandBehavior.CloseConnection | CommandBehavior.KeyInfo; } else if (IsOpenTrans) { cb = CommandBehavior.Default;//避免事务时第一次拿表结构链接被关闭。 } sdr = _com.ExecuteReader(cb); if (sdr != null) { RecordsAffected = sdr.RecordsAffected; } } catch (DbException err) { string msg = "ExeDataReader():" + err.Message; DebugInfo.Append(msg + AppConst.BR); RecordsAffected = -2; WriteError(msg + (isProc ? "" : AppConst.BR + GetParaInfo(cmdText))); } //finally //{ // if (coSlave != null) // { // ChangeConn(connObject.Master);//恢复链接。 // } //} } return sdr; } public DbDataReader ExeDataReader(string cmdText, bool isProc) { SetCommandText(cmdText, isProc); DbDataReader sdr = null; //if (_dbOperator.ContainsKey(DataBase)) //{ // if (!CheckIsConcurrent()) // { // // dbOperator[DataBase] = true; // sdr = ExeDataReaderSQL(cmdText, isProc); // // dbOperator[DataBase] = false; // } //} //else //{ sdr = ExeDataReaderSQL(cmdText, isProc); //} WriteTime(); return sdr; } private int ExeNonQuerySQL(string cmdText, bool isProc) { RecordsAffected = -2; if (IsOpenTrans || UsingConnBean.IsSlave)// && 事务操作时,如果在从库,切回主库 { ResetConn(ConnObj.Master); } if (OpenCon())//这里也会切库了,同时设置了10秒切到主库。 { try { if (UsingConnBean.ConnString != ConnObj.Master.ConnString) { // recordsAffected = -2;//从库不允许执行非查询操作。 string msg = "You can't do ExeNonQuerySQL() on Slave DataBase!"; DebugInfo.Append(msg + AppConst.BR); WriteError(msg + (isProc ? "" : AppConst.BR + GetParaInfo(cmdText))); } else { if (isUseUnsafeModeOnSqlite && !isProc && DataBaseType == DataBaseType.SQLite && !IsOpenTrans) { _com.CommandText = "PRAGMA synchronous=Off;" + _com.CommandText; } RecordsAffected = _com.ExecuteNonQuery(); } } catch (DbException err) { string msg = "ExeNonQuery():" + err.Message; DebugInfo.Append(msg + AppConst.BR); //recordsAffected = -2; WriteError(msg + (isProc ? "" : AppConst.BR + GetParaInfo(cmdText))); } finally { if (!IsOpenTrans) { CloseCon(); } } } return RecordsAffected; } public int ExeNonQuery(string cmdText, bool isProc) { SetCommandText(cmdText, isProc); int result = ExeNonQuerySQL(cmdText, isProc); WriteTime(); return result; } private object ExeScalarSQL(string cmdText, bool isProc) { RecordsAffected = -2; object returnValue = null; ConnBean coSlave = null; //mssql 有 insert into ...select 操作。 bool isSelectSql = !IsOpenTrans && !cmdText.ToLower().TrimStart().StartsWith("insert ");//&& _IsAllowRecordSql bool isOpenOK; if (isSelectSql) { coSlave = ConnObj.GetSlave(); isOpenOK = OpenCon(coSlave, AllowConnLevel.MaterBackupSlave); } else { if (UsingConnBean.IsSlave) // 如果是在从库,切回主库。(insert ...select 操作) { ResetConn(ConnObj.Master); } isOpenOK = OpenCon(); } if (isOpenOK) { try { if (!isSelectSql && UsingConnBean.ConnString != ConnObj.Master.ConnString) { RecordsAffected = -2;//从库不允许执行非查询操作。 string msg = "You can't do ExeScalarSQL(with transaction or insert) on Slave DataBase!"; DebugInfo.Append(msg + AppConst.BR); WriteError(msg + (isProc ? "" : AppConst.BR + GetParaInfo(cmdText))); } else { returnValue = _com.ExecuteScalar(); RecordsAffected = returnValue == null ? 0 : 1; } } catch (DbException err) { RecordsAffected = -2; string msg = "ExeScalar():" + err.Message; DebugInfo.Append(msg + AppConst.BR); WriteError(msg + (isProc ? "" : AppConst.BR + GetParaInfo(cmdText))); } finally { if (!IsOpenTrans) { CloseCon(); } } } return returnValue; } public object ExeScalar(string cmdText, bool isProc) { SetCommandText(cmdText, isProc); object returnValue = null; //if (_dbOperator.ContainsKey(DataBase)) //{ // if (!CheckIsConcurrent()) // { // returnValue = ExeScalarSQL(cmdText, isProc); // } //} //else //{ returnValue = ExeScalarSQL(cmdText, isProc); //} WriteTime(); return returnValue; } /* private DataTable ExeDataTableSQL(string cmdText, bool isProc) { DbDataAdapter sdr = _fac.CreateDataAdapter(); sdr.SelectCommand = _com; DataTable dataTable = null; if (OpenCon()) { try { dataTable = new DataTable(); recordsAffected = sdr.Fill(dataTable); } catch (DbException err) { recordsAffected = -2; WriteError("ExeDataTable():" + err.Message + (isProc ? "" : AppConst.BR + GetParaInfo(cmdText))); } finally { sdr.Dispose(); if (!isOpenTrans) { CloseCon(); } } } return dataTable; } public DataTable ExeDataTable(string cmdText, bool isProc) { SetCommandText(cmdText, isProc); DataTable dataTable = null; //if (_dbOperator.ContainsKey(DataBase)) //{ // if (!CheckIsConcurrent()) // { // dataTable = ExeDataTableSQL(cmdText, isProc); // } //} //else //{ dataTable = ExeDataTableSQL(cmdText, isProc); //} WriteTime(); return dataTable; } */ } /// /// 链接管理 /// internal abstract partial class DalBase { int threadCount = 0; /// /// 测试链接 /// /// 1:只允许当前主链接;2:允许主备链接;3:允许主备从链接 /// internal bool TestConn(AllowConnLevel allowLevel) { threadCount = 0; openOKFlag = -1; ConnObject obj = ConnObj; if (obj.Master != null && obj.Master.IsOK && (int)allowLevel >= 1) { threadCount++; Thread thread = new Thread(new ParameterizedThreadStart(TestOpen)); thread.Start(obj.Master); } Thread.Sleep(30); if (openOKFlag == -1 && obj.BackUp != null && obj.BackUp.IsOK && (int)allowLevel >= 2) { threadCount++; Thread.Sleep(30); Thread thread = new Thread(new ParameterizedThreadStart(TestOpen)); thread.Start(obj.BackUp); } if (openOKFlag == -1 && obj.Slave != null && obj.Slave.Count > 0 && (int)allowLevel >= 3) { for (int i = 0; i < obj.Slave.Count; i++) { Thread.Sleep(30); if (openOKFlag == -1 && obj.Slave[i].IsOK) { threadCount++; Thread thread = new Thread(new ParameterizedThreadStart(TestOpen)); thread.Start(obj.Slave[i]); } } } int sleepTimes = 0; while (openOKFlag == -1) { sleepTimes += 30; if (sleepTimes > 3000 || threadCount == errorCount) { break; } Thread.Sleep(30); } if (openOKFlag == -1 && DebugInfo.Length == 0) { if (obj.Master != null && !string.IsNullOrEmpty(obj.Master.ErrorMsg)) { DebugInfo.Append(obj.Master.ErrorMsg); } } return openOKFlag == 1; } private int openOKFlag = -1; private int errorCount = 0; private void TestOpen(object para) { ConnBean connBean = para as ConnBean; if (connBean != null && connBean.IsOK && openOKFlag == -1) { if (connBean.TryTestConn() && openOKFlag != 1) { openOKFlag = 1; _Version = connBean.Version;//设置版本号。 ResetConn(connBean);//切到正常的去。 } else { errorCount++; DebugInfo.Append(connBean.ErrorMsg); } } } /// /// 切换链接 /// private bool ResetConn(ConnBean cb)//, bool isAllowReset { if (cb != null && cb.IsOK && _con != null && _con.State != ConnectionState.Open && UsingConnBean.ConnString != cb.ConnString) { UsingConnBean = cb; _con.ConnectionString = cb.ConnString; return true; } return false; } /// /// 打开链接,只切主备(同时有N秒的主库定位) /// /// internal bool OpenCon() { ConnBean master = ConnObj.Master; if (!master.IsOK && ConnObj.BackUp != null && ConnObj.BackUp.IsOK) { master = ConnObj.BackUp; ConnObj.InterChange();//主从换位置 } if (master.IsOK || (ConnObj.BackUp != null && ConnObj.BackUp.IsOK)) { bool result = OpenCon(master, AllowConnLevel.MasterBackup); if (result && _IsRecordDebugInfo) { ConnObj.SetFocusOnMaster(); //isAllowResetConn = false; } return result; } if (!DebugInfo.ToString().EndsWith(master.ErrorMsg)) { DebugInfo.Append(master.ErrorMsg); } return false; } /// /// 打开链接,允许切从。 /// internal bool OpenCon(ConnBean cb, AllowConnLevel leve) { try { if (cb == null) { cb = UsingConnBean; if (IsOpenTrans && cb.IsSlave) { ResetConn(ConnObj.Master); } } if (!cb.IsOK) { if ((int)leve > 1 && ConnObj.BackUp != null && ConnObj.BackUp.IsOK) { ResetConn(ConnObj.BackUp);//重置链接。 ConnObj.InterChange();//主从换位置 return OpenCon(ConnObj.Master, leve); } else if ((int)leve > 2) { //主挂了,备也挂了(因为备会替主) ConnBean nextSlaveBean = ConnObj.GetSlave(); if (nextSlaveBean != null) { ResetConn(nextSlaveBean);//重置链接。 return OpenCon(nextSlaveBean, leve); } } } else if (!IsOpenTrans && cb != UsingConnBean && ConnObj.IsAllowSlave())//&& isAllowResetConn { ResetConn(cb);//,_IsAllowRecordSql只有读数据错误才切,表结构错误不切? } if (UsingConnBean.IsOK) { Open();//异常抛 } else { WriteError(UsingConnBean.ConnDataBaseType + ".OpenCon():" + UsingConnBean.ErrorMsg); } if (IsRecordDebugInfo && _watch != null) { _watch.Start(); } return UsingConnBean.IsOK; } catch (DbException err) { UsingConnBean.IsOK = false; UsingConnBean.ErrorMsg = err.Message; return OpenCon(null, leve); } } protected virtual void AfterOpen() { } private void Open() { if (_con.State == ConnectionState.Closed) { if (DataBaseType == DataBaseType.Sybase) { _com.Connection = _con;//重新赋值(Sybase每次Close后命令的Con都丢失) } _con.Open(); AfterOpen(); //if (useConnBean.ConfigName == "Conn") //{ //System.Console.WriteLine(useConnBean.ConfigName); //} } if (IsOpenTrans) { if (_tran == null) { _tran = _con.BeginTransaction(TranLevel); _com.Transaction = _tran; } else if (_tran.Connection == null) { //ADO.NET Bug:在 guid='123' 时不抛异常,但自动关闭链接,不关闭对象,会引发后续的业务不在事务中。 Dispose(); Error.Throw("Transation:Last execute command has syntax error:" + DebugInfo.ToString()); } } } /* private bool OpenConBak() { try { if (connObject.ProviderName == connObject.ProviderNameBak)//同种数据库链接。 { conn = connObject.ConnBak;//切换到备用。 _con.ConnectionString = conn;//切换到备用。 Open(); //交换主从链接 connObject.ExchangeConn(); if (IsAllowRecordSql) { _watch.Start(); } return true; } else { connObject.ExchangeConn();//不同种,切换链接后,本次操作直接抛异常 } } catch (DbException err) { WriteError("OpenConBak():" + err.Message); } return false; } */ internal void CloseCon() { try { if (_con.State != ConnectionState.Closed) { if (_tran != null) { IsOpenTrans = false; if (_tran.Connection != null) { _tran.Commit(); } _tran = null; } _con.Close(); } } catch (DbException err) { WriteError("CloseCon():" + err.Message); } } } }