程序员的自我救赎---1.4.1:核心框架讲解(DAL)
《核心框架讲解》
之前在《Winner2.0框架解决方案命分层规范》 有讲到过Winner框架最重要的三个程序集分别是:
DataAcces: 继承 “DataAccessBase”
Entities: 没有单独可基础的核心框架类,可直接引用工具类
Facade: 继承:“FacadeBase”
APP(MVC): 继承:“TopControllerBase”
我们重点讲一下,“DataAccessBase”,“FacadeBase”,“TopControllerBase” 这三个基类。
其实前面在 《代码生成器的使用》和《事务的使用》两篇文章中就分别讲到过“DataAccessBase” 和 "FacadeBase" 。
我们先来分析DataAccessBase我们看一下在我们真实开发中关于数据库访问代码的的使用。建议复制到VS中去看,不然在博客中确实不好看懂。
/***********************代码生成器生成的 充值表数据操作************ * * Data Access Layer Of Winner Framework * FileName : Tdct_Recharge.generate.cs * Version : V 1.1.0 * Author:架构师 曾杰(Jie) * E_Mail : 6e9e@163.com * Tencent QQ:554044818 * Blog : http://www.cnblogs.com/fineblog/ * CreateTime : 2017-10-11 18:06:04 * ***************************************************/ using System; using System.Collections.Generic; using System.Data; using System.Text; using Winner.Framework.Core; using Winner.Framework.Core.DataAccess; using Winner.Framework.Core.DataAccess.Oracle; using Winner.Framework.Utils; using Winner.DCTS.Entities; namespace Winner.DCTS.DataAccess { /// <summary> /// Data Access Layer Object Of Tdct_Recharge /// </summary> public partial class Tdct_Recharge : DataAccessBase { #region 默认构造 public Tdct_Recharge() : base() { } public Tdct_Recharge(DataRow dataRow) : base(dataRow) { } #endregion 默认构造 #region 对应表结构的常量属性 public const string _RECHARGE_ID="RECHARGE_ID"; public const string _PAY_TYPE="PAY_TYPE"; public const string _AMOUNT="AMOUNT"; public const string _ACCOUNT="ACCOUNT"; public const string _USER_ID="USER_ID"; public const string _STATUS="STATUS"; public const string _CREATE_TIME="CREATE_TIME"; public const string _REMARKS="REMARKS"; public const string _TABLENAME="Tdct_Recharge"; #endregion 对应表结构的常量属性 #region 公开属性 /// <summary> /// 充值记录ID /// [default:0] /// </summary> public int RechargeId { get { return Convert.ToInt32(DataRow[_RECHARGE_ID]); } set { setProperty(_RECHARGE_ID,value); } } /// <summary> /// 支付类型 1支付宝,2银行卡,3微信 /// [default:0] /// </summary> public int PayType { get { return Convert.ToInt32(DataRow[_PAY_TYPE]); } set { setProperty(_PAY_TYPE,value); } } /// <summary> /// 充值金额 /// [default:0] /// </summary> public decimal Amount { get { return Convert.ToDecimal(DataRow[_AMOUNT]); } set { setProperty(_AMOUNT,value); } } /// <summary> /// 充值账号 /// [default:string.Empty] /// </summary> public string Account { get { return DataRow[_ACCOUNT].ToString(); } set { setProperty(_ACCOUNT,value); } } /// <summary> /// 充值用户 /// [default:0] /// </summary> public int UserId { get { return Convert.ToInt32(DataRow[_USER_ID]); } set { setProperty(_USER_ID,value); } } /// <summary> /// 充值状态 0等待银行到账,1已经到账,2操作失败 /// [default:0] /// </summary> public RechargeStatus Status { get { return (RechargeStatus)Convert.ToInt32(DataRow[_STATUS]); } set { setProperty(_STATUS,(int)value); } } /// <summary> /// 充值时间 /// [default:new DateTime()] /// </summary> public DateTime CreateTime { get { return Convert.ToDateTime(DataRow[_CREATE_TIME].ToString()); } } /// <summary> /// 备注 /// [default:string.Empty] /// </summary> public string Remarks { get { return DataRow[_REMARKS].ToString(); } set { setProperty(_REMARKS,value); } } #endregion 公开属性 #region 私有成员 protected override string TableName { get { return _TABLENAME; } } protected override DataRow BuildRow() { DataTable dt = new DataTable(_TABLENAME); dt.Columns.Add(_RECHARGE_ID, typeof(int)).DefaultValue = 0; dt.Columns.Add(_PAY_TYPE, typeof(int)).DefaultValue = 0; dt.Columns.Add(_AMOUNT, typeof(decimal)).DefaultValue = 0; dt.Columns.Add(_ACCOUNT, typeof(string)).DefaultValue = string.Empty; dt.Columns.Add(_USER_ID, typeof(int)).DefaultValue = 0; dt.Columns.Add(_STATUS, typeof(int)).DefaultValue = 0; dt.Columns.Add(_CREATE_TIME, typeof(DateTime)).DefaultValue = new DateTime(); dt.Columns.Add(_REMARKS, typeof(string)).DefaultValue = string.Empty; return dt.NewRow(); } #endregion 私有成员 #region 常用方法 protected bool DeleteByCondition(string condition) { string sql = @"DELETE FROM TDCT_RECHARGE WHERE " + condition; return base.DeleteBySql(sql); } public bool Delete(int rechargeId) { string condition = "RECHARGE_ID=:RECHARGE_ID"; AddParameter(_RECHARGE_ID, rechargeId); return DeleteByCondition(condition); } public bool Delete() { string condition = "RECHARGE_ID=:RECHARGE_ID"; AddParameter(_RECHARGE_ID, RechargeId); return DeleteByCondition(condition); } public bool Insert() { RechargeId=base.GetSequence("SELECT SEQ_TDCT_RECHARGE.NEXTVAL FROM DUAL"); string sql=@"INSERT INTO TDCT_RECHARGE( RECHARGE_ID, PAY_TYPE, AMOUNT, ACCOUNT, USER_ID, STATUS, REMARKS) VALUES( :RECHARGE_ID, :PAY_TYPE, :AMOUNT, :ACCOUNT, :USER_ID, :STATUS, :REMARKS)"; AddParameter(_RECHARGE_ID,DataRow[_RECHARGE_ID]); AddParameter(_PAY_TYPE,DataRow[_PAY_TYPE]); AddParameter(_AMOUNT,DataRow[_AMOUNT]); AddParameter(_ACCOUNT,DataRow[_ACCOUNT]); AddParameter(_USER_ID,DataRow[_USER_ID]); AddParameter(_STATUS,DataRow[_STATUS]); AddParameter(_REMARKS,DataRow[_REMARKS]); return base.InsertBySql(sql); } public bool Update() { return UpdateByCondition(string.Empty); } protected bool UpdateByCondition(string condition) { //移除主键标记 ChangePropertys.Remove(_RECHARGE_ID); if (ChangePropertys.Count == 0) { return true; } StringBuilder sql = new StringBuilder(); sql.AppendLine("UPDATE TDCT_RECHARGE SET"); while (ChangePropertys.MoveNext()) { sql.AppendFormat(" {0}{1}=:{1} ", (ChangePropertys.CurrentIndex == 0 ? string.Empty : ","), ChangePropertys.Current); AddParameter(ChangePropertys.Current, DataRow[ChangePropertys.Current]); } sql.AppendLine(" WHERE RECHARGE_ID=:RECHARGE_ID"); AddParameter(_RECHARGE_ID, DataRow[_RECHARGE_ID]); if (!string.IsNullOrEmpty(condition)) sql.AppendLine(" AND " + condition); bool result = base.UpdateBySql(sql.ToString()); ChangePropertys.Clear(); return result; } protected bool SelectByCondition(string condition) { string sql = @" SELECT RECHARGE_ID, PAY_TYPE, AMOUNT, ACCOUNT, USER_ID, STATUS, CREATE_TIME, REMARKS FROM TDCT_RECHARGE WHERE " + condition; return base.SelectBySql(sql); } public bool SelectByPK(int rechargeId) { string condition = "RECHARGE_ID=:RECHARGE_ID"; AddParameter(_RECHARGE_ID, rechargeId); return SelectByCondition(condition); } public Tnet_User Get_Tnet_User_ByUserId() { Tnet_User da=new Tnet_User(); da.SelectByPK(UserId); return da; } #endregion 常用方法 //提示:此类由代码生成器生成,如无特殊情况请不要更改。如要扩展请在外部同名类中扩展 } /// <summary> /// Data Access Layer Object Collection Of Tdct_Recharge /// </summary> public partial class Tdct_RechargeCollection : DataAccessCollectionBase { #region 默认构造 public Tdct_RechargeCollection() { } public Tdct_RechargeCollection(DataTable table) : base(table) { } #endregion 默认构造 #region 私有成员 protected override DataAccessBase GetItemByIndex(int index) { return new Tdct_Recharge(DataTable.Rows[index]); } protected override DataTable BuildTable() { return new Tdct_Recharge().CloneSchemaOfTable(); } protected override string TableName { get { return Tdct_Recharge._TABLENAME; } } protected bool ListByCondition(string condition) { string sql = @" SELECT RECHARGE_ID, PAY_TYPE, AMOUNT, ACCOUNT, USER_ID, STATUS, CREATE_TIME, REMARKS FROM TDCT_RECHARGE WHERE " + condition; return base.ListBySql(sql); } public bool ListByRechargeId(int rechargeId) { string condition = "RECHARGE_ID=:RECHARGE_ID"; AddParameter(Tdct_Recharge._RECHARGE_ID, rechargeId); return ListByCondition(condition); } public bool ListAll() { string condition = "1=1"; return ListByCondition(condition); } public bool DeleteByCondition(string condition) { string sql = "DELETE FROM TDCT_RECHARGE WHERE " + condition; return DeleteBySql(sql); } #endregion #region 公开成员 public Tdct_Recharge this[int index] { get { return new Tdct_Recharge(DataTable.Rows[index]); } } public bool DeleteAll() { return this.DeleteByCondition(string.Empty); } #region Linq public Tdct_Recharge Find(Predicate<Tdct_Recharge> match) { foreach (Tdct_Recharge item in this) { if (match(item)) return item; } return null; } public Tdct_RechargeCollection FindAll(Predicate<Tdct_Recharge> match) { Tdct_RechargeCollection list = new Tdct_RechargeCollection(); foreach (Tdct_Recharge item in this) { if (match(item)) list.Add(item); } return list; } public bool Contains(Predicate<Tdct_Recharge> match) { foreach (Tdct_Recharge item in this) { if (match(item)) return true; } return false; } public bool DeleteAt(Predicate<Tdct_Recharge> match) { BeginTransaction(); foreach (Tdct_Recharge item in this) { item.ReferenceTransactionFrom(Transaction); if (!match(item)) continue; if (!item.Delete()) { Rollback(); return false; } } Commit(); return true; } #endregion Linq #endregion //提示:此类由代码生成器生成,如无特殊情况请不要更改。如要扩展请在外部同名类中扩展 } }
/***********************由技术员编写**************************** * * Data Access Layer Of Winner Framework * FileName : Tdct_Recharge.extension.cs * Version : V 1.1.0 * Author:架构师 曾杰(Jie) * E_Mail : 6e9e@163.com * Tencent QQ:554044818 * Blog : http://www.cnblogs.com/fineblog/ * CreateTime : 2017-10-11 17:55:39 * ***************************************************/ using System; using System.Collections.Generic; using System.Data; using System.Text; using Winner.Framework.Core; using Winner.Framework.Core.DataAccess; using Winner.Framework.Core.DataAccess.Oracle; using Winner.Framework.Utils; using Winner.DCTS.Entities; using Winner.DCTS.Entities.enums; namespace Winner.DCTS.DataAccess { /// <summary> /// Data Access Layer Object Of Tdct_Recharge /// </summary> public partial class Tdct_Recharge : DataAccessBase { //Custom Extension Class public bool Delete(int rechargeId, int userId) { string condition = "RECHARGE_ID=:RECHARGE_ID AND USER_ID=:USER_ID AND STATUS=0"; AddParameter(_RECHARGE_ID, rechargeId); AddParameter(_USER_ID, userId); return DeleteByCondition(condition); } public bool SelectById(int rechargeId) { string condition = "RECHARGE_ID=:RECHARGE_ID AND STATUS=:STATUS"; AddParameter(_RECHARGE_ID, rechargeId); AddParameter(_STATUS, RechargeStatus.等待充值.GetHashCode()); return SelectByCondition(condition); } } /// <summary> /// Data Access Layer Object Collection Of Tdct_Recharge /// </summary> public partial class Tdct_RechargeCollection : DataAccessCollectionBase { public bool ListByUserId(int userId) { string condition = " USER_ID = :USER_ID ORDER BY RECHARGE_ID DESC "; AddParameter("USER_ID", userId); return ListByCondition(condition); } /// <summary> /// 查询列表 /// </summary> /// <param name="status"></param> /// <param name="paytype"></param> /// <returns></returns> public bool ListByKeyWord(string keyword,int status,int paytype) { string sql = "SELECT TR.*,VU.USER_CODE,VU.USER_NAME FROM TDCT_RECHARGE TR JOIN VNET_USER VU ON TR.USER_ID=VU.USER_ID WHERE 1=1 "; if(!string.IsNullOrEmpty(keyword)) { sql += " AND( TR.ID=:ID"; sql += " OR VU.USER_NAME=:USERNAME)"; AddParameter("ID", keyword); AddParameter("USERNAME", keyword); } if (status>=0) { sql += " AND TR.STATUS=:STATUS"; AddParameter("STATUS", status); } if(paytype>=0) { sql += " AND TR.PAY_TYPE=:PAYTYPE"; AddParameter("PAYTYPE",paytype); } sql += " ORDER BY TR.RECHARGE_ID DESC"; return ListBySql(sql); } //Custom Extension Class } }
对于项目结构不理解的需要先看《Winner2.0框架解决方案命分层规范》 才能看懂,为什么我们会这样写。 我想了想关于DataAccessBase,我们还是又浅入深的去讲。
先从最最简单的 AddParameter() 这个方法开始讲起,明眼一看就知道这是一个 “参数化” 传值,至于参数化传值有什么好处就自行百度吧,我看到很多公司的项目本身可以封装
一层参数化方法,但是却“痴迷”于用 存储过程 却解决参数化问题,然后放大存储过程“预编译”的优势,其实这个没有必要。无论是哪种数据库都是支持C# 参数化 操作的。
我们看看AddParameter() 这个方法是怎么实现的:
#region AddParameter /// <summary> /// 添加DAO.NET的SqlCommand命令对象参数 /// </summary> /// <param name="parameterName">参数名称</param> /// <param name="value">参数值</param> protected DbParameter AddParameter(string parameterName, object value) { if (string.IsNullOrEmpty(parameterName)) { throw new Exception("传入的参数名称为空"); } if (string.IsNullOrEmpty(DBFactory.ParameterSign)) { throw new Exception("未指定参数符合(例如:Oracle::、SqlServer:@....)"); } if (!parameterName.StartsWith(this.DBFactory.ParameterSign)) { parameterName = this.DBFactory.ParameterSign + parameterName; } DbParameter parm = DBFactory.DBProvider.CreateParameter(); parm.ParameterName = parameterName; parm.Value = value; AddParameter(parm); return parm; } /// <summary> /// 添加DAO.NET的SqlCommand命令对象参数 /// </summary> /// <param name="parameterName">参数名称</param> /// <param name="direction">参数模式</param> /// <param name="func">处理返回值函数</param> /// <example> /// <code> /// <![CDATA[ /// //接收的参数 /// int id; /// AddParameter("Id", ParameterDirection.ReturnValue, value => id = value.ConvertTo<int>()); /// ]]> /// </code> /// </example> protected DbParameter AddParameter(string parameterName, ParameterDirection direction, Func<DbParameter, object> func) { if (string.IsNullOrEmpty(parameterName)) { throw new Exception("传入的参数名称为空"); } if (string.IsNullOrEmpty(DBFactory.ParameterSign)) { throw new Exception("未指定参数符合(例如:Oracle::、SqlServer:@....)"); } if (!parameterName.StartsWith(this.DBFactory.ParameterSign)) { parameterName = this.DBFactory.ParameterSign + parameterName; } DbParameter parm = DBFactory.DBProvider.CreateParameter(); parm.ParameterName = parameterName; parm.Direction = direction; parm.Value = string.Empty; parm.Size = 2000; AddParameter(parm); Parameters.OutValuesParamerer.Add(parm, func); return parm; } /// <summary> /// 添加DAO.NET的SqlCommand命令对象参数 /// </summary> /// <param name="parameterName">参数名称</param> /// <param name="func">处理返回值函数</param> /// <example> /// <code> /// <![CDATA[ /// //接收的参数 /// int id; /// AddParameter("Id", value => id = value.ConvertTo<int>()); /// ]]> /// </code> /// </example> protected DbParameter AddParameter(string parameterName, Func<DbParameter, object> func) { return AddParameter(parameterName, ParameterDirection.Output, func); } /// <summary> /// 添加DAO.NET的SqlCommand命令对象参数 /// </summary> /// <param name="parm">参数</param> protected DbParameter AddParameter(DbParameter parm) { if (this.Parameters == null) { this.Parameters = new ParameterCollection(DBFactory.DBProvider); } DbParameter item = this.Parameters.Find(s => s.ParameterName == parm.ParameterName); if (item != null) { this.Parameters.Remove(item); } this.Parameters.Add(parm); return parm; } #endregion
每种数据库语法的不同,对应的参数化符合自然不同。Sqlerver是用“@”,Oracle是用“:”。这里阿杰用了“工厂模式”来解决这个问题。
这里我们贴出OracleProvider.cs类来作为示例:
using System.Configuration; using System.Data.Common; using System.Data.OracleClient; using Winner.Framework.Core.CustomException; using Winner.Framework.Utils; namespace Winner.Framework.Core.DataAccess.Oracle { /// <summary> /// Oracle 配置工厂 /// </summary> internal class OracleProvider : DBFactory { private OracleProvider() { } //static OracleFactory() { } /// <summary> /// Oracle操作工厂实例 /// </summary> public static readonly OracleProvider Instance = new OracleProvider(); /// <summary> /// 数据库工厂 /// </summary> internal override DbProviderFactory DBProvider { get { return OracleClientFactory.Instance; } } /// <summary> /// 数据库连接字符串 /// </summary> internal override string GetConnectionString() { ConnectionStringSettings conn = ConfigurationManager.ConnectionStrings["Winner.Framework.Oracle.ConnectionString"]; if (conn == null) { ConnectionStringException ex = new ConnectionStringException("AppConfig.ConnectionStrings节点未配置Winner.Framework.Oracle.ConnectionString连接字符串"); throw ex; } return conn.ConnectionString; } /// <summary> /// 参数标识符 /// </summary> public override string ParameterSign { get { return ":"; } } /// <summary> /// 通用数据库的SQL语句帮助类 /// </summary> public override SQLHelper SQLHelper { get { return SQLHelperProvider.Instance; } } } }
还有工厂类的代码:
using System; using System.Data.Common; using Winner.Framework.Core.CustomException; using Winner.Framework.Utils; namespace Winner.Framework.Core.DataAccess { /// <summary> /// 配置工厂 /// </summary> public abstract class DBFactory { /// <summary> /// 数据库工厂 /// </summary> internal abstract DbProviderFactory DBProvider { get; } private string _connectionString; /// <summary> /// 数据库连接字符串 /// </summary> internal string ConnectionString { get { if (string.IsNullOrEmpty(_connectionString)) { string temp = GetConnectionString(); if (temp.Empty()) { var connectionStringException = new ConnectionStringException("未获取到数据库连接字符串"); Log.Error(connectionStringException); throw connectionStringException; } if (AppSettingsProvider.ConnectionStringEncrypt) { try { temp = Winner.Framework.Encrypt.Hardware.Decode(temp); } catch (Exception ex) { Log.Warn("配置字符串:" + temp); var connectionStringException = new ConnectionStringException("数据库连接字符串解密失败,请确保硬件设备已分配权限,请联系架构师Jie[QQ:554044818]!" + ex.Message, ex); Log.Error(connectionStringException); throw connectionStringException; } } _connectionString = temp; } return _connectionString; } } /// <summary> /// 设置连接字符串 /// </summary> /// <param name="str"></param> public void SetConnectionString(string str) { _connectionString = str; } /// <summary> /// 重置连接字符串 /// </summary> public void ResetConnectionString() { _connectionString = GetConnectionString(); } /// <summary> /// 设置连接字符串 /// </summary> internal abstract string GetConnectionString(); /// <summary> /// 参数标识符号 /// </summary> public abstract string ParameterSign { get; } /// <summary> /// SQL 帮助类 /// </summary> public abstract SQLHelper SQLHelper { get; } } }
在我们团队内部属于严格要求必须使用参数化。在此之前我们会使用SQL拼接,当然框架现在也是支持参数拼接的,只是拼接在防sql注入上需要严谨对待,下面我
贴一下,框架封装的sql拼接工具类:
using System; using System.Drawing; using System.IO; using Winner.Framework.Encrypt; namespace Winner.Framework.Core.DataAccess { /// <summary> /// 通用数据库的SQL语句帮助类 /// </summary> public abstract class SQLHelper { /// <summary> /// 此类作为静态方式使用不能在其他程序集实例化 /// </summary> internal SQLHelper() { } /// <summary> /// 美化SQL,去掉\t\n\r及多余空格 /// </summary> /// <param name="sql">SQL语句</param> /// <returns></returns> public string BeautifulSql(string sql) { sql = sql.Replace("\r", " ").Replace("\t", " ").Replace("\n", " "); while (sql.IndexOf(" ") >= 0) { sql = sql.Replace(" ", " "); } sql = sql.Replace(", ", ","); return sql.Trim(); } /// <summary> /// SQL参数 敏感处理 /// </summary> /// <param name="obj"></param> /// <returns></returns> private string SQLParameterHandle(object obj) { string value = string.Empty; if (obj == null) { return string.Empty; } value = obj.ToString().Replace("\r", " ").Replace("\t", " ").Replace("\n", " "); while (value.IndexOf(" ") >= 0) { value = value.Replace(" ", " "); } return value.Replace(", ", ",").Replace("'", "''"); } /// <summary> /// 转换为SQL参数字符串,如:123 转换成 '123' /// </summary> /// <param name="obj"></param> /// <returns></returns> public string ToSQLParameter(object obj) { string value = SQLParameterHandle(obj); return string.Format("'{0}'", value); } /// <summary> /// 按时间查询的Berween And 语句,如:Age Between 15 ADN 18 /// </summary> /// <param name="columnName">根据查询的列</param> /// <param name="begTime">开始时间</param> /// <param name="endTime">结束时间</param> /// <returns>String类型的SQL语句</returns> public string ToSQLBetween(string columnName, DateTime begTime, DateTime endTime) { return string.Format(" {0} Between {1} ADN {2} ", columnName, begTime, endTime); } /// <summary> /// 转换为左Like语句,如:UserName like '%杰' /// </summary> /// <param name="columnName">列名</param> /// <param name="Value">值</param> /// <returns></returns> public string ToSQLLeftLike(string columnName, string Value) { return string.Format(" {0} like '%{1}'", columnName, SQLParameterHandle(Value)); } /// <summary> /// 转换为Like语句,如:UserName like '%杰%' /// </summary> /// <param name="columnName">列名</param> /// <param name="Value">值</param> /// <returns></returns> public string ToSQLLike(string columnName, string Value) { return string.Format(" {0} like '%{1}%'", columnName, SQLParameterHandle(Value)); } /// <summary> /// 转换为右Like语句,如:UserName like '杰%' /// </summary> /// <param name="columnName">列名</param> /// <param name="Value">值</param> /// <returns></returns> public string ToSQLRightLike(string columnName, string Value) { return string.Format(" {0} like '{1}%'", columnName, SQLParameterHandle(Value)); } #region To SQL Date /// <summary> /// 按时间查询的Berween And 语句 /// </summary> /// <param name="columnName">根据查询的列</param> /// <param name="beginDate">开始时间</param> /// <param name="endDate">结束时间</param> /// <returns>String类型的SQL语句</returns> public abstract string ToSQLBetweenDate(string columnName, DateTime beginDate, DateTime endDate); /// <summary> /// 按时间查询的Berween And 语句 /// </summary> /// <param name="columnName">根据查询的列</param> /// <param name="beginDate">开始时间</param> /// <param name="endDate">结束时间</param> /// <returns>String类型的SQL语句</returns> public abstract string ToSQLBetweenDateTime(string columnName, DateTime beginDate, DateTime endDate); /// <summary> /// 转换为SQL语句中的日期类型,格式如:2009-05-18 /// </summary> /// <param name="obj">需要转换为SQL类型的值</param> /// <returns></returns> public abstract string ToSQLDate(object obj); /// <summary> /// 转换为SQL语句类型的时间格式,如2010-03-04 12:12:12 /// </summary> /// <param name="obj">需要转换为SQL类型的值</param> /// <returns></returns> public abstract string ToSQLDateTime(object obj); /// <summary> /// 转换为当天范围SQL语句 /// </summary> /// <param name="columnName">字段名称</param> /// <param name="date">日期</param> /// <returns></returns> public abstract string ToSQLInduriteDate(string columnName, DateTime date); #endregion } }
注意这里,sqlhelp是个工具类,其中因为各个数据库的语法不同,我们部分拼接方法需要重新实现抽象类比如:ToSQLBetweenDate();
SqlServer 写法:
public override string ToSQLBetweenDate(string FieldName, DateTime beginDate, DateTime endDate) { return string.Format(" {0} Between Convert(DateTime,'{1}') and Convert(DateTime,'{2}') ", FieldName, beginDate.ToString("yyyy-MM-dd"), endDate.ToString("yyyy-MM-dd")); }
Oracle 写法:
public override string ToSQLBetweenDate(string FieldName, DateTime beginDate, DateTime endDate) { return string.Format("({0} BETWEEN TO_DATE('{1}','YYYY-MM-DD') AND TO_DATE('{2}','YYYY-MM-DD'))", FieldName, beginDate.ToString("yyyy-MM-dd"), endDate.ToString("yyyy-MM-dd")); }
关于DataAccess最重要的职责是负责与数据库通讯,当然这里我就不去讲述是如何实现的了,看了上面那个AddParameter()的讲解之后
里面贴的代码就已经讲述了,如何链接数据库。 而看了我们的SqlHelp之后自然也就能了解我们的“增删查改”是怎么实现了的,
其实很简单.
DataAccessBase :DataAccessCore
DataAccessCore:DataProviderBase
写到这里其实贴的代码多了,有点眼花缭乱的。 我们还是回归到由浅入深的思路去讲解,其实作为使用者到这里就不必过分深究如何实现“增删查改”每一个方法。
接下来我写一下关于DataAccess 另外好用的功能,如果说之前写的事务能Winner框架中最好用的功能,那下面这个功能就可以算的上是第二好用的功能----“分页”
在接触Winner之前,我最常用的是从学校学的那个“分页存储过程”。先不说好用不好用,以动软例总之每个列表要单独处理分页。(也许是我没用好)但是在Winner
里面很简单。贴一下我们是怎么用的:
是的,我们只要设置DA的Changepage这个对象即可。 Winner在控制上用了一个“扩展类”,不清楚扩展类的就要自行百度一下了。
回到前面,可以看到代码生成器生成的集合类操作我们是需要继承 “DataAccessCollectionBase” 而 DataAccessCollectionBase:DataAccessCollectionCore
DataAccessCollectionCore 里我们定义了一个分页对象 ChangePage
ChangePage 实现了 IChangePage 接口,准确来说任何实现了IChangePage的对象都可以使用我们的分页插件。
ChangePage 就是一个分页对象。
/*************************************************** * * Create Time : 2014/9/1 11:38:37 * Version : V 1.01 * Create User:Jie * E_Mail : 6e9e@163.com * ***************************************************/ using Winner.Framework.Core.Interface; namespace Winner.Framework.Core.DataAccess { /// <summary> /// 分页控件适配器 /// </summary> public class ChangePage : IChangePage { /// <summary> /// 分页语句类型 /// </summary> public QueryCommandType CommandType { get; set; } /// <summary> /// 总行数 /// </summary> public int Count { get; set; } /// <summary> /// 当前页面 /// </summary> public int CurrentPageIndex { get; set; } /// <summary> /// 每页显示行数 /// </summary> public int PageSize { get; set; } /// <summary> /// 排序字段名称 /// </summary> public string SortFields { get; set; } /// <summary> /// 排序顺序 /// </summary> public SortType SortType { get; set; } /// <summary> /// 构造新的适配器 /// </summary> /// <param name="pageIndex">当前页面码</param> /// <param name="pageSize">每页显示行数</param> /// <param name="sortFields">排序字段名称</param> /// <param name="sortType">排序顺序</param> /// <param name="commandType">分页语句类型</param> /// <returns></returns> public static ChangePage NewInstance(int pageIndex, int pageSize, string sortFields = "", SortType sortType = SortType.DESC, QueryCommandType commandType = QueryCommandType.Text) { pageIndex = pageIndex <= 0 ? 1 : pageIndex; pageSize = pageSize <= 0 ? 10 : pageSize; ChangePage changePage = new ChangePage { CurrentPageIndex = pageIndex, PageSize = pageSize, SortFields = sortFields, SortType = sortType, CommandType = commandType, }; return changePage; } } }
现在分页需要的基本参数有了,就是在对数据库操作的时候加入这些参数即可:
上面是Sqlserver的实现方式,我们再看一下Oracle 的实现方式:
如果我们想要一页20条数据怎么就和简单了,修改ChangePage的参数就行了。
由于是MVC,this.ChangePage() 会自动接收参数。
至于前端,由于我们就用JavaScript去实现了。 这里贴一个Jason写的基于KO写的前端js分页控件:
function pageModel() { this.currentPageIndex = 1; this.MaxPageIndex = 1; this.loadData = function () { }; this.pagesize = 10; this.eleid = ""; } function koViewModel() { var self = this; self.Data = []; } function renderPage(options) { var currentPageIndex = options.pageindex; var MaxPageIndex = options.MaxPageIndex; var loadData = options.loadData; var pagesize = options.pagesize || 10; var $this = $("#" + options.eleid).parent(); var ul = $("<ul></ul>").addClass("pagination"); var prev = $("<li></li>").append($("<a></a>").append($("<i class='icon-double-angle-left'></i>"))).addClass("prev"); if (currentPageIndex == 1) { prev.addClass("disabled"); } var next = $("<li></li>").append($("<a></a>").append($("<i class='icon-double-angle-right'></i>"))).addClass("next"); if (MaxPageIndex <= 1 || currentPageIndex == MaxPageIndex) { next.addClass("disabled"); } var recordsinfo = $("<li class='disabled'></li>").append($("<a></a>") .append("<span>共</span><span class='totalrecords'>" + options.totalRecords + "</span><span>条,每页</span>" + "<span class='pagesize'>" + pagesize + "</span>条,<span></span>" + "<span class='maxpageindex'>" + MaxPageIndex + "</span><span>页</span>")); var minIndex = currentPageIndex - 5; var plus = 0; if (minIndex <= 0) { plus = 0 - minIndex; minIndex = 1; } var maxIndex = currentPageIndex + 5 + plus; if (maxIndex > MaxPageIndex) { maxIndex = MaxPageIndex; } ul.append(prev); for (var i = minIndex; i <= maxIndex; i++) { var li_html = $("<li></li>"); if (currentPageIndex == i) li_html.addClass("active disabled"); var item = li_html.append($("<a></a>").html(i)); ul.append(item); } ul.append(next); ul.append(recordsinfo); //ul.append(pagesizeoptions); ul.children("li").click(function () { if (!$(this).is(".disabled")) { var index = 1; var pagesize = options.pagesize; if ($(this).is(".selectpagesize")) { return; } else { index = $(this).text(); } if (index.length == 0) { index = ul.find(".active").text(); if ($(this).is(".prev") && index > 1) { index--; } else if ($(this).is(".next") && index < MaxPageIndex) { index++; } } var arg = $.extend({ "pageindex": parseInt(index), "pagesize": pagesize }, options); arg.pageindex = parseInt(index); loadData(arg); } }); if ($this.parent().find(".dataTables_paginate").length > 0) { $this.parent().find(".dataTables_paginate").empty().append(ul); } else { var div_pagination = $("<div class=\"dataTables_paginate paging_bootstrap\"></div>"); div_pagination.append(ul); $this.after(div_pagination);//.empty().append(ul); } } function renderLoading(eleid) { var loadname = eleid + "-loading-" + new Date().valueOf(); var $this = $("#" + eleid); $this.attr("data-loading-id", loadname); var count = $("#" + eleid).find("tr").eq(0).find("td,th").length; $this.find("tbody").empty(); var tr = $("<tr></tr>"); var td = $("<td></td>").attr("colspan", count); var wrap = $("<div></div>").addClass("data-loading-wrap text-center"); var span = $("<span></span>").css({ "border": "1px solid #aaa", "padding": "5px 10px", "line-height": "16px" }); var img = $("<img />").attr("src", "/assets/images/ko_loading.gif").addClass("icon"); var tips = $("<span></span>").html("正在加载数据..."); span.append(img); span.append(tips); wrap.append(span); td.append(wrap); tr.append(td); if ($this.get(0).tagName != "TABLE") { var table = $("<table></table>").attr("id", loadname); table.append(tr); $this.append(table); } else { tr.attr("id", loadname); $this.append(tr); } } function renderWhenNoData(eleid) { var loadname = eleid + "-no-data-" + new Date().valueOf(); var $this = $("#" + eleid); $this.attr("data-nodata-id", loadname); var count = $("#" + eleid).find("tr").eq(0).find("td,th").length; $this.find("tbody").empty(); var tr = $("<tr></tr>"); var td = $("<td></td>").attr("colspan", count); var wrap = $("<div></div>").addClass("data-loading-wrap text-center"); var span = $("<span></span>").css({ "border": "1px solid #aaa", "padding": "5px 10px", "line-height": "16px" }); //var img = $("<img />").attr("src", "/asserts/images/loading.gif").addClass("icon"); var tips = $("<span></span>").html("暂无数据"); //span.append(img); span.append(tips); wrap.append(span); td.append(wrap); tr.append(td); console.log($this.get(0).tagName); if ($this.get(0).tagName != "TABLE") { var table = $("<table style='width:100%;'></table>").attr("id", loadname).addClass("ajax-nodata"); table.append(tr); $this.append(table); } else { tr.addClass("ajax-nodata").attr("id", loadname); $this.append(tr); } } (function ($) { $.extend({ DT: { init: function (opt) { var container = $("#" + opt.eleid).parent().parent(); var activeIndex = container.find(".dataTables_paginate > .pagination > li.active"); //console.log(activeIndex); var currentPageIndex = activeIndex.length > 0 ? activeIndex.text() : undefined; if (opt.pageindex) { } else if (!opt.pageindex && !currentPageIndex) { opt.pageindex = 1; } else { try { opt.pageindex = parseInt(currentPageIndex); } catch (e) { opt.pageindex = 1; } } if (!opt.pagesize) { opt.pagesize = 10; } this.loadData(opt); }, loadData: function (opt) { var pageindex = opt.pageindex || 1; var pagesize = opt.pagesize || 10; var url = opt.url || ""; if (typeof(opt.query) == 'string') { var urlquery = opt.query; opt.query = {}; var array = urlquery.split('&'); for (var i in array) { var keyvalue = array[i].split('='); if (keyvalue[1]) { opt.query[keyvalue[0]] = decodeURI(keyvalue[1]); } } } var query = opt.query || {}; var errorfunc = opt.errorfunc || alert; var enablePaginate = opt.enablePaginate; var searchData = query; if (enablePaginate) { searchData = $.extend(query, { "pageindex": pageindex, "pagesize": pagesize }); } renderLoading(opt.eleid); var pModel = $.extend(opt, { loadData: this.loadData, MaxPageIndex: 1 }); $.ajax({ "url": url, "data": searchData, "type": "POST", "success": function (resp) { var viewModel = new koViewModel(); if (resp.Success) { var loadingid = $("#" + opt.eleid).attr("data-loading-id"); $("#" + loadingid).remove(); if (resp.Content && resp.Content.Data && resp.Content.Data.length > 0) { viewModel.Data = resp.Content.Data; ko.applyBindings(viewModel, document.getElementById(opt.eleid)); if (enablePaginate) { var maxPageIndex = parseInt(resp.Content.Count / pagesize); if (resp.Content.Count % pagesize) { maxPageIndex++; } pModel.MaxPageIndex = maxPageIndex; pModel.totalRecords = resp.Content.Count; pModel.pagesize = pagesize; renderPage(pModel); } } else { $(".ajax-nodata").remove(); if (opt.renderWhenNoData) { opt.renderWhenNoData(opt.eleid); } else { renderWhenNoData(opt.eleid); } } if (opt.success) { opt.success.apply(viewModel, [resp]); } } else { var loadingid = $("#" + opt.eleid).attr("data-loading-id"); $("#" + loadingid).remove(); errorfunc(resp.Message); } }, "error": function () { var loadingid = $("#" + opt.eleid).attr("data-loading-id"); $("#" + loadingid).remove(); errorfunc("连接服务器失败"); } }) } } }); })(jQuery); $(function () { $(".ko-pager").each(function (index, item) { if ($(item).get(0).tagName.toUpperCase() == "INPUT" || $(item).get(0).tagName.toUpperCase() == "BUTTON") { $(item).bind("click", function () { loadDataByKoAttribute(item); }); } else { loadDataByKoAttribute(item); } }); }); function loadDataByKoAttribute(item) { var elementid = $(item).attr("data-ko-element"); var url = $(item).attr("data-ko-url"); var queryinfo = eval("(" + $(item).attr("data-ko-query") + ")"); var enablePaginate = $(item).attr("data-ko-enablepaginate"); var pagesize = $(item).attr("data-ko-pagesize") || 10; var successName = $(item).attr("data-ko-success"); var successFunction = function () { }; if (successName) { successFunction = eval(successName); } var query = {}; for (var key in queryinfo) { if ($(queryinfo[key]).length > 1) { var value = ""; $(queryinfo[key]).each(function (i, ele) { value += $(ele).val(); if (i + 1 < $(queryinfo[key]).length) { value += ","; } }); query[key] = value; } else { query[key] = $(queryinfo[key]).val(); } } $.DT.init({ eleid: elementid, url: url, enablePaginate: enablePaginate == "true" ? true : false, pagesize: pagesize, query: query, success: successFunction }); }
引用js后,调用即可。
function init(pageindex) { //异步加载列表参数 var option = { url: '@Url.Action("getusers","user")',//ajax url eleid: 'user-list',//列表的容器ID //pagesize:10,//每页数据条数 enablePaginate: true,//是否开启分页 //errorfunc:function(str){},//出现错误时的回调 //success:function(){},//执行成功的回调 //ajax请求时携带的参数,可以使用query:$("#form1").serialize() query: $("#form1").serialize() }; //加载指定页码的数据 if (pageindex && pageindex > 0) { option.pageindex = pageindex; } //执行分页列表加载 $.DT.init(option); }
好吧,本来想一篇博客把主要的三,四个程序集写完。但是看来一篇博客是写不玩了,只能分成三篇来写。因为里面的东西实在太多了,让总结Winner-DA四大点:
1,多数据的支持;
2,简洁的分页对象;
3,封装事务机制;
4,封装增删查改基本操作;
今天先写到这里,明天继续写。关于代码的问题,今天没忙过来,这几天我会开一个GitHub,然后把这: