轻量级ORM框架Dapper相信很多人了解,也用过,可能也有很多人可能还不知道
Dapper官网: https://code.google.com/p/dapper-dot-net/
我在网上复制一下别人对Dapper的描述:
Dapper是一个轻型的ORM类。代码就一个SqlMapper.cs文件。文件见下。编译后就40K的一个很小的Dll.
Dapper很快,有多快。实验下就知道了。官方给了点测试包,想玩的时候就去测试下。Dapper的速度接近与IDataReader,取列表的数据超过了DataTable。
Dapper支持什么数据库。Dapper支持Mysql,SqlLite,Mssql2000,Mssql2005,Oracle等一系列的数据库,当然如果你知道原理也可以让它支持Mongo db.
Dapper的r支持多表并联的对象。支持一对多 多对多的关系。并且没侵入性,想用就用,不想用就不用。无XML无属性。代码以前怎么写现在还怎么写。
Dapper原理通过Emit反射IDataReader的序列队列,来快速的得到和产生对象。性能实在高。
Dapper支持net2.0,3.0,3.5,4.0。
Dapper的语法是这样的。语法十分简单。并且无须迁就数据库的设计。
为什么要扩展Dapper:
了解Dapper都知道,在书写代码时,我们还是会手动写SQL,扩展的目的就是在完全不改变dapper源代码和使用基础上,进行一次封闭,达到零SQL,实现完全对象操作。
接下来,我们开始Dapper的扩展之旅第一章。。。
下载Dapper后,我们新建类库项目:DapperEx ,并把Dapper项目加载到项目中:
1.在DapperEx 中添加Dapper引用 ,如下:
2.为了存储数据库类型,以及根据数据库类型,使用不同的参数化操作数据库,添加一个DbBase类:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
namespace Dapper
{
public class DbBase : IDisposable
{
private string paramPrefix = "@";
private string providerName = "System.Data.SqlClient";
private IDbConnection dbConnecttion;
private DbProviderFactory dbFactory;
private DBType _dbType = DBType.SqlServer;
public IDbConnection DbConnecttion
{
get
{
return dbConnecttion;
}
}
public string ParamPrefix
{
get
{
return paramPrefix;
}
}
public string ProviderName
{
get
{
return providerName;
}
}
public DBType DbType
{
get
{
return _dbType;
}
}
public DbBase(string connectionStringName)
{
var connStr = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
else
throw new Exception("ConnectionStrings中没有配置提供程序ProviderName!");
dbFactory = DbProviderFactories.GetFactory(providerName);
dbConnecttion = dbFactory.CreateConnection();
dbConnecttion.ConnectionString = connStr;
dbConnecttion.Open();
SetParamPrefix();
}
private void SetParamPrefix()
{
string dbtype = (dbFactory == null ? dbConnecttion.GetType() : dbFactory.GetType()).Name;
//使用类型名判断
if (dbtype.StartsWith("MySql")) _dbType = DBType.MySql;
else if (dbtype.StartsWith("SqlCe")) _dbType = DBType.SqlServerCE;
else if (dbtype.StartsWith("Npgsql")) _dbType = DBType.PostgreSQL;
else if (dbtype.StartsWith("Oracle")) _dbType = DBType.Oracle;
else if (dbtype.StartsWith("SQLite")) _dbType = DBType.SQLite;
else if (dbtype.StartsWith("System.Data.SqlClient.")) _dbType = DBType.SqlServer;
//else try with provider name
else if (providerName.IndexOf("MySql", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.MySql;
else if (providerName.IndexOf("SqlServerCe", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.SqlServerCE;
else if (providerName.IndexOf("Npgsql", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.PostgreSQL;
else if (providerName.IndexOf("Oracle", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.Oracle;
else if (providerName.IndexOf("SQLite", StringComparison.InvariantCultureIgnoreCase) >= 0) _dbType = DBType.SQLite;
if (_dbType == DBType.MySql && dbConnecttion != null && dbConnecttion.ConnectionString != null && dbConnecttion.ConnectionString.IndexOf("Allow User Variables=true") >= 0)
paramPrefix = "?";
if (_dbType == DBType.Oracle)
paramPrefix = ":";
}
public void Dispose()
{
if (dbConnecttion != null)
{
try
{
dbConnecttion.Dispose();
}
catch { }
}
}
}
public enum DBType
{
SqlServer,
SqlServerCE,
MySql,
PostgreSQL,
Oracle,
SQLite
}
}
View Code
此类功能利用DbProviderFactories实现数据源连接接口IDbConnection,通过数据库判断设置参数前缀为@、?、:的一种。
3.和大多数实体映射一样,为了更方便的操作数据库,我们添加几个特性,来实现实体与数据库表的映射关系:
BaseAttribute:特性基类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dapper
{
public class BaseAttribute:Attribute
{
///
///别名,对应数据里面的名字///
public string Name { get; set; }
}
}
View Code
ColumnAttribute:字段列特性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dapper
{
///
///列字段///
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ColumnAttribute : BaseAttribute
{
///
///自增长///
public bool AutoIncrement { get; set; }
public ColumnAttribute()
{
AutoIncrement = false;
}
///
///是否是自增长///
///
public ColumnAttribute(bool autoIncrement)
{
AutoIncrement = autoIncrement;
}
}
}
View Code
IdAttribute:主键列特性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dapper
{
///
///主键///
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class IdAttribute : BaseAttribute
{
///
///是否为自动主键///
public bool CheckAutoId { get; set; }
public IdAttribute()
{
this.CheckAutoId = false;
}
///
///
///
/// 是否为自动主键
public IdAttribute(bool checkAutoId)
{
this.CheckAutoId = checkAutoId;
}
}
}
View Code
IgnoreAttribute:忽略列特性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dapper
{
///
///忽略字段///
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class IgnoreAttribute:BaseAttribute
{
}
}
View Code
TableAttribute:数据库表特性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dapper
{
///
///数据库表///
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class TableAttribute : BaseAttribute
{
}
}
View Code
4.接下来建立一个生成SQL时参数里面的列名和对应值名称的对应类:ParamColumnModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Dapper
{
///
///生成SQL时参数里面的列名和对应值名称///
public class ParamColumnModel
{
///
///数据库列名///
public string ColumnName { get; set; }
///
///对应类属性名///
public string FieldName { get; set; }
}
}
View Code
5.建立一个公共类,方便对特性、属性进行操作和取值等操作Common
using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
namespace DapperEx
{
public class Common
{
///
///获取对象对应数据库表名///
///
///
public static string GetTableName()
{
var ty = typeof(T);
var arri = ty.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
if (arri is TableAttribute && (!string.IsNullOrEmpty((arri as BaseAttribute).Name)))
{
return (arri as BaseAttribute).Name;
}
return ty.Name;
}
///
///在没有指定排序时,获取一个默认的排序列///
///
///
public static string GetDefaultOrderField()
{
var name = "";
foreach (var propertyInfo in typeof(T).GetProperties())
{
var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
if (arri is IgnoreAttribute)
{
arri = null;
continue;
}
name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
break;
}
return name;
}
///
///获取要执行SQL时的列,添加和修改数据时///
///
///
public static IList GetExecColumns() where T : class
{
var columns = new List();
foreach (var propertyInfo in typeof(T).GetProperties())
{
var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
if (arri is IgnoreAttribute)
{
arri = null;
continue;
}
else if (arri is IdAttribute)
{
if ((arri as IdAttribute).CheckAutoId)
{
arri = null;
continue;
}
}
else if (arri is ColumnAttribute)
{
if ((arri as ColumnAttribute).AutoIncrement)
{
arri = null;
continue;
}
}
string name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
columns.Add(new ParamColumnModel() { ColumnName = name, FieldName = propertyInfo.Name });
}
return columns;
}
///
///获取对象的主键标识列///
///
/// 对应实体属性名
///
public static string GetPrimaryKey(out string PropertyName) where T : class
{
string name = "";
PropertyName = "";
foreach (var propertyInfo in typeof(T).GetProperties())
{
var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
if (arri is IdAttribute)
{
name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
PropertyName = propertyInfo.Name;
break;
}
}
if (string.IsNullOrEmpty(PropertyName))
{
throw new Exception("没有任何列标记为主键特性");
}
return name;
}
///
///通过属性名获取对应的数据列名///
///
///
///
public static string GetExecCloumName(string propertyName) where T : class
{
var propertyInfo = typeof(T).GetProperty(propertyName);
var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
if (arri is IgnoreAttribute)
{
arri = null;
}
string name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
return name;
}
///
///通过表达示树获取属性名对应列名///
///
///
///
public static string GetNameByExpress(Expression> expr) where T : class
{
var pname = "";
if (expr.Body is UnaryExpression)
{
var uy = expr.Body as UnaryExpression;
pname = (uy.Operand as MemberExpression).Member.Name;
}
else
{
pname = (expr.Body as MemberExpression).Member.Name;
}
var propertyInfo = typeof(T).GetProperty(pname);
var arri = propertyInfo.GetCustomAttributes(typeof(BaseAttribute), true).FirstOrDefault();
if (arri is IgnoreAttribute)
{
throw new Exception(string.Format("{0}不能进行SQL处理", pname));
}
string name = (arri == null || string.IsNullOrEmpty((arri as BaseAttribute).Name)) ? propertyInfo.Name : (arri as BaseAttribute).Name;
return name;
}
///
///字符串中连续多个空格合并成一个空格///
///
///
public static string UnitMoreSpan(string str)
{
Regex replaceSpace = new Regex(@"\s{1,}", RegexOptions.IgnoreCase);
return replaceSpace.Replace(str, " ").Trim();
}
}
}
View Code
6.有了以上基础,现在开始进行扩展操作,新建DapperEx类:
本系列,我们先扩展两个方法:添加一个实体 和 批量添加
代码比较简单:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dapper;
using System.Data;
using DapperEx;
namespace Dapper
{
public static class DapperEx
{
///
///扩展插入数据///
///
///
///
///
///
///
///
public static bool Insert(this DbBase dbs, T t, bool useTransaction = false, int? commandTimeout = null) where T : class,new()
{
var db = dbs.DbConnecttion;
IDbTransaction tran = null;
if (useTransaction)
tran = db.BeginTransaction();
var result = false;
var tbName = Common.GetTableName();
var columns = Common.GetExecColumns();
var flag = db.Execute(CreateInertSql(tbName, columns, dbs.ParamPrefix), t, tran, commandTimeout);
if (tran != null)
{
try
{
tran.Commit();
result = true;
}
catch
{
tran.Rollback();
}
}
else
{
return flag == 1;
}
return result;
}
///
///批量插入///
///
///
///
///
///
///
public static bool InsertBatch(this DbBase dbs, IList lt, bool useTransaction = false, int? commandTimeout = null) where T : class,new()
{
var db = dbs.DbConnecttion;
IDbTransaction tran = null;
if (useTransaction)
tran = db.BeginTransaction();
var result = false;
var tbName = Common.GetTableName();
var columns = Common.GetExecColumns();
var flag = db.Execute(CreateInertSql(tbName, columns, dbs.ParamPrefix), lt, tran, commandTimeout);
if (tran != null)
{
try
{
tran.Commit();
result = true;
}
catch
{
tran.Rollback();
}
}
else
{
return flag == lt.Count;
}
return result;
}
///
///组装插入语句///
///
///
///
private static string CreateInertSql(string tbName, IList colums, string ParamPrefix)
{
StringBuilder sql = new StringBuilder();
sql.Append(string.Format("INSERT INTO {0}(", tbName));
for (int i = 0; i < colums.Count; i++)
{
if (i == 0) sql.Append(colums[i].ColumnName);
else sql.Append(string.Format(",{0}", colums[i].ColumnName));
}
sql.Append(")");
sql.Append("VALUES(");
for (int i = 0; i < colums.Count; i++)
{
if (i == 0) sql.Append(string.Format("{0}{1}", ParamPrefix, colums[i].FieldName));
else sql.Append(string.Format(",{0}{1}", ParamPrefix, colums[i].FieldName));
}
sql.Append(")");
return sql.ToString();
}
}
}
View Code
今天要讲的都已经完成,接下来,我们进行使用测试:
在解决方案中添加一个测试库:DapperExTest,并在测试库中添加一个本地数据库:dbSqlCeEx.sdf,并在数据库中中添加一个表:Account
注意最后个一字段。Flag是一个字增长列.
在测试库中添加一个应用程序配置文件:App.config,并修改成自己相应的连接数据库字符串
在测试库中添加一个相应的实体类:Account
namespace DapperExTest
{
public class Account
{
[Id]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual string Password { get; set; }
public virtual string Email { get; set; }
public virtual DateTime CreateTime { get; set; }
public virtual int Age { get; set; }
[Column(true)]
public virtual int Flag { get; set; }
[Ignore]
public virtual string AgeStr
{
get
{
return "年龄:" + Age;
}
}
}
}
上述工作完成后,在测试类UnitTest1中添加如下代码:
public string connectionName = "strSqlCe";
public DbBase CreateDbBase()
{
return new DbBase(connectionName);
}
现在我们对添加功能,进行测试,添加方法:
[TestMethod]
public void Insert()//插入一条数据
{
var model = new Account()
{
Id = "1",
Name = "张三1",
Password = "123456",
Email = "[email protected]",
CreateTime = DateTime.Now,
Age = 15
};
using (var db = CreateDbBase())
{
var result = db.Insert(model);
if (result)
Console.WriteLine("添加成功");
else
Console.WriteLine("添加失败");
}
}
右键运行测试,测试成功,成功添加一条数据:
现在测试批量添加:
[TestMethod]
public void InsertBatch()//插入多条数据
{
var list = new List();
for (int i = 2; i < 21; i++)
{
var model = new Account()
{
Id = i.ToString(),
Name = "张三" + i.ToString(),
Password = "123456",
Email = "[email protected]",
CreateTime = DateTime.Now,
Age = 15
};
list.Add(model);
}
using (var db = CreateDbBase())
{
var result = db.InsertBatch(list, true);
if (result)
Console.WriteLine("添加成功");
else
Console.WriteLine("添加失败");
}
}
运行完成后,查看数据库:
成功!!!
OK,
今天的扩展到此结束,后续系列将会对修改、批量修改、删除、批量删除、查询、分页进行扩展
希望大家多多关注,觉得对自己有所帮助或有意见的,欢迎留言,觉得不错的,不要吝啬你的鼠标,点点支持,点点推荐,谢谢啦!!!
本系列源码:http://pan.baidu.com/s/1dDh4T7F