Dapper入门

Dapper

一、dapper是什么

dapper是一款轻量级的ORM(Object Relationship Mapper),它负责**数据库和编程语言之间的映射。SqlConnection,MysqlConnection,OracleConnection都是继承于DBConnection,而DBConnection又是实现了IDBConnection的接口。因为Dapper是对IDBConnection接口进行了方法扩展**,比如你看到的SqlMapper.cs,所以dapper支持多数据库。

二、dapper的简单使用

Dapper总结(一)—基本CRUD操作 (wjhsh.net)

https://www.cnblogs.com/nsky/p/8425410.html

0、准备的测试类
 1   //用户类
 2     public class UserInfo
 3     {
 4         public int UId { get; set; }//用户Id
 5         public string UserName { get; set; }//用户名
 6         public int Age { get; set; }//年龄
 7         public int RoleId { get; set; }//角色Id
 8 
 9     }
10     //角色类
11     public class RoleInfo
12     {
13         public int RId { get; set; }//角色Id
14         public string RoleName { get; set; }//角色名
15     }
1、execute方法,返回值为int类型,表示受影响行数
var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["connString"].ConnectionString);
 Model.Show insert = new Model.Show { name = "insert", remark = "新增" };
//以下注意一点:下面操作是基于多个文件来的,所以可能出现不同的字符串(例如:Show是一个表)
//插入数据
 2             //单条数据插入
 3             int result = conn.Execute("insert into userinfo values(@username,@age,@roleid)", new
 4             {
 5                 @UserName = "user1",   //是否加前缀 @ 都可以
 6                 Age = 20,
 7                 RoleId = 1
 8             });  // result=1
 9 
10             //多条数据插入
11             List<UserInfo> users=new List<UserInfo>(){
12                  new UserInfo(){UserName="user2",Age=22,RoleId=1},
13                  new UserInfo(){UserName="user3",Age=23,RoleId=1},
14                  new UserInfo(){UserName="user4",Age=24,RoleId=2},
15                                  };
int result2 = conn.Execute("insert into userinfo values(@username,@age,@roleid)", users); // result2=3    

 //匿名类也行
            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);
19         //修改
20             int rusultEdit = conn.Execute("update userinfo set age=25 where username=@username", new { UserName = "user0" });
			
			Model.Show insert = new Model.Show { id = 258, name = "insert", remark = "新增" };
			//传对象
			conn.Execute("update show set name='update' where id =@id", insert);
 //删除
     int id = 250;
 conn.Execute("delete show where id = @id", new { id }); //变量名id必须跟@id匹配。才能映射

23             int resultDel = conn.Execute("delete from userinfo where uid=@uid", new { UId = 6 });  //也可以
 
//删除也可以传对象
Model.Show insert = new Model.Show { id = 256, name = "71", remark = "新增" };
conn.Execute("delete show where id = @id and name=@name", insert); //也可以
2、Query方法,返回值为IEnumerable类型
 1     //带参数查询 例子:查询用户名为user1,年龄为20的用户[dapper自动可以Mapper到Object]
 2             IEnumerable<UserInfo> query1 = conn.Query<UserInfo>("select * from userinfo where age=@Age and username=@username", new{Age=20,UserName="user1"}) 
 //in查询    例子:查询年龄是20,21,或22的用户
 5             IEnumerable<UserInfo> query2=conn.Query<UserInfo>("select * from userinfo where age in @ages",new {ages=new int [3]{20,21,22}});
 				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 * froExecutem 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次
            }



//返回多个结果集   例子:查询用户列表和角色列表       
 8             var sql = "select * from userinfo; select * from roleinfo";
 9             var query3 = conn.QueryMultiple(sql);//GridReader类型
10             var usesList = query3.Read<UserInfo>();//IEnumberable类型
11             var roleList = query3.Read<RoleInfo>();

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

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

GridReader(网格读者)类型

这里需要注意 的是,参数部分顺序: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
在这里插入图片描述
这里用到了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
执行结果后。你会发现 info 中除了id有值,其余属性是没有获取到值的(i.address本应该有值)
在这里插入图片描述
看下Query方法有个默认值splitOn = “Id”
(ll in this we go right to left through the data reader in order to cope with properties that arel/
named the same as a subsequent primary key that we split on
在这种情况下,我们**从右到左**通过数据读取器来处理属性,这些属性与我们分割的后续主键相同。)

其实说白了就是:splitOn所采用的方法就是**对我们所要获得的属性列表list从右到左进行查找**,如果查找到了 **splitOn = “Id”**中的这个Id属性那么就将list一分为二,左边属于TFirst 右边属于TSecond.


我的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(以属性name进行分割)
修改代码:
在这里插入图片描述
执行结果正确了。info的id没值。是因为select 过滤掉了
在这里插入图片描述


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

可以**把多个类整合到一个类下面**。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
在这里插入图片描述

表连接查询
 1   //用户类
 2     public class UserInfo
 3     {
 4         public int UId { get; set; }//用户Id
 5         public string UserName { get; set; }//用户名
 6         public int Age { get; set; }//年龄
 7         public int RoleId { get; set; }//角色Id
 8 
 9     }
10     //角色类
11     public class RoleInfo
12     {
13         public int RId { get; set; }//角色Id
14         public string RoleName { get; set; }//角色名
15     }

//表连接查询  例子:查询用户信息和其对应的角色名
14         //1、返回强类型结果 
15             var sql2 = @"select u.username,u.age,u.uid ,r.rolename from userinfo as u join roleinfo as r on u.roleid=r.rid";
16             var result2 = conn.Query<UserInfo, RoleInfo, UserInfo>(sql2, (user, role) =>
17             {
18                 user.Role = role;
19                 return user;
20             }, splitOn: "RoleName");//splitOn参数表示分割,前边的是第一个对象的属性,后边的是第二个对象的属性
21 
22         //2、返回动态类型结果
23             var result3 = conn.Query(sql2);
24             foreach (var item in result3)
25             {
26                 Console.WriteLine("username:{0},rolename:{1}", item.username, item.rolename);
27             }
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();

存储过程的crud格式
创建存储过程

使用Transact-SQL语句创建存储过程

Transact-SQL语言使用CREATE PROCEDURE语句创建存储过程,其一般格式为:

CREATE PROC [ EDURE ] procedure_name [ ;number ]

[ @parameter data_type [ = default ],…]

AS

sql_statement

说明:

procedure_name:给出存储过程名。

number:为可选的整数,对同名的存储过程指定一个序号

@parameter:为存储过程的形参,@符号作为第一个字符来指定参数名。

data_type:指出参数的数据类型。

default:给出参数的默认值。

sql_statement:存储过程所要执行的SQL语句,它可以是一组SQL语句,可以包含流程控制语句等。

执行存储过程

zTransact-SQL语言使用EXECUTE语句执行存储过程,其一般格式为:

exec dbo.存储过程名 参数值;

一般在执行存储过程是,最好加上架构名称,例如 dbo.USP_GetAllUser 这样可以可以减少不必要的系统开销,提高性能。 因为如果在存储过程名称前面没有加上架构名称,SQL SERVER 首先会从当前数据库sys schema(系统架构)开始查找,如果没有找到,则会去其它schema查找,最后在dbo架构(系统管理员架构)里面查找。

存储过程的修改和删除

1**.修改存储过程Transact-SQL语言使用ALTER PROCEDURE语句修改存储过程,其一般格式为:**

大致同创建

 ALTER PROC [ EDURE ] procedure_name [ ;number ]

   **[ @parameter data_type [ = default ]********]**

AS 

   sql_statement

2**.删除存储过程**

Transact-SQL语言使用DROPPROCEDURE****语句删除存储过程,其一般格式为:

 ALTER PROC [ EDURE ] procedure_name [ ;number ]

   **[ @parameter data_type [ = default ]********]**

AS 

   sql_statement

6-5】 删除存储过程score_find

DROP PROCEDURE score_find







.Net调用存储过程
①不需要获取返回值和输出参数

我这里存储过程,就不在编写了,用之前的列子 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");

执行结果:
在这里插入图片描述
也许你看不明白,这些值的说明。那看看我存储过程的逻辑
在这里插入图片描述

存储过程及触发器

sql servre 帮助文档中对存储过程的解释
  创建存储过程。存储过程是已保存的 Transact-SQL 语句集合,或对 Microsoft .NET Framework 公共语言运行时 (CLR) 方法的引用,
  可接收并返回用户提供的参数。可以创建过程供永久使用,或在一个会话(局部临时过程)中临时使用,或在所有会话(全局临时过程)中临时使用
创建存储过程可以**用 proc或者procedure关键字** proc是简写

编写个简单的存储过程,没有任何参数和返回值的存储过程
USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL --存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
AS
SELECT * FROM Show

执行sql语句成功后,在sql中可以看到存储过程已经创建

OBJECT_ID(object_name,'object_type);函数用于判断对象是否存在
object_type对应的类型如下
  AF = Aggregate function (CLR)
  C = CHECK constraint --检查约束
  D = DEFAULT (constraint or stand-alone)
  F = FOREIGN KEY constraint --外键约束
  FN = SQL scalar function --函数
  FS = Assembly (CLR) scalar-function
  FT = Assembly (CLR) table-valued function
  IF = SQL inline table-valued function
  IT = Internal table
  P = SQL Stored Procedure --存储过程
  PC = Assembly (CLR) stored-procedure --CLR存储过程
  PG = Plan guide
  PK = PRIMARY KEY constraint --主键约束
  R = Rule (old-style, stand-alone) --规则
  RF = Replication-filter-procedure
  S = System base table --数据库
  SN = Synonym
  SQ = Service queue
  TA = Assembly (CLR) DML trigger --CLR触发器
  TF = SQL table-valued-function
  TR = SQL DML trigger --DML触发器
  U = Table (user-defined) --数据表
  UQ = UNIQUE constraint --唯一约束
  V = View --视图
  X = Extended stored procedure

比如上面的判断存储过程是否存在 OBJECT_ID('proc_show01','P') 获取存储过程名为:proc_show01
判断表是否存在 OBJECT_ID('info','U') 获取表名为:info
或者:
select * from sysobjects where name='info' and type='u'

SQL Server支持五种类型的完整性约束
  NOT NULL (非空)–防止NULL值进入指定的列,在单列基础上定义,默认情况下,ORACLE允许在任何列中有NULL值.
  CHECK (检查)–检查在约束中指定的条件是否得到了满足.
  UNIQUE (唯一)–保证在指定的列中没有重复值.在该表中每一个值或者每一组值都将是唯一的.
  PRIMARY KEY (主键)–用来唯一的标识出表的每一行,并且防止出现NULL值,一个表只能有一个主键约束.
  POREIGN KEY (外部键)–通过使用公共列在表之间建立一种父子(parent-child)关系,在表上定义的外部键可以指向主键或者其他表的唯一键.

在.Net转编写测试代码。调用刚创建的存储过程

/// 执行存储过程
        /// </summary>
        /// <param name="cmdText">存储过程的名称</param>
        /// <param name="param">存储过程参数</param>
        /// <returns></returns>

        public static DataTable GetPro2(string cmdText, params SqlParameter[] param)
        {
            DataTable dt = new DataTable();
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                SqlCommand cmd = new SqlCommand(cmdText, conn); //数据库处理函数命令
                // 指定执行语句为存储过程
                cmd.CommandType = CommandType.StoredProcedure;
                if (param != null && param.Length != 0)
                {
                    cmd.Parameters.AddRange(param);
                }
                SqlDataAdapter dp = new SqlDataAdapter(cmd);
                //填充dataTable
                dp.Fill(dt);
            }
            return dt;
        }

在这里插入图片描述

编写一个有输入参数的存储过程
USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL--存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @name nvarchar(20)
)
AS
SELECT * FROM Show where @name=Name

编写存储过程的可空参数,当存储过程参数有默认值的时候,
那么.net在调用存储过程的时候。可以不用传参数。否则如果不传这会报错
所以:
除非定义了参数的默认值或者将参数设置为等于另一个参数,否则用户必须在调用过程时为每个声明的参数提供值

USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL --存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @name nvarchar(20) = null --默认值为空
)
AS
IF @name is null
begin
    set @name='刘德华' --当没有传值的时候。设置默认值
end
select * from Show where @name = name
--测试。当不传值的时候,默认查询的是 “刘德华”
创建有输入参数和输出参数的存储过程
USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL--存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @id int output, --输出参数,输出id
    @name nvarchar(20) = null --默认值为空
)
AS
IF @name is null
begin
    set @name='刘德华' --当没有传值的时候。设置默认值
    set @id=0
    select * from Show where name = @name
    --return
end
else
    begin
        set @id=(select ID from Show where name = @name)
        select * from Show where name = @name
    end

既然有输出参数。那么得修改上面的.Net代码。如下

/// <summary>
        /// 调用存储过程
        /// </summary>
        /// <param name="cmdText">存储过程名称</param>
        /// <param name="v1">输出参数(这里是ID)</param>
        /// <param name="param">参数</param>
        /// <returns></returns>
        public static DataTable GetPro1(string cmdText, out int v1, params SqlParameter[] param)
        {
            DataTable dt = new DataTable();
            using (SqlConnection conn = new SqlConnection(connStr))
            {
                SqlCommand cmd = new SqlCommand(cmdText, conn);
                // 指定执行语句为存储过程
                cmd.CommandType = CommandType.StoredProcedure;
                if (param != null && param.Length != 0)
                {
                    cmd.Parameters.AddRange(param);
                }
                SqlDataAdapter dp = new SqlDataAdapter(cmd);
                //dp.SelectCommand.Parameters.AddRange(param);
                dp.Fill(dt);

                /*
                  获取输出参数。
                 * 这里在组装参数的时候。指定了数组的第一个数是输出参数
                 */
                v1 = Convert.ToInt32(param[0].Value);           //也可以通过SqlCommand根据参数名称获取存储过程的返回值,跟param[0].Value是同样的效果                object id = cmd.Parameters["@id"].Value;
            }
            return dt;
        }

在组装参数的时候**。要指定哪些参数是输入参数。哪些是输出参数,默认是输入参数(Input)。**
如果是输入(或者输入输出)参数。则必须要赋值。在存储过程中没有给默认值的情况下
在下图可以看出。有输入参数有两个 Input 和 InputOutput
.Net 有个ParameterDirection 枚举类
在这里插入图片描述

sql 中有out 输出参数,output 输入输出参数

CREATE PROC proc_show01
(
    @id int output, --输入输出参数,输出id
    @ck int out,
    @name nvarchar(20) = null --默认值为空 ,默认是输入参数
)

但在.Net都是output。因为output就是输入输出参数的总称
param[3].Direction = ParameterDirection.Output

测试代码

 static void Main(string[] args)
        {
            //SqlParameter p = new SqlParameter("@name", "张三");

            //拼装参数 (定义一个参数对象)
            SqlParameter[] param = { 
                                        new SqlParameter("@id",SqlDbType.Int),
                                        new SqlParameter("@name",SqlDbType.NVarChar)
                                   };

            //设置参数是输出参数
            param[0].Direction = ParameterDirection.Output;
            param[1].Value = "王五";
            int id;
            DataTable dt = SQLHelper.GetPro1("proc_show01", out id, param); //没有参数
        }

运行看结果
在这里插入图片描述
上面用 param[0].Direction = ParameterDirection.Output; 指定第一个参数是输出参数
然后通过 v1 = **Convert*.ToInt32(param[0].Value); 获取存储过程返回的值

从ParameterDirection枚举可以看出输入输出参数都测试过了。还有一个操作的返回值没有测试

有操作类返回值的存储过程
USE TestInfo
GO
IF OBJECT_ID('proc_show01','P') IS NOT NULL--存储过程是否存在 
DROP PROCEDURE proc_show01 --删除存储过程
GO
CREATE PROC proc_show01
(
    @id int output, --输出参数,输出id
    @name nvarchar(20) = null --默认值为空
)
AS
/*
定义一个变量,返回一个值 
也可以不定义变量,直接用return 返回
*/
declare @returnValue int 
IF @name is null
begin
    set @name='刘德华' --当没有传值的时候。设置默认值
    set @id=0
    select * from Show where name = @name
    set @returnValue=10
    --return 0 --这里同样可以
end
else
    begin
        set @id=(select ID from Show where name = @name)
        select * from Show where name = @name
        set @returnValue=11
    end
return @returnValue --返回值

用 ParameterDirection.ReturnValue 指定是返回值
在这里插入图片描述

GetPro1则要加一个out 参数
public static DataTable GetPro1(string cmdText, out int v1,out int v2, params SqlParameter[] param)
则: v2 = Convert.ToInt32(param[1].Value);

测试看效果
在这里插入图片描述
存储过程里面也可以执行一个新增操作。然后返回刚新增的ID,比如:

begin
    set @name='刘德华' --当没有传值的时候。设置默认值
    --set @id=0
    select * from Show where name = @name
    set @returnValue=10
    --return 0 --这里同样可以
    insert Show(Name,Remark)VALUES('新增','地球');
    set @id = @@IDENTITY --返回新增的ID
end

上面都是通过.net代码访问存储过程,那么通过Transact-SQL语句怎么执行呢?
Transact-SQL 语句用exec(简写)关键字 ,全称是execute关键字
因为存储过程的参数顺序是
在这里插入图片描述
所以可以这样直接传参数,但顺序必须根据存储过程定义参数顺序一样
在这里插入图片描述
上面只是获取结果集。那么怎获取输出参数(outPut)和返回值呢(ReturnValue)
因为返回值是方法的返回值。所以可以变量名=存储过程名称 是不是跟.Net中很相似?

declare @m int --定义变量接收值
declare @result int 
exec @result=[proc_show01] @name='赵六',@id=@m output --一定要注名是output
select @m as '输出参数',@result as '返回值'

执行结果
在这里插入图片描述
其实就是参数名= 值 @name=‘赵六’,@id=@m 的方式。因为
一旦使用了 ‘@name = value’ 形式之后,所有后续的参数就必须以 ‘@name = value’ 的形式传递。
用这种方式。顺序可以不用跟存储过程中定义参数的顺序相同
如果不用@name = value’的方式同样可以,
但这样直接传参数,但顺序必须根据存储过程定义参数顺序一样返回值放在最前面

DECLARE @name varchar
--set @name='张三'
DECLARE @id int
DECLARE @result int
EXEC @result = proc_show01 @id output,'张三' --这里传参数的顺序一定要对应
--EXEC @result = proc_show01 @id output,@name 
select @id as '输出参数',@result as '返回值'

结果一样,其实不一样。因为传的参数不一样,哈哈哈
在这里插入图片描述

使用老师提供的SqlMapperUtil.cs来学习

基于的是控制台

GetMaxID

SqlMapperUtil.cs代码

        /// <summary>
        /// return specific table max ID
        /// 字段名FieldName要符合max()函数的要求
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="TableName"></param>
        /// <param name="connectionName"></param>
        /// <returns></returns>
        public static int GetMaxID(string FieldName, string TableName, string connectionName = null) {
            string sql = "select max(" + FieldName + ") from " + TableName;
            using (SqlConnection cnn = GetOpenConnection(connectionName)) {
                return cnn.Query<int>(sql, null).Single();  //严格返回一个元素项,否则抛出异常
            }
        }

Program.cs代码

Console.WriteLine(SqlMapperUtil.GetMaxID("sname", "student"));

InsertSqlWithReturnId

SqlMapperUtil.cs代码

 //返回新插入行的主键Id值
        public static int InsertSqlWithReturnId(string sql, dynamic parms, string connectionName = null) {
            using (SqlConnection cnn = GetOpenConnection(connectionName)) {
                return cnn.Query<int>(sql + ";SELECT CAST(SCOPE_IDENTITY() as int);", (object)parms).Single(); //SCOPE_IDENTITY()取得返回在当前会话中的任何表内所生成的最后一个标识值(返回值为十进制(38,0),所以采用强制类型转换)
            }
        }

Program.cs代码

var sql = "insert into Student values(@Sno,@Sname,@Ssex,@Sbirthday,@Sclass)";
int num = SqlMapperUtil.InsertSqlWithReturnId(sql, new
{
    Sno = "118",
    Sname = "张三",
    Ssex = "男",
    Sbirthday = "2022/7/2",
    Sclass = "研20"
});
Console.WriteLine(num);

Exists

SqlMapperUtil.cs代码

        public static bool Exists(string sql, dynamic parms, string connectionName = null) {
            using (SqlConnection cnn = GetOpenConnection(connectionName)) {
                return cnn.Query<int>(sql, (object)parms).Single() > 0 ? true : false; //cnn.Query()返回值为IEnumerable类型
            }
        }

Program.cs代码

var sql = "insert into Student values(@Sno,@Sname,@Ssex,@Sbirthday,@Sclass)";
int num = SqlMapperUtil.InsertSqlWithReturnId(sql, new
{
    Sno = "118",
    Sname = "张三",
    Ssex = "男",
    Sbirthday = "2022/7/2",
    Sclass = "研20"
});
Console.WriteLine(num);
MultipleSql
 public static int MultipleSql<T>(string sql, IEnumerable<T> entities, string connectionName = null) where T : class, new() {
            using (SqlConnection cnn = GetOpenConnection(connectionName)) {
                int records = 0;

                foreach (T entity in entities) {
                    records += cnn.Execute(sql, entity);
                }
                return records;
            }
        }
string multsql = @"select * from Student where Sname=@Sname
                select * from Student where Sname=@Sname
                 select * from Student where Sname=@Sname"
;
var entities =new List<Student> { new { Sname = "李军"  } , "李军" , "李军" };  
Console.WriteLine(SqlMapperUtil.MultipleSql<Student>(multsql, entities));

未理解

SqlWithTransaction
        public static int SqlWithTransaction(string sql, dynamic parms = null, string connectionName = null) {
            using (SqlConnection cnn = GetOpenConnection(connectionName)) {
                int records = 0;
                //方法一
                using (var trans = cnn.BeginTransaction()) {  //开启数据库事物
                    try {
                        records = cnn.Execute(sql, (Object)parms, trans, 30, CommandType.Text); 
                        trans.Commit();  //提交事物
                    }
                    catch (DataException ex) {
                        trans.Rollback();   //事物回滚
                        throw ex;
                    }
                }
                //方法二TransactionScope不一定比方法一好,原因在: http://stackoverflow.com/questions/10689779/bulk-inserts-taking-longer-than-expected-using-dapper

                return records;
            }
        }
var sql = "select * from course";
Console.WriteLine(SqlMapperUtil.SqlWithTransaction(sql)); 

//demo2
var sql = "update Student set Ssex= '男' where Sno = '108'";
Console.WriteLine(SqlMapperUtil.SqlWithTransaction(sql)); 
StoredProcWithTransaction
public static int StoredProcWithTransaction(string procname, dynamic parms = null, string connectionName = null) {
            using (SqlConnection cnn = GetOpenConnection(connectionName)) {
                int records = 0;
                //方法一
                using (var trans = cnn.BeginTransaction()) {
                    try {
                        records = cnn.Execute(procname, (object)parms, trans, 30, CommandType.StoredProcedure);
                        trans.Commit();
                    }
                    catch (DataException ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
                //方法二TransactionScope不一定比方法一好 http://stackoverflow.com/questions/10689779/bulk-inserts-taking-longer-than-expected-using-dapper

                return records;
            }
        }
var parms = new DynamicParameters();
parms.Add("@Sname", "李军");
Console.WriteLine(SqlMapperUtil.StoredProcWithTransaction("getStudentBySname", parms));

//方法2
Console.WriteLine(SqlMapperUtil.StoredProcWithTransaction("getStudentBySname", new{Sname=”李军“}));
CREATE PROCEDURE [dbo].[getStudentBySname]
	 @Sname nvarchar(20)
AS
begin
	select *
	from dbo.Student
	where @Sname=Sname;
end
go 
--通过他们的Sname来查找一个特定的用户

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWSsxfRs-1668686915130)(https://s2.loli.net/2022/10/26/iNdeJPKIOpGft7Z.png)]

ToDataTable
 //由一个list列表得到一个table
        public static DataTable ToDataTable<T>(this IList<T> list) {
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); //获取指定类型组件的属性集合。
            DataTable table = new DataTable();
            for (int i = 0; i < props.Count; i++) {  //创建表格
                PropertyDescriptor prop = props[i];
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }
            object[] values = new object[props.Count];
            foreach (T item in list) {          //表格中添加数据
                for (int i = 0; i < values.Length; i++)
                    values[i] = props[i].GetValue(item) ?? DBNull.Value;
                table.Rows.Add(values);
            }
            return table;
        }

GetParametersFromObject
        /// <summary>
        /// 由一个对象去生成一个动态参数袋
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyNamesToIgnore"></param>
        /// <returns></returns>
public static DynamicParameters GetParametersFromObject(object obj, string[] propertyNamesToIgnore) {
            if (propertyNamesToIgnore == null) propertyNamesToIgnore = new string[] { String.Empty };
            DynamicParameters p = new DynamicParameters();  //构造一个动态参数袋
            PropertyInfo[] properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); //GetType() 当前实例的确切运行时类型

            foreach (PropertyInfo prop in properties) {
                if (!propertyNamesToIgnore.Contains(prop.Name))
                    p.Add("@" + prop.Name, prop.GetValue(obj, null));
            }
            return p;
        }

SetIdentity
public static void SetIdentity<T>(IDbConnection connection, Action<T> setId) {
            dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single();
            T newId = (T)identity.Id;
            setId(newId);
        }
GetPropertyValue
        public static object GetPropertyValue(object target, string propertyName) {
            PropertyInfo[] properties = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);//获得当前实例的确切运行时类型的属性组

            object theValue = null;
            foreach (PropertyInfo prop in properties) {
                if (string.Compare(prop.Name, propertyName, true) == 0) {
                    theValue = prop.GetValue(target, null);
                }
            }
            return theValue;
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值