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 Dapper Dapper Dapper MyGet
Dapper.Contrib Dapper.Contrib Dapper.Contrib Dapper.Contrib Dapper.Contrib MyGet
Dapper.EntityFramework Dapper.EntityFramework Dapper.EntityFramework Dapper.EntityFramework Dapper.EntityFramework MyGet
Dapper.EntityFramework.StrongName Dapper.EntityFramework.StrongName Dapper.EntityFramework.StrongName Dapper.EntityFramework.StrongName Dapper.EntityFramework.StrongName MyGet
Dapper.Rainbow Dapper.Rainbow Dapper.Rainbow Dapper.Rainbow Dapper.Rainbow MyGet
Dapper.SqlBuilder Dapper.SqlBuilder Dapper.SqlBuilder Dapper.SqlBuilder Dapper.SqlBuilder MyGet
Dapper.StrongName Dapper.StrongName Dapper.StrongName Dapper.StrongName Dapper.StrongName MyGet

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编辑  收藏  举报

导航