#region using System; using System.Data; using System.Data.Common; using CSFrameworkV5.Common; using CSFrameworkV5.Core; using CSFrameworkV5.Interfaces; #endregion namespace CSFrameworkV5.DataAccess { /// /// 业务单据数据层基类 /// public class dalBaseBusiness : dalBase { /// /// true:提交数据前去左右空格、全角转半角,若设置此值会有性能损失! /// protected bool _ReplaceString = false; /// /// 主表主键 /// protected string _SummaryKeyName = ""; /// /// 主表表名 /// protected string _SummaryTableName = ""; /// /// 设置为受保护的构造器,不允许外部实例化 /// /// protected dalBaseBusiness(Loginer loginer) : base(loginer) { // } /// /// 单据审核/批准功能 /// /// 业务主键 /// 审核标记:Y/N /// 审核人 /// 审核日期 public virtual bool ApprovalBusiness(QueryApproval P) { var sp = _Database.CreateSqlProc("usp_ApprovalBusiness"); sp.AddParam("TableName", DbType.String, 50, P.TableName); sp.AddParam("KeyFieldName", DbType.String, 50, P.KeyFieldName); sp.AddParam("KeyValue", DbType.String, 50, P.KeyValue); sp.AddParam("FlagApp", DbType.String, 250, P.FlagApp); sp.AddParam("AppUser", DbType.String, 250, _Loginer.Account); sp.AddParam("AppNAME", DbType.String, 250, P.AppNAME); var i = _Database.ExecuteCommand(sp.Command); return true; } /// /// 调用后台数据库执行SQL /// /// 业务主键 /// 审核标记:Y/N /// 审核人 /// 审核日期 public virtual DataTable GetDataTable(string SQL) { return _Database.GetTable(SQL); } /// /// 调用后台数据库执行SQL /// /// 业务主键 /// 审核标记:Y/N /// 审核人 /// 审核日期 public virtual bool InstDataSet(DataTable SQL, string NAME) { return _Database.InstDataSet(SQL, NAME); } /// /// 调用后台数据库执行SQL返回INT /// /// 业务主键 /// 审核标记:Y/N /// 审核人 /// 审核日期 public virtual int GetExecute(string SQL) { return _Database.ExecuteSQL(SQL); } /// /// 检查业务数据是否存在 /// /// 业务单据主键 /// public virtual bool CheckNoExists(string keyValue) { var sql = $"SELECT COUNT(*) AS Total FROM {_SummaryTableName} WHERE {_SummaryKeyName}={_Database.ParamSymboName}KEY"; var cmd = _Database.CreateCommand(sql); cmd.AddParam("KEY", DbType.String, keyValue); var o = _Database.ExecuteScalar(cmd.Command); return ConvertEx.ToInt(o) > 0; } /// /// 根据表名获取该表的SQL命令生成器 /// /// 表名 /// protected virtual IGenerateSqlCommand CreateSqlGenerator( DataTable table) { return null; } /// /// 获取业务单据流水号 /// protected virtual string GetNumber(DbTransaction tran) { return string.Empty; } /// /// 业务单据的保存数据方法 /// public virtual SaveResult Update(DataSet data) { /*********************************************************************************** * 数据更新注意事项: * * 1. 参数:data 是业务数据所有更新的数据表. DataSet的第1张表为主表, * 第2,3,...n张表为明细表或其它数据。 * * 2. 主表的主键可能是系统自动生成或用户输入,若是用户输入,必须在保存前检查唯一性. * 当_UpdateSummaryKeyMode=UpdateKeyMode.None,系统生成主键,通常是用户输入主键值. * * 3. 首先获取主表主键的值,再调用UpdateDetailKey()方法更新所有明细表的外键值. * ************************************************************************************/ var mResult = SaveResult.CreateDefault(); //预设保存结果 var mGUID = string.Empty; //生成的GUID号码 var mDocNo = string.Empty; //生成的流水号 var mCurrentTable = string.Empty; //当前正在更新的表 _CurrentDataSet4Update = data; //临时存储当前数据集 //非用户手动事务模式,预设启用事务 if (_UserManualControlTrans == false) BeginTransaction(); if (_CurrentTrans == null) throw new Exception("用户手动控制事务模式下,您没有启用事务!"); try { //枚举所有资料表,逐一提交 foreach (DataTable dt in data.Tables) { _CurrentTable4Update = dt; //临时存储当前数据表 if (dt.GetChanges() == null) continue; //仅处理有修改的资料表 if (_ReplaceString) FieldFitString.FitString(dt); //去左右空格,将大写符号转小写 //根据表名获取SQL命令生成器,派生类必须重写CreateSqlGenerator方法 var gen = CreateSqlGenerator(dt); if (gen == null) throw new CustomException("创建SQL命令生成器失败!表名:" + dt.TableName); mCurrentTable = dt.TableName; //若当前更新的是主表(模型的特性参数isSummaryTable=True),取主表的主键,用于设置明细表的外键 if (gen.IsSummary()) { UpdateSummaryKey(_CurrentTrans, dt, _UpdateSummaryKeyMode, gen.GetPrimaryFieldName(), ref mGUID, gen.GetDocNoFieldName(), ref mDocNo); //生成主键 mResult.GUID = mGUID; mResult.DocNo = mDocNo; } else { //更新明细表的外键 if (_UpdateSummaryKeyMode == UpdateKeyMode.OnlyDocumentNo) //单号 UpdateDetailKey(dt, gen.GetForeignFieldName(), mDocNo); else if (_UpdateSummaryKeyMode == UpdateKeyMode.OnlyGuid) //GUID UpdateDetailKey(dt, gen.GetForeignFieldName(), mGUID); } var adp = _Database.CreateDataAdapter(AdapterRowUpdatingEvent); adp.UpdateCommand = gen.GetUpdateCommand(_CurrentTrans); adp.InsertCommand = gen.GetInsertCommand(_CurrentTrans); adp.DeleteCommand = gen.GetDeleteCommand(_CurrentTrans); adp.AcceptChangesDuringUpdate = false; //提交数据后保留原始数据,用于保存修改日志,对比原始值2017-02-26 adp.Update(dt); adp.Dispose(); } if (_UserManualControlTrans == false) CommitTransaction(); //提交事务 } catch (DBConcurrencyException ex) { if (_UserManualControlTrans == false) RollbackTransaction(); //回滚事务 mResult.Description = "并发操作!其他用户已更新了源数据!Event:DAL.Update()"; //保存结果设置异常消息 mResult.Result = 0; throw new Exception(mResult.Description); } catch (Exception ex) { if (_UserManualControlTrans == false) RollbackTransaction(); //回滚事务 mResult.Description = "更新数据发生错误!Event:Update(),Table:" + mCurrentTable + ex.Message; mResult.Result = 0; throw new Exception(mResult.Description); } return mResult; //返回保存结果 } /// /// 更新明细表的外键 /// /// 明细表数据 /// 外键字段名称,对应主表的主键字段名称,如:PONO /// 外键值,对应主表的主键值,如:PO999999 protected virtual void UpdateDetailKey(DataTable detail, string foreignFieldName, string foreignKeyValue) { if (detail == null) throw new Exception("明细表不能为空(null)!"); if (string.IsNullOrEmpty(foreignFieldName)) throw new Exception("明细表没指定外键, 请检查表模型定义!"); foreach (DataRow row in detail.Rows) //仅新增记录才需要更新外键,注意状态的使用 if (row.RowState == DataRowState.Added) { if (string.IsNullOrEmpty(foreignKeyValue)) throw new Exception("外键值为空!"); row[foreignFieldName] = foreignKeyValue; } } /// /// 更新主表主键 /// /// 当前数据库事务 /// 主表数据 /// 主键数据更新类型 /// 主键字段名称,对应模型字段特性定义为isPrimaryKey=true的字段 /// 若mode=OnlyGuid,自动生成32位的GUID作为主键值 /// 单据号码的字段名,对应模型字段特性定义为docNoFieldName=true的字段 /// 若mode=OnlyDocumentNo,自动生成单据号码 protected virtual void UpdateSummaryKey(DbTransaction tran, DataTable summary, UpdateKeyMode mode, string keyFieldName, ref string GUID, string docNoFieldName, ref string docNo) { //主表取DataTable的第一条记录 var row = summary.Rows[0]; //如果未指定单号更新类型则取旧的单号. if (row.RowState != DataRowState.Added || mode == UpdateKeyMode.None) //取旧的单号 { if (summary.Columns[keyFieldName] != null) GUID = row[keyFieldName].ToStringEx(); if (summary.Columns[docNoFieldName] != null) docNo = row[docNoFieldName].ToStringEx(); return; } //新增记录,更新主键的值 if (row.RowState == DataRowState.Added) { //注意状态的使用,只有在新增状态下才更新单号 if (mode == UpdateKeyMode.OnlyGuid) { if (keyFieldName == "") throw new Exception("没有设定主键,检查模型类的参数定义!"); GUID = Guid.NewGuid().ToStringEx().Replace("-", ""); row[keyFieldName] = GUID; } if (mode == UpdateKeyMode.OnlyDocumentNo) { if (docNoFieldName == "") throw new Exception("没有设定单号主键,检查模型类的参数定义!"); docNo = GetNumber(tran); //调用模板方法获取单据号码,派生类必须重写GetNumber方法 row[docNoFieldName] = docNo; } } } } /// /// 更新主表的主键模式 /// public enum UpdateKeyMode { /// /// 未指定.单据号码由用户手工输入 /// None, /// /// 自动生成流水号 /// OnlyDocumentNo, /// /// 自动生成36位GUID(全球唯一标识) /// OnlyGuid } }