Dapper-一个简单的.Net对象映射器

它提供了3个助手:

执行查询并将结果映射到强类型列表

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);

执行查询并将结果映射到动态对象列表

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");

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);

执行一个不返回结果的命令

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"

这适用于任何实现IEnumerable<T>的参数 T

Performance

Dapper的一个主要特点是性能。 以下度量标准显示对数据库执行500条SELECT语句需要多长时间,并将返回的数据映射到对象。

性能测试分为3个列表:

  • 用于支持从数据库中提取静态类型对象的框架的POCO序列化。 使用原始的SQL.
  • 支持返回动态对象列表的框架的动态序列化.
  • 典型的框架使用。 通常典型的框架使用与最佳使用性能明显不同。 通常它不会涉及编写SQL.

SELECT映射超过500次迭代的性能 - POCO序列化

MethodDurationRemarks
Hand coded (using a SqlDataReader)47ms
Dapper ExecuteMapperQuery49ms
ServiceStack.OrmLite (QueryById)50ms
PetaPoco52msCan be faster
BLToolkit80ms
SubSonic CodingHorror107ms
NHibernate SQL104ms
Linq 2 SQL ExecuteQuery181ms
Entity framework ExecuteStoreQuery631ms

SELECT映射超过500次迭代的性能 - 动态序列化

MethodDurationRemarks
Dapper ExecuteMapperQuery (dynamic)48ms
Massive52ms
Simple.Data95ms

SELECT映射超过500次迭代的性能 - 典型用法

MethodDurationRemarks
Linq 2 SQL CompiledQuery81msNot super typical involves complex code
NHibernate HQL118ms
Linq 2 SQL559ms
Entity framework859ms
SubSonic ActiveRecord.SingleOrDefault3619ms

参数化查询

参数作为匿名类传入。 这使您可以轻松地为参数命名,并使您能够简单地剪切并粘贴SQL代码片段并在Query分析器中运行它们。

new {A = 1, B = "b"} // A将被映射到参数@A,B到参数@B

列表支持

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支持bool和数字类型的文字替换

connection.Query("select * from User where UserId = {=Id}", new {Id = 1}));

The literal replacement is not sent as a parameter; this allows better plans and filtered index usage but should usually be used sparingly and after testing. This feature is particularly useful when the value being injected
is actually a fixed value (for example, a fixed “category id”, “status code” or “region” that is specific to the query). For live data where you are considering literals, you might also want to consider and test provider-specific query hints like OPTIMIZE FOR UNKNOWN with regular parameters.
字面替换不作为参数发送; 这允许更好的计划和过滤的索引使用,但通常应该谨慎使用和测试之后。 这个特性在被注入的值时特别有用,实际上是固定的值(例如,固定的“category id”,“status code”或“region”,特定于查询)。 对于考虑使用文本的live数据,您可能会also想要考虑并测试特定于提供者的查询提示,例如 OPTIMIZE FOR UNKNOWN与常规参数。

有缓冲与无缓冲的读

Dapper的默认行为是执行你的sql并缓存整个读的返回。 这在大多数情况下是理想的,因为它可以最大限度地减少数据库中的共享锁,并减少数据库网络时间。

但是,当执行巨大的查询时,您可能需要最大限度地减少内存占用,并只根据需要加载对象。为此,将缓冲:false传递给Query方法。

多映射

Dapper允许您将单个行映射到多个对象。 如果你想避免多余的查询和负载关联,这是一个关键的功能。

示例:

考虑两个类:PostUser

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; }
}

现在让我们说,我们想要映射一个连接帖子和用户表的查询。 到目前为止,如果我们需要结合2个查询的结果,我们需要一个新的对象来表达它,但是在这种情况下,把User对象放在Post对象内更有意义。

这是多映射的用户案例。 你告诉Dapper,查询返回一个Post和一个User对象,然后给它一个函数来描述你想对每个包含PostUser对象的行做什么。 在我们的例子中,我们想把user对象放到post对象中。 所以我们写这个函数:

(post, user) => { post.Owner = user; return post; }

Query方法的3个参数指定了dapper应该使用哪些对象来反序列化行,以及返回的内容。 我们将把这两行解释为PostUser的组合,然后我们返回一个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 is able to split the returned row by making an assumption that your Id columns are named Id or id. If your primary key is different or you would like to split the row at a point other than Id, use the optional splitOn parameter.
Dapper能够通过假设您的Id列被命名为Idid来分割返回的行。 如果您的主键不同,或者您想在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参数,如果你正在使用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的ansi时使用unicode是至关重要的。

每行切换类型

Usually you’ll want to treat all rows from a given table as the same data type. However, there are some circumstances where it’s useful to be able to parse different rows as different data types. This is where IDataReader.GetRowParser comes in handy.

Imagine you have a database table named “Shapes” with the columns: Id, Type, and Data, and you want to parse its rows into Circle, Square, or Triangle objects based on the value of the Type column.

通常情况下,您会希望将给定表中的所有行视为相同的数据类型。 但是,在某些情况下,能够将不同的行解析为不同的数据类型是有用的。 这是IDataReader.GetRowParser派上用场的地方。

想象一下,你有一个名为Shapes的数据库表,其中包含Id, TypeData这些列,并且你想要将它的行分析成Circle, SquareTriangle 对象,基于Type列的值。

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);
    }
}

限制和警告

Dapper缓存每个查询的信息,这使得它能够快速实现对象并快速处理参数。 当前实现将这些信息缓存在一个ConcurrentDictionary对象中。 它存储的对象从不刷新。 如果您不使用参数即时生成SQL字符串,则可能会遇到内存问题。 我们可能会将字典转换为LRU缓存。

Dapper的简单性意味着ORM随附的许多功能都被剥离出来。 它担心95%的情况,并为您提供大部分时间需要的工具。 它不会试图解决所有问题。

Dapper会与我的数据库提供商合作吗?

Dapper没有数据库特定的实现细节,它适用于所有.NET ADO提供者,包括SQLite, SQL CE, Firebird, Oracle, MySQL, PostgreSQL 和SQL Server。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值