代码改变世界

Dapper基础入门

2018-02-07 14:22  糯米粥  阅读(2443)  评论(0编辑  收藏  举报

Dapper是一个轻量级的ORM。之前最常用的ORM是EF,其实EF底层是Ado.net实现的。

现在基本上已经远离SqlHelper时代了。

Dapper是开源的  https://github.com/StackExchange/Dapper

那从基本的增删改查开始

配置好文件

 

在Nuget可以直接导入

 

 

引入:using Dapper;

 

当前使用的版本

 

获取连接字符串

 var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["connString"].ConnectionString);

 

Dapper支持多数据库的本质是因为Dapper是对IDBConnection接口进行了方法扩展,比如你看到的SqlMapper.cs,一旦你这样做了,我们也知道,

SqlConnection,MysqlConnection,OracleConnection都是继承于DBConnection,而DBConnection又是实现了IDBConnection的接口

 

 

Insert 

 var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["connString"].ConnectionString);

            Model.Show insert = new Model.Show { name = "insert", remark = "新增" };

            //insert 
            conn.Execute("insert into show (Name,remark)values(@name,@remark)", insert);

            conn.Execute("insert into show (Name,remark)values(@name,@remark)", new
            {
                @name = insert.name, //是否加前缀 @ 都可以
                remark = insert.remark
            });

            List<Model.Show> ls = new List<Model.Show> { 
                new Model.Show{name="1",remark="2"},
                new Model.Show{name="11",remark="21"},
                new Model.Show{name="12",remark="22"}
            };

            //批量插入
            conn.Execute("insert into show (Name,remark)values(@name,@remark)", ls);

            //匿名类也行
            var anonymous = new List<dynamic> { 
                new {name="51",remark="2"},
                new {name="61",remark="2"},
                new {name="71",remark="2"},
                new {name="81",remark="2"},
            };
            //批量插入
            int r1 = conn.Execute("insert into show (Name,remark)values(@name,@remark)", anonymous);

 

delete

 int id = 250;
 conn.Execute("delete show where id = @id", new { id }); //变量名id必须跟@id匹配。才能映射
 conn.Execute("delete show where id = @id", new { @id = 251 }); //也可以
//删除也可以传对象
Model.Show insert = new Model.Show { id = 256, name = "71", remark = "新增" };
conn.Execute("delete show where id = @id and name=@name", insert); //也可以

 

update

conn.Execute("update show set name='update' where id =@id", new { id = 252 });
 Model.Show insert = new Model.Show { id = 258, name = "insert", remark = "新增" };
            
//传对象
conn.Execute("update show set name='update' where id =@id", insert);

 

Query

查询也有多种方式,首先看简单的查询

 //query 无参数查询 
  var list1 = conn.Query("select * from show");

当然。一般实际应用中。都是返回便于操作的实体类,SqlHelper时代是。获取Table转Model,而dapper自动可以Mapper到Object

conn.Query<Model.Show>("select * from show where id in @ids", new { @ids = new[] { 1, 2, 3 } })

 

这样写法也行

int[] ids = { 1, 2, 3 };
var list3 = conn.Query<Model.Show>("select * from show where id in @ids", new { ids });

 

或者:

conn.Query<Model.Show>("select * from show where id in (@ids)", new { ids=252 });

 

可以看到。这样写的方式  in 后面的有括号和没有括号的区别

 

 

dapper 多表查询,dapper可以实现多个sql一起查询,然后返回多个结果。需要用QueryMultiple 方法

比如:三个条件的sql

string multsql = @"select * from show where id=@id
                               select * from show where name=@name
                               select * from show where remark=@remark";

当然。我这里都是from show ,你可以from A  from B from C 都是可以的

 

       //多表查询 这里不单单是一个表的查询,可以是多个表 from A from B... 
            string multsql = @"select * from show where id=@id
                               select * from show where name=@name
                               select * from show where remark=@remark";
            SqlMapper.GridReader gridReader = conn.QueryMultiple(multsql, new { id = 1, name = "张三", remark = "海南" });
            if (!gridReader.IsConsumed) //没有释放 true代表释放
            {
                var g1 = gridReader.Read<Model.Show>(); //转实体类
                var g2 = gridReader.Read<Model.Show>();
                var g3 = gridReader.Read<Model.Show>();

                //var g4 = gridReader.Read(); //基本读取方式,只返回3个表。不能读第4次
            }

 

可以看到。QueryMultiple 有两种读取方式 ,很方便

 var g3 = gridReader.Read<Model.Show>(); //转实体类
 var g4 = gridReader.Read(); //基本读取方式

 

这里需要注意 的是,参数部分顺序:new { id = 1, name = "张三", remark = "海南" }

需要跟sql 中参数的顺序是一一对应的。 id=@id   name=@name   remark=@remark

Read读取的顺序也需要返回的顺序一样,返回几个表。就只能Read几次

 

query join 操作

dapper 提供了join操作,

比如我现在有两个表join

select * from Show s right join info i on s.Name = i.Name

 

 

实体类

 

代码:

var ss1 = conn.Query<Model.Show, Model.info, Model.Show>("select * from Show s right join info i on s.Name = i.Name", (show, info) =>
            {
                show.info = info;
                return show;
            });

看看Query参数,一个泛型委托  Func

 

执行看看效果,跟上面sql一样的结果

 

 

这里用到了select *  这样查询效率是不可取的,

在实际中。我们一般会select 出需要的字段,或者指定字段。或者过滤不需要的字段

比如:select s.Name,s.remark,s.id,i.name,i.address,i.id from Show s right join info i on s.Name = i.Name

var ss = conn.Query<Model.Show, Model.info, Model.Show>("select s.Name,s.remark,s.id,i.name,i.address,i.id from Show s right join info i on s.Name = i.Name", (show, info) =>
             {
                 show.info = info; //赋值
                 return show;
             });

执行结果后。你会发现 info 中除了id有值,其余属性是没有获取到值的

 

看下Query方法有个默认值, splitOn = "Id"

 

 

 

 

 

我的sql中select s.Name,s.remark,s.id,i.name,i.address,i.id

Dapper找到了最后一个id。来分割读取数据。分割后。左边是前面的表(show)右边是后面的表(info),

也就是方法中的 TFirst 和TSecond

然后分别映射

这就是为什么show有值,而info只有id有值的原因

 

为了进一步测试我说的正确性。我修改sql :select s.Name,s.remark,s.id,i.name,i.address

如果按照上面说的。s.id应该是show表的id。那么分割后,映射到了info表,执行看看结果,是正确的

 

要解决这个问题。就只能手动指定splitOn的值。这里应该是splitOn:name

修改代码:

执行结果正确了。info的id没值。是因为select 过滤掉了

 

当然。这样仅仅 是两个表join。如果是三个表,或者更多。在项目中还是有的吧,2个以上的表join。这样就不可取了

可以把多个类整合到一个类下面。show和info都有同名 的name。所以需要取别名

 

修改sql 

select s.Name,s.remark,s.id  ,i.name as infoName,i.address as infoAddress,i.id as infoId from Show s right join info i on s.Name = i.Name

 

 var ss3 = conn.Query<Model.result>("select s.Name,s.remark,s.id  ,i.name as infoName,i.address as infoAddress,i.id as infoId from Show s right join info i on s.Name = i.Name");

 执行结果:

 

注意一点:

上面说了  splitOn 默认是id  分割。如果sql中没有指定id。则需要手动指定。否则会报错

比如:我sql中没有默认的id

 

 

dapper事物

  模拟一个删除数据失败,事物回滚的操作

假设删除 id=259的数据

 

            //创建一个事物
            using (var trans = conn.BeginTransaction()) //开启数据库事物
            {
                try
                {
                    conn.Execute("delete show where id = @id", new { id = 259 }, trans);

                    int a = 0;
                    int b = 5 / a; //此处会异常,导致事物回滚
                    trans.Commit();  //提交事物
                }
                catch (Exception ex)
                {
                    //事物回滚
                    trans.Rollback();
                }
            }

运行发现报错:无效操作。连接被关闭。

 

 因为dapper在CRUD操作中会自动判断连接是否打开:ConnectionState.Closed

 

而事物不会,则需要手动打开连接

conn.Open(); //先打开连接

事物已经提交。但没有Commit前不会生效,在cath中回滚事物 trans.Rollback();

 

存储过程

我这里存储过程,就不在编写了,用之前的列子 http://www.cnblogs.com/nsky/p/7766653.html

用CommandType.StoredProcedure 标记是存储过程

如果只执行存储过程。不需要获取返回值和输出参数。直接这样既可

 //不获取输出参数
var qq = conn.Query<Model.Show>("[proc_show01]", new { id = 1, ck = 1 }, commandType: CommandType.StoredProcedure);

执行结果:

 

如果考虑安全,可以用参数化

 DynamicParameters dp = new DynamicParameters();
            dp.AddDynamicParams(new { @id = 1 });
            dp.AddDynamicParams(new { @ck = 1 });
            //不获取输出参数
            var qq = conn.Query<Model.Show>("[proc_show01]", dp, commandType: CommandType.StoredProcedure);

 

DynamicParameters  还有一个参数的构造函数

可以这样写:

 DynamicParameters dp = new DynamicParameters(new { id = 1,ck=1 });

 

需要获取输出参数和返回值

 

//获取输出参数
            DynamicParameters dp = new DynamicParameters();
            dp.Add("@id", 0, DbType.Int32, ParameterDirection.Output); //输出参数。
            dp.Add("@ck", 0, DbType.Int32, ParameterDirection.Output); //输出参数
            dp.Add("@returnValue", 0, DbType.Int32, ParameterDirection.ReturnValue); //返回值
            var qq1 = conn.Query<Model.Show>("[proc_show01]", dp, commandType: CommandType.StoredProcedure);
            var ck = dp.Get<int>("@ck"); //输出参数
            var id2 = dp.Get<int>("@id"); //输出参数,是新增后的id值
            var returnValue = dp.Get<int>("@returnValue");

执行结果:

 

也许你看不明白,这些值的说明。那看看我存储过程的逻辑

 

因为仅仅是测试。所以代码中的命名没有那么规范~~~~

 

参考:

http://www.cnblogs.com/yiting/p/5600262.html

https://yq.aliyun.com/articles/40182

https://www.cnblogs.com/huangxincheng/p/5828470.html

https://www.cnblogs.com/dunitian/p/5221058.html