最近在工作过程中发现,还有很多猿在使用动软代码生成器,或者自己手写DAL类,每个表一个DAL类,并且每个DAL都写一堆CRUD的方法,除了表名,字段名不一样,其他的代码基本都是重复的,这完全不符合面向对象编程的特性,并且会造成代码冗余,复用性极低,维护不便等等问题。
下面介绍一种复用性极高的ORM框架 反射+Dapper.Net
关于反射的使用这里不再说了,大家可以去查看我的文章-> 对C# 反射使用的一些整理
Dapper.NET -轻量级ORM,有多轻呢,封装一个DapperHelper类,写好连接字符串就可以调用了,相比Entity Framework这种来处理关系映射的ORM,会发现Dapper.NET既省时又省力。
为什么选择Dapper.NET?
1、轻量,可复用性极高
2、速度快。Dapper的速度接近与IDataReader,取列表的数据超过了DataTable。
3、支持多种数据库。Dapper可以在所有Ado.net Providers下工作,包括sqlite, sqlce, firebird, oracle, MySQL, PostgreSQL and SQL Server
4、可以映射一对一,一对多,多对多等多种关系。
5、性能高。通过Emit反射IDataReader的序列队列,来快速的得到和产生对象,性能不错。
6、支持FrameWork2.0,3.0,3.5,4.0,4.5,4.6,4.7 .......
接下来我们看一下常见的使用Dapper.NET写的BLL和DAL层代码
BLL层 新增Insert方法
public bool Insert(User model)
{
try
{
return new UserDAL().Insert(model);
}
catch (Exception ex)
{
}
return false;
}
DAL层 新增Insert方法
public bool Insert(User model, string tableName)
{
string sql = "insert into User(name,sex) values(@name,@sex)";
return dapper.Execute(sql, model);
}
从上面的代码可以看出相对于传统的ORM框架,使用Dapper.NET后,代码量明显减少了很多,但是复用性太低,如果这么写,那每个数据库表都要建这么一个BLL和DAL类,并且还要把CRUD的方法在重新写一遍。
接下来我们把反射应用上去
BLL层 新增Insert方法
public bool Insert(T model, string tableName)
{
try
{
return _baseDAL.Insert(model, tableName)
}
catch (Exception ex)
{
}
return false;
}
DAL层 新增Insert方法
public bool Insert(T model, string tableName)
{
string sqls = $"insert into [{tableName}]({_dapper.GetParas()}) values({_dapper.GetATParas()})";
return _dapper.Execute(sqls, model);
}
DAL层中用到的两个方法
GetParas
public string GetParas()
{
StringBuilder columns = new StringBuilder();
//StringBuilder values = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST") || p.Name.ToUpper() == "ID")
{
continue;
}
columns.Append(p.Name).Append(",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
GetATParas
public string GetATParas()
{
StringBuilder columns = new StringBuilder();
//StringBuilder values = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST") || p.Name.ToUpper() == "ID")
{
continue;
}
columns.Append("@").Append(p.Name).Append(",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
最后DAL层Insert方法的sql值为 insert into User(Name,Sex) values(@Name,@Sex);
使用反射的好处显而易见,我们不需要知道传入的对象是谁,你随便传,我把对象拿过来直接解析出你所有的属性,然后拼接成sql执行就完事了。
接下来我们在扩展几个简单的CRUD方法,然后把封装成Base类,然后作为父类给其他子类使用,其他子类只需要继承Base类就可以了
BaseBLL类
_baseDAL = new BaseDAL();
///
/// 公共全部查询方法
///
///
///
///
///
public virtual List SelectAll(string where, string tableName)
{
try
{
List lists = _baseDAL.SelectAll(where, tableName);
return lists;
}
catch (Exception ex)
{
}
return null;
}
///
/// 公共新增方法
///
///
///
///
public bool Insert(T model, string tableName)
{
try
{
return _baseDAL.Insert(model, tableName)
}
catch (Exception ex)
{
}
return false;
}
///
/// 公共更新方法
///
///
///
///
public virtual bool Update(T model, string tableName)
{
try
{
return _baseDAL.Update(model, tableName);
}
catch (Exception ex)
{
}
return false;
}
///
/// 公共删除方法
///
///
///
///
public virtual bool Del(T model, string tableName)
{
try
{
return _baseDAL.Del(model, tableName);
}
catch (Exception ex)
{
}
return false;
}
BaseDAL类
_dapper = new DapperHelper();
///
/// 公共全部查询方法
///
///
///
///
///
public List SelectAll(string where, string tableName)
{
string sql = $"select * from [{tableName}] where 1=1";
if (!string.IsNullOrEmpty(where))
{
sql += $" and {where}";
}
return _dapper.Query(sql).ToList();
}
///
/// 公共新增方法
///
/// 表类
/// 对象
/// 表名
/// 插入成功true 否则false
public bool Insert(T model, string tableName)
{
string sqls = $"insert into [{tableName}]({_dapper.GetParas()}) values({_dapper.GetATParas()})";
return _dapper.Execute(sqls, model);
}
///
/// 公共更新方法
///
/// 表类
/// 对象
/// 表明
/// 插入成功true 否则false
public bool Update(T model, string tableName)
{
string sql = $"update [{tableName}] set {_dapper.GetParasToAT()} where Id=@Id";
return _dapper.Execute(sql, model);
}
///
/// 公共删除方法
///
/// 表类
/// 对象
/// 表明
/// 插入成功true 否则false
public bool Del(T model, string tableName)
{
string sql = $"delete [{tableName}] where Id=@Id";
return _dapper.Execute(sql, model);
}
DapperHelper
public class DapperHelper
{
///
/// the dapper option
///
private DapperOptions _dapperOptions;
public DapperHelper(string key = "", DapperOptions dapper = null)
{
if (dapper == null)
{
dapper = new DapperOptions();
dapper.ConnectionString = ConfigurationManager.ConnectionStrings["constr"].ToString();
}
this._dapperOptions = dapper;
}
///
/// get the connection
///
///
private SqlConnection OpenConnection()
{
SqlConnection conn = new SqlConnection(_dapperOptions.ConnectionString);
conn.Open();
return conn;
}
///
/// 返回对象属性名称(name)去除标记id name,name,name
///
///
///
public string GetParas()
{
StringBuilder columns = new StringBuilder();
//StringBuilder values = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST") || p.Name.ToUpper() == "ID")
{
continue;
}
columns.Append(p.Name).Append(",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
///
/// 返回对象属性名称(@name) @name,@name,@name
///
/// 类型
///
public string GetATParas()
{
StringBuilder columns = new StringBuilder();
//StringBuilder values = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST") || p.Name.ToUpper() == "ID")
{
continue;
}
columns.Append("@").Append(p.Name).Append(",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
///
/// 返回 name=@name, name=@name, name=@name
///
///
///
public string GetParasToAT()
{
StringBuilder columns = new StringBuilder();
//StringBuilder values = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST") || p.Name.ToUpper() == "ID")
{
continue;
}
columns.Append(p.Name).Append("=@" + p.Name + ",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
public string GetParasToAT(T model)
{
StringBuilder columns = new StringBuilder();
//StringBuilder values = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST") || p.Name.ToUpper() == "ID")
{
continue;
}
object value = p.GetValue(model, null);
if (!string.IsNullOrEmpty(value?.ToString()))
{
columns.Append(p.Name).Append("=@" + p.Name + ",");
}
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
///
/// exec using T-SQL
///
///
///
///
///
///
///
public bool Execute(string sql, object param = null, IDbTransaction transaction = null,
int? commandTimeout = null, CommandType commandType = CommandType.Text)
{
using (IDbConnection conn = OpenConnection())
{
int count = conn.Execute(sql, param, transaction, commandTimeout, commandType);
return count > 0;
}
}
public bool ExecuteTran(string sql, object param = null, IDbTransaction transaction = null,
int? commandTimeout = null, CommandType commandType = CommandType.Text)
{
using (IDbConnection conn = OpenConnection())
{
transaction = conn.BeginTransaction();
int count = conn.Execute(sql, param, transaction, commandTimeout, commandType);
transaction.Commit();
return count > 0;
}
}
///
/// exec using stored procedure
///
///
///
///
///
///
///
public bool ExecuteForProc(string storedProcedureName, object param = null, IDbTransaction transaction = null,
int? commandTimeout = null, CommandType commandType = CommandType.StoredProcedure)
{
using (IDbConnection conn = OpenConnection())
{
int count = conn.Execute(storedProcedureName, param, transaction, commandTimeout, commandType);
return count > 0;
}
}
///
/// query using T-SQL
///
///
///
///
///
///
///
///
///
public IList Query(string sql, object param = null, IDbTransaction transaction = null,
bool buffered = true, int? commandTimeout = null, CommandType commandType = CommandType.Text)
{
using (IDbConnection conn = OpenConnection())
{
return conn.Query(sql, param, transaction, buffered, commandTimeout, commandType).ToList();
}
}
///
/// query using stored procedure
///
///
///
///
///
///
///
///
///
public IList QueryForProc(string storedProcedureName, object param = null, IDbTransaction transaction = null,
bool buffered = true, int? commandTimeout = null, CommandType commandType = CommandType.StoredProcedure)
{
using (IDbConnection conn = OpenConnection())
{
return conn.Query(storedProcedureName, param, transaction, buffered, commandTimeout, commandType).ToList();
}
}
///
/// dynamic query using sql
///
///
///
///
///
///
///
///
public dynamic Query(string sql, object param = null, IDbTransaction transaction = null,
bool buffered = true, int? commandTimeout = null, CommandType commandType = CommandType.Text)
{
using (IDbConnection conn = OpenConnection())
{
return conn.Query(sql, param, transaction, buffered, commandTimeout, commandType).ToList();
}
}
///
/// dynamic query using stored procedure
///
///
///
///
///
///
///
///
public dynamic QueryForProc(string storedProcedureName, object param = null, IDbTransaction transaction = null,
bool buffered = true, int? commandTimeout = null, CommandType commandType = CommandType.StoredProcedure)
{
using (IDbConnection conn = OpenConnection())
{
return conn.Query(storedProcedureName, param, transaction, buffered, commandTimeout, commandType).ToList();
}
}
///
/// 批量插入功能
///
public void InsertBatch(IEnumerable entityList, string tblName, IDbTransaction transaction = null) where T : class
{
tblName = tblName ?? string.Format("dbo.{0}", typeof(T).Name);
var tran = (SqlTransaction)transaction;
using (var bulkCopy = new SqlBulkCopy(OpenConnection() as SqlConnection, SqlBulkCopyOptions.TableLock, tran))
{
bulkCopy.BatchSize = entityList.Count();
bulkCopy.DestinationTableName = tblName;
var table = new DataTable();
ISqlGenerator sqlGenerator = new SqlGeneratorImpl(new DapperExtensionsConfiguration());
var classMap = sqlGenerator.Configuration.GetMap();
var props = classMap.Properties.Where(x => x.Ignored == false).ToArray();
foreach (var propertyInfo in props)
{
bulkCopy.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name);
table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyInfo.PropertyType) ?? propertyInfo.PropertyInfo.PropertyType);
}
var values = new object[props.Count()];
foreach (var itemm in entityList)
{
for (var i = 0; i < values.Length; i++)
{
values[i] = props[i].PropertyInfo.GetValue(itemm, null);
}
table.Rows.Add(values);
}
bulkCopy.WriteToServer(table);
}
}
///
/// 批量更新数据(每批次5000)
///
/// 数据库链接字符串
///
public bool MultiUpdateData(List list, string tableName)
{
DataTable table = ToDataTable(list);
table.TableName = tableName;
using (SqlConnection conn = OpenConnection())
{
SqlCommand comm = conn.CreateCommand();
comm.CommandTimeout = 60;
comm.CommandType = CommandType.Text;
comm.CommandText = $"select top 1 * from {tableName}";
SqlDataAdapter adapter = new SqlDataAdapter(comm);
SqlCommandBuilder commandBulider = new SqlCommandBuilder(adapter);
commandBulider.ConflictOption = ConflictOption.OverwriteChanges;
commandBulider.SetAllValues = true;
foreach (DataRow dr in table.Rows)
{
if (dr.RowState == DataRowState.Unchanged)
dr.SetModified();
}
try
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//设置批量更新的每次处理条数
adapter.UpdateBatchSize = 5000;
adapter.SelectCommand.Transaction = conn.BeginTransaction();/////////////////开始事务
if (table.ExtendedProperties["SQL"] != null)
{
adapter.SelectCommand.CommandText = table.ExtendedProperties["SQL"].ToString();
}
int nCount = adapter.Update(table);
adapter.SelectCommand.Transaction.Commit();/////提交事务
return nCount > 0;
}
catch (Exception ex)
{
if (adapter.SelectCommand != null && adapter.SelectCommand.Transaction != null)
{
adapter.SelectCommand.Transaction.Rollback();
}
LogManager.Error("更新失败", ex);
return false;
}
finally
{
conn.Close();
conn.Dispose();
}
}
}
///
/// List To DataTable
///
///
///
///
///
public static DataTable ToDataTable(IList list, params string[] propertyName)
{
List propertyNameList = new List();
if (propertyName != null)
{
propertyNameList.AddRange(propertyName);
}
DataTable result = new DataTable();
if (list.Count > 0)
{
PropertyInfo[] propertys = list[0].GetType().GetProperties();
foreach (PropertyInfo pi in propertys)
{
if (propertyNameList.Count == 0)
{
Type colType = pi.PropertyType;
if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
colType = colType.GetGenericArguments()[0];
}
result.Columns.Add(pi.Name, colType);
}
else
{
if (propertyNameList.Contains(pi.Name))
{
result.Columns.Add(pi.Name, pi.PropertyType);
}
}
}
for (int i = 0; i < list.Count; i++)
{
ArrayList tempList = new ArrayList();
foreach (PropertyInfo pi in propertys)
{
if (propertyNameList.Count == 0)
{
object obj = pi.GetValue(list[i], null);
tempList.Add(obj);
}
else
{
if (propertyNameList.Contains(pi.Name))
{
object obj = pi.GetValue(list[i], null);
tempList.Add(obj);
}
}
}
object[] array = tempList.ToArray();
result.LoadDataRow(array, true);
}
}
return result;
}
}
DapperOptions类
public class DapperOptions
{
///
/// 连接字符串
///
public string ConnectionString { get; set; }
}
上面只是简单的CRUD方法,大家可以根据这个思路,自己探索吧!!