• Dapper


    本文内容

    • 特点
    • 性能
    • 参数化的查询
    • List 支持
    • 缓存和非缓存的 readers
    • 多个映射
    • 多个结果
    • 存储过程
    • Ansi Strings 和 varchar
    • 限制和注意事项
    • Dapper 能运行在我的 db 提供者上吗?
    • 有例子的完整例子列表吗?
    • 谁在使用 Dapper?
    • 参考

    跳槽了,新公司的数据库层,准确地说,数据库层和持久层使用 Dapper,这东西的确很方便~个人觉得这种方便性体现在三点:

    1. 能很方便地执行数据库 DML 和 DLL 操作。比如,当你执行一个带参数的 SQL 时,SQL 中的变量能与你传递给它的实体或匿名对象中的属性,自定匹配。而我们知道,带参数的 SQL,能提高数据库执行 SQL 的效率;
    2. 能很方便地将数据库检索结果映射为面向对象的对象。从数据库中的检索结果,通常是张二维表,如 DataTable,而应用程序中是实体类,以及实体类的集合,那么 Dapper 能够将 DataTable 自动地映射成为实体类的集合;
    3. 能很方便地书写 SQL 语句。比如,写多个 SQL,用分号分隔。

    下载 Demo

    (该下载包含 Dapper 项目,项目中有 Dapper 的测试示例和性能测试例子)

    (Dapper 的示例使用 SQLServer 数据库,我个人的示例是 MySQL)

    特点


    Dapper 只有一个文件,你可以把它拖到你的项目中,来扩展你的 IDbConnection 接口。

    它提供了三方面的帮助:

    执行一个查询,并把结果映射到一个强类型 list

    注意:所有的扩展方法都假设数据库连接已打开,如果关闭连接,它们将失败。

    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 });
     
    dog.Count()
        .IsEqualTo(1);
     
    dog.First().Age
        .IsNull();
     
    dog.First().Id
        .IsEqualTo(guid);

    执行一个查询,并把结果映射到一个动态 object 的 list

    public static IEnumerable<dynamic> Query (this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null, bool buffered = true)

    该方法将执行 SQL,并返回一个动态 list,即 var 变量。

    用法:

    var rows = connection.Query("select 1 A, 2 B union all select 3, 4");
     
    ((int)rows[0].A)
       .IsEqualTo(1);
     
    ((int)rows[0].B)
       .IsEqualTo(2);
     
    ((int)rows[1].A)
       .IsEqualTo(3);
     
    ((int)rows[1].B)
        .IsEqualTo(4);

    rows[0] 这种访问方式会出错,不知道示例是怎么给的~

    执行一个不返回结果的 Command

    public static int Execute(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null)

    用法:

    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 })
       .IsEqualTo(2);

    多次执行一个 Command


    相同的签名也可以让你方便高效地对一个命令执行多次,例如批量加载数据(bulk-load data)。

    用法:

    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 } }
      ).IsEqualTo(3); // 3 rows inserted: "1,1", "2,2" and "3,3"

    对任何类型实现 TIEnumerable 的参数都可以执行。

    性能


    Dapper 的主要特点是性能。以下数据显示对一个数据库执行 SELECT 出 500 条,并把数据映射到对象中需要多长时间。

    性能测试分为三个方面:

    • POCO 序列化框架,支持从数据库获得静态类型的对象。使用原始的 SQL。
    • 动态序列化框架,支持返回对象的动态列表。
    • 典型的框架用法。往往不会涉及编写 SQL。

    Performance of SELECT mapping over 500 iterations - POCO 序列化

    方法

    持续时间(毫秒)

    备注

    Hand coded (using a SqlDataReader)

    47

    Can be faster

    Dapper ExecuteMapperQuery

    49

    同上

    ServiceStack.OrmLite (QueryById)

    50

    同上

    PetaPoco 

    52

    同上

    BLToolkit

    80

    同上

    SubSonic CodingHorror

    107

    同上

    NHibernate SQL

    104

    同上

    Linq 2 SQL ExecuteQuery

    181

    同上

    Entity framework ExecuteStoreQuery

    631

    同上

    Performance of SELECT mapping over 500 iterations - dynamic 序列化

    方法

    持续时间(毫秒)

    备注

    Dapper ExecuteMapperQuery (dynamic)

    48

     

    Massive

    52

     

    Simple.Data

    95

     

    Performance of SELECT mapping over 500 iterations - 典型用法

    方法

    持续时间(毫秒)

    备注

    Linq 2 SQL CompiledQuery

    81

    Not super typical involves complex code

    NHibernate HQL

    118

     

    Linq 2 SQL

    559

     

    Entity framework

    859

     

    SubSonic ActiveRecord.SingleOrDefault

    3619

     

    参数化的查询


    参数可以作为匿名类来传递。这使你可以轻松地给命名参数,只要简单地剪切和粘贴 SQL 片断,并在查询分析器中执行即可。

    new {A = 1, B = "b"} // A will be mapped to the param @A, B to the param @B 

    List 支持


    Dapper 运行让你传递 IEnumerable,自动地参数化的查询。

    例如下面 SQL 的 in 查询:

    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

    缓存和非缓存的 readers


    Dapper 的默认动作是执行 SQL 并在返回时缓冲整个 reader。在大多数情况下,这是理想的,因为它能最大限度地减少数据库中的共享锁,以及减少数据库的网络时间。

    但是,在执行庞大查询时,你可能为了减少内存的占用,只加载需要的对象。要做到这点,通过缓冲到查询方法中。

    /// <summary>
    /// NonBuffered
    /// 将会抛出异常,Invalid attempt to Read when reader is closed.
    /// </summary>
    public static void TestBasicStringUsageAsyncNonBuffered()
    {
        var query = DbHelp.QueryAsync<string>(new CommandDefinition("select 'abc' as Value union all select @txt", new { txt = "def" }, flags: CommandFlags.None));
        var arr = query.Result.ToArray();
        arr.IsSequenceEqualTo(new[] { "abc", "def" });
    }
     
    /// <summary>
    /// Buffered
    /// 不会抛出异常
    /// </summary>
    public static void TestBasicStringUsageAsyncBuffered()
    {
        var query = DbHelp.QueryAsync<string>(new CommandDefinition("select 'abc' as Value union all select @txt", new { txt = "def" }, flags: CommandFlags.Buffered));
        var arr = query.Result.ToArray();
        arr.IsSequenceEqualTo(new[] { "abc", "def" });
    }
    /// <summary>
    /// Pipelined
    /// 将会抛出异常,Invalid attempt to Read when reader is closed.
    /// </summary>
    public static void TestBasicStringUsageAsyncPipelined()
    {
        var query = DbHelp.QueryAsync<string>(new CommandDefinition("select 'abc' as Value union all select @txt", new { txt = "def" }, flags: CommandFlags.Pipelined));
        var arr = query.Result.ToArray();
        arr.IsSequenceEqualTo(new[] { "abc", "def" });
    }

    多个映射


    Dapper 允许你把单行映射到多个对象。如果你想避免额外的查询和加载关联,那么这个功能就很关键了。

    例如:

    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();
     
    post.Content.IsEqualTo("Sams Post1");
    post.Id.IsEqualTo(1);
    post.Owner.Name.IsEqualTo("Sam");
    post.Owner.Id.IsEqualTo(99);

    提示: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).First();}}}

    如果你想要更灵活的操作,可以这样做:

    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 Strings 和 varchar


    Dapper 支持 varchar 参数,如果你在一个 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。

    限制和注意事项


    对于 Dapper 执行的每个查询的缓存信息,使得它能够快速地物化对象和处理参数。当前的实现把信息缓存在一个 ConcurrentDictionary 对象中。它存储的对象永远不会被刷新。如果你生成的 SQL 字符串没有使用参数,那么可能会出现命中内存问题。我们把字典转换成 LRU(Least Recently Used)缓存。

    ORM 的很多特点都被 Dapper 去掉了,没有身份地图(Identity Map),没有更新/选择的助手等。

    Dapper 不会管理你连接的生命周期,它假定它得到的连接是打开的,并且不存在 DataReader 枚举(除非启用 MARS)。

    什么是 Mars?它是在创建数据库连接时指定的,下面是 Dapper 中连接 SQL Server 的示例:

    public static SqlConnection GetOpenConnection(bool mars = false)
    {
        var cs = connectionString;
        if (mars)
        {
            SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(cs);
            scsb.MultipleActiveResultSets = true;
            cs = scsb.ConnectionString;
        }
        var connection = new SqlConnection(cs);
        connection.Open();
        return connection;
    }

    如果指定了 Mars,那么还会创建 SqlConnectionStringBuilder,并指定其 MultipleActiveResultSets 属性为 true。不过,看 Dapper 的例子,貌似 SQL Server 是有 Mars 的,但 MySQL 没有。

    Dapper 能运行在我的 db 提供者上吗?


    Dapper 能运行在所有 .net ado 提供者上,包括 sqlite,sqlce,firebird,oracle,MySQL,PostgreSQL 和 SQL Server。

    有例子的完整例子列表吗?


    Dapper 在测试项目中有完整的测试套件。

    谁在使用 Dapper?


    目前使用 Dapper 的有 Stack Overflowhelpdesk

    (if you would like to be listed here let me know)

    参考资料


     

    下载 Demo

  • 相关阅读:
    广商博客冲刺第六七天new
    广商博客沖刺第一天(new ver):
    20150529项目之典型用户与场景
    冲刺第二,三,四,五,六天
    冲刺第一天:
    Android 學習之旅!(2)
    20150512 作业6 团队项目之需求
    Android 學習之旅!(1)
    20150421 作业5 四则运算 测试与封装 5.1 5.2(doing)
    20150421 作业5 四则运算 测试与封装 5.1
  • 原文地址:https://www.cnblogs.com/liuning8023/p/4363910.html
Copyright © 2020-2023  润新知