Dapper的基本使用
Dapper - a simple object mapper for .Net
Dapper是.NET下一个micro ORM,它和Entity Framework或Nhibnate不同,属于轻量级的,并且是半自动的,也就是说实体类都要自己写。
下载地址:https://github.com/StackExchange/Dapper/
优点
1. 使用Dapper可以自动进行对象映射。
2. 轻量级,单文件。
3. 支持多数据库。
4. Dapper原理通过Emit反射IDataReader的序列队列,来快速的得到和产生对象。
5. 可以映射一对一,一对多,多对多等多种关系。
发布说明
Located at stackexchange.github.io/Dapper
Packages
MyGet Pre-release feed: https://www.myget.org/gallery/dapper
Package | NuGet Stable | NuGet Pre-release | Downloads | MyGet |
---|---|---|---|---|
Dapper | ||||
Dapper.Contrib | ||||
Dapper.EntityFramework | ||||
Dapper.EntityFramework.StrongName | ||||
Dapper.Rainbow | ||||
Dapper.SqlBuilder | ||||
Dapper.StrongName |
Features
Dapper是一个NuGet库,您可以将其添加到您的项目中,以扩展IDbConnection接口。
连接语句
var _connectionString = ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString; IDbConnection conn = new SqlConnection(_connectionString);
使用dapper不需要考虑conn是否连接,在执行dapper时自行判断 open状态,如果没有打开它会自己打开。一条查询的时候不用管,多个查询在一起的时候,建议自己打开,写事务的时候还是需要自己打开的哦。
它提供了3个 helpers:
1、执行查询并将结果映射到强类型列表
public static IEnumerable<T> Query<T>(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)
使用示例:
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
public int IgnoredProperty { get { return 1; } }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });
Assert.Equal(1,dog.Count());
Assert.Null(dog.First().Age);
Assert.Equal(guid, dog.First().Id);
2、执行查询并将其映射到动态对象列表
public static IEnumerable<dynamic> Query (this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)
该方法将执行SQL并返回一个动态列表。
使用示例:
var rows = connection.Query("select 1 A, 2 B union all select 3, 4").AsList();
Assert.Equal(1, (int)rows[0].A);
Assert.Equal(2, (int)rows[0].B);
Assert.Equal(3, (int)rows[1].A);
Assert.Equal(4, (int)rows[1].B);
3、执行不返回任何结果的命令
public static int Execute(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null)
使用示例:
var count = connection.Execute(@"
set nocount on
create table #t(i int)
set nocount off
insert #t
select @a a union all select @b
set nocount on
drop table #t", new {a=1, b=2 });
Assert.Equal(2, count);
批量插入
相同的签名还允许您方便而高效地多次执行命令(例如批量加载数据)
使用示例:
var count = connection.Execute(@"insert MyTable(colA, colB) values (@a, @b)",
new[] { new { a=1, b=1 }, new { a=2, b=2 }, new { a=3, b=3 } }
);
Assert.Equal(3, count); // 3 rows inserted: "1,1", "2,2" and "3,3"
这适用于任何为某个T实现IEnumerable的参数。
query = "INSERT INTO Dogs(Id,Name,Age,Weight) VALUES(@Id,@Name,@Age,@Weight)"; conn.Execute(query, new List<Dog>(){ new Dog{ Id = DateTime.Now.Ticks+1, Name = "小红1", Age = 2, Weight = 10 }, new Dog { Id = DateTime.Now.Ticks+2, Name = "小红2", Age = 2, Weight = 10 }, new Dog { Id = DateTime.Now.Ticks+3, Name = "小红3", Age = 2, Weight = 10 }, new Dog { Id = DateTime.Now.Ticks+4, Name = "小红3", Age = 2, Weight = 10 }, });
参数化查询
参数作为匿名类传入。这允许您轻松地命名参数,并使您能够简单地剪切和粘贴SQL片段,并在db平台的查询分析器中运行它们。
new {A = 1, B = "b"} // A will be mapped to the param @A, B to the param @B
List 支持
Dapper允许你传入 IEnumerable<int> 并将自动参数化查询。
例如:
connection.Query<int>("select * from (select 1 as Id union all select 2 union all select 3) as X where Id in @Ids", new { Ids = new int[] { 1, 2, 3 } });
将转化为:
select * from (select 1 as Id union all select 2 union all select 3) as X where Id in (@Ids1, @Ids2, @Ids3)" // @Ids1 = 1 , @Ids2 = 2 , @Ids2 = 3
Dapper中in,like的使用
list = conn.Query<Dog>("SELECT * FROM Dogs WHERE id IN @ids ", new { ids = new long[] { list[0].Id, list[1].Id, list[2].Id } }).ToList(); list = conn.Query<Dog>("SELECT * FROM Dogs WHERE name LIKE @name ", new { name = $"%{name}%" }).ToList();
文字字面替换
Dapper支持对bool和numeric类型进行文字替换。
connection.Query("select * from User where UserTypeId = {=Admin}", new { UserTypeId.Admin });
文字替换不作为参数发送;这允许更好的计划和过滤索引的使用,但通常应该在测试后谨慎使用。
当注入的值实际上是固定值时(例如,特定于查询的固定“category id”、“status code”或“region”),此特性尤其有用。
对于正在考虑文本的实时数据,您可能还需要考虑和测试provider-specific的查询提示,比如使用常规参数OPTIMIZE FOR UNKNOWN。
缓冲和无缓冲的阅读器
Dapper的默认行为是执行SQL并在返回时缓冲整个读取器。
在大多数情况下,这是理想的,因为它最小化了数据库中的共享锁,并减少了数据库网络时间。
但是,在执行大型查询时,您可能需要最小化内存占用,并且只根据需要加载对象。为此,将buffered: false传递到Query方法中。
多映射
Dapper允许您将一行映射到多个对象。如果您想避免不必要的查询和急于加载关联,这是一个关键特性。
例子:
考虑两个类: Post
and User
class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public User Owner { get; set; }
}
class User
{
public int Id { get; set; }
public string Name { get; set; }
}
现在,假设我们想映射一个连接posts和users表的查询。
直到现在,如果我们需要合并两个查询的结果,我们需要一个新的对象来表示它,但是在这种情况下,将User对象放在Post对象中更有意义。
这是多映射的用例。您告诉dapper查询返回一个Post和一个User对象,然后给它一个函数,描述您想对包含Post和User对象的每一行做什么。在本例中,我们希望获取user对象并将其放入post对象中。我们写下函数:
(post, user) => { post.Owner = user; return post; }
查询方法的3个类型参数指定dapper应该使用哪些对象来反序列化行,以及将返回什么。
我们将把这两行解释为Post和User的组合然后返回一个Post对象。因此类型声明成为
<Post, User, Post>
把所有东西放在一起,就像这样:
var sql =
@"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
var post = data.First();
Assert.Equal("Sams Post1", post.Content);
Assert.Equal(1, post.Id);
Assert.Equal("Sam", post.Owner.Name);
Assert.Equal(99, post.Owner.Id);
Dapper可以通过假设Id列的名称为Id或Id来分割返回的行。如果您的主键不同,或者您想在Id以外的位置分割行,那么可以使用可选的splitOn参数。
多结果查询
Dapper允许您在一个查询中处理多个结果网格。
例子:
var sql =
@"
select * from Customers where CustomerId = @id
select * from Orders where CustomerId = @id
select * from Returns where CustomerId = @id";
using (var multi = connection.QueryMultiple(sql, new {id=selectedId}))
{
var customer = multi.Read<Customer>().Single();
var orders = multi.Read<Order>().ToList();
var returns = multi.Read<Return>().ToList();
...
}
存储过程
Dapper完全支持存储过程:
var user = cnn.Query<User>("spGetUser", new {Id = 1},
commandType: CommandType.StoredProcedure).SingleOrDefault();
如果你想要更花哨的,你可以这样做:
var p = new DynamicParameters();
p.Add("@a", 11);
p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output);
p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
cnn.Execute("spMagicProc", p, commandType: CommandType.StoredProcedure);
int b = p.Get<int>("@b");
int c = p.Get<int>("@c");
Ansi字符串和varchar
Dapper支持varchar params,如果你使用param在varchar列上执行where子句,请确保以这种方式传递:
Query<Thing>("select * from Thing where Name = @Name", new {Name = new DbString { Value = "abcde", IsFixedLength = true, Length = 10, IsAnsi = true });
在SQL Server上,在查询unicode时使用unicode,和在查询非unicode时使用ANSI是至关重要的。
每行类型切换
通常,您希望将给定表中的所有行视为相同的数据类型。
但是,在某些情况下,能够将不同的行解析为不同的数据类型是很有用的。这就是我成为玩家的地方。IDataReader.GetRowParser
迟早会派上用场。
假设您有一个名为“Shapes”的数据库表,列有:Id、Type和Data,并且您希望根据Type列的值将其行解析为Circle
, Square
, or Triangle
对象。
var shapes = new List<IShape>(); using (var reader = connection.ExecuteReader("select * from Shapes")) { // 您期望的每种类型生成一个行解析器。
// 通用类型<IShape>是解析器将返回的内容。
// 参数(typeof(*))是要解析的具体类型。
var circleParser = reader.GetRowParser<IShape>(typeof(Circle)); var squareParser = reader.GetRowParser<IShape>(typeof(Square)); var triangleParser = reader.GetRowParser<IShape>(typeof(Triangle)); var typeColumnIndex = reader.GetOrdinal("Type"); while (reader.Read()) { IShape shape; var type = (ShapeType)reader.GetInt32(typeColumnIndex); switch (type) { case ShapeType.Circle: shape = circleParser(reader); break; case ShapeType.Square: shape = squareParser(reader); break; case ShapeType.Triangle: shape = triangleParser(reader); break; default: throw new NotImplementedException(); } shapes.Add(shape); } }
在MySQL用户定义的变量
为了使用非参数SQL变量与MySql连接器,你必须添加以下选项到你的连接字符串:
Allow User Variables=True
请确保没有向Dapper提供要映射的属性。
限制和警告
Dapper缓存关于它运行的每个查询的信息,这允许它快速物化对象并快速处理参数。
当前实现将此信息缓存到一个ConcurrentDictionary对象中。只使用一次的语句通常会从该缓存中刷新。但是,如果您在不使用参数的情况下动态地生成SQL字符串,那么可能会遇到内存问题。
Dapper的简洁性意味着ORMs自带的许多功能都被去掉了。它关注95%的场景,并为您提供大多数情况下需要的工具。它并不试图解决所有的问题。
Dapper会与我的DB provider一起工作吗?
Dapper没有DB特定的实现细节,它可以跨所有。net ADO提供程序工作,包括SQLite, SQL CE, Firebird, Oracle, MySQL, PostgreSQL and SQL Server.
你有一个完整的例子列表吗??
Dapper有一个全面的测试套件:test project.
谁在用这个?
Dapper在生产中使用:Stack Overflow.
扩展:Dapper To Linq框架
此框架是Dapper的扩展,效率优于EntityFramwork,并且支持.NetFramework和.NetCore框架
https://www.cnblogs.com/kogel/p/10805696.html
EntityFrameworkCore结合Dapper的使用
基本使用:
我们可以通过DbContext.Database.GetDbConnection() 获得 EF 使用的数据库连接对象 DbConnection,这允许我们使用 Dapper 。
与DbContext不同的是,使用 DbConnection 进行的 【写】 操作将直接在数据库生效,不需要调用 DbContext.SaveChanges()。
使用事务:
我们可以通过 DbContext.Database.BeginTransaction() 获取到 IDbContextTransaction 对象,但它不是 IDbTransaction,所以没法直接在 Dapper 中使用。
幸运的是微软提供了扩展方法 IDbContextTransaction.GetDbTransaction() 获取 IDbTransaction ,为了统一控制 EFCore 和 Dapper 的事务,所以在 Dapper 执行数据库操作时,必须将 IDbTransaction 传到 transaction 参数中。
posted on 2020-10-28 10:24 springsnow 阅读(2546) 评论(1) 编辑 收藏 举报