程序员的自我救赎---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 = ;
dt.Columns.Add(_PAY_TYPE, typeof(int)).DefaultValue = ;
dt.Columns.Add(_AMOUNT, typeof(decimal)).DefaultValue = ;
dt.Columns.Add(_ACCOUNT, typeof(string)).DefaultValue = string.Empty;
dt.Columns.Add(_USER_ID, typeof(int)).DefaultValue = ;
dt.Columns.Add(_STATUS, typeof(int)).DefaultValue = ;
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 == )
{
return true;
} StringBuilder sql = new StringBuilder();
sql.AppendLine("UPDATE TDCT_RECHARGE SET");
while (ChangePropertys.MoveNext())
{
sql.AppendFormat(" {0}{1}=:{1} ", (ChangePropertys.CurrentIndex == ? 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>=)
{
sql += " AND TR.STATUS=:STATUS";
AddParameter("STATUS", status);
} if(paytype>=)
{
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 = ;
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(" ") >= )
{
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(" ") >= )
{
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 <= ? : pageIndex;
pageSize = pageSize <= ? : 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,然后把这:
程序员的自我救赎---1.4.1:核心框架讲解(DAL)的更多相关文章
- 程序员的自我救赎---1.4.2: 核心框架讲解(BLL&Tool)
<前言> <目录> (一) Winner2.0 框架基础分析 (二) 短信中心 (三)SSO单点登录 (四)PLSQL报表系统 (五)钱包系统 (六)GPU支付中心 (七)权限 ...
- 程序员的自我救赎---11.3:WinService服务
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---11.4:FileSystem文件服务
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---11.1:RPC接口使用规范
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---12.2.3: 虚拟币交易平台(区块链) 下 【C#与以太坊通讯】
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---3.1:理解Oauth2.0
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---3.2:SSO及应用案例
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---1.4.3: 核心框架讲解(MVC)
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 程序员的自我救赎---10.1:APP版本控制系统
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
随机推荐
- 字符串的缺点以及StringBuilder
字符串的缺点: String 对象是不可改变的.每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间.在需要对字符串执行重复修改的 ...
- JavaScript学习笔记(十四)——对象
在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...
- 人体姿态的相似性评价基于OpenCV实现最近邻分类KNN K-Nearest Neighbors
最近学习了人体姿态的相似性评价.需要用到KNN来统计与当前姿态相似的k个姿态信息. 假设我们已经有了矩阵W和给定的测试样本姿态Xi,需要寻找与Xi相似的几个姿态,来估计当前Xi的姿态标签. //knn ...
- Less的Extend_Less继承
Extend就相当于Java的继承,它允许一个选择器继承另一个选择器的样式.Extend有两种语法格式. 一种是: <selector>:extend(<parentSelector ...
- codeforces 883M. Quadcopter Competition 思路
M. Quadcopter Competition time limit per test 3 seconds memory limit per test 256 megabytes input st ...
- Nginx如何配置虚拟主机?
注意,该环境是依赖于http://www.php20.com/forum.php?m ... &extra=page%3D1 基础上进行配置.默认不具备这些文件 .下面是增加一个mytest点 ...
- QT的radioButton组的使用
在使用Qt的radioButton控件时,会产生一个疑问,如何让你选择的那个radio得到一个数据,进行判断,网上的一些资料有些不全,容易出错. 所以你得做件事,给每个radioButton进行赋初值 ...
- C++ size_t 和size_type的区别
为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned size_t是全局定义的类型:size_type是STL类中定义的类型属性,用 ...
- java.util.ArrayList、java.util.vector和java.util.LinkedList (JDK 1.8.0_111)
一.java.util.ArrayList 1.1 ArrayList 继承结构 ArrayList实现了RandomAccess,可以随机访问(其实就是通过数组下标访问):实现了Cloneable, ...
- mongoDB之集合操作
mongoDB之集合操作 mongoDB中的集合相当于mysql中的表. mongoDB中集合的创建: 第一种方式:不限制集合大小 db.createCollection("集合名称&q ...