WebApi
    2020年2月15日
    21:46

    Restful语义风格

    Get 表示一个查询操作
    Post 表示一个提交操作
    Put 表示先进行Get操作,如果得不到预期结果,则进行Post操作
    Delete 表示一个删除操作
    1. Note:一般比较常用GetPostPutDelete看情况<br /> <br /> <br /> <br />路由的配置<br />2020313日<br />12:32<br /> <br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034378189-705581e6-0071-4a8a-af8e-e53e98bac839.png#height=291&width=779)<br /> <br /> <br />Restful语义风格<br />2020年3月13日<br />12:20<br /> <br />定义api方法名称的约定,比如Get(int id)即表示是以HttpGet方式请求的WebApi方法,Post(string name)即表示是以HttpPost方式请求的WebApi方法。<br /> <br />具体如下:
    Get 表示一个查询操作
    Post 表示一个提交操作
    Put 表示先进行Get操作,如果得不到预期结果,则进行Post操作
    Delete 表示一个删除操作
         Note:一般比较常用Get、Post方式,Put和Delete比较少<br /> <br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034378310-9fb214d8-e6f9-40b5-b3e5-1aca9a12a024.png#height=555&width=896)<br /> <br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034378536-a0d8d9ba-90a8-4f61-b2d9-2b330fa9c406.png#height=412&width=634)<br /> <br /> <br />跨域<br />2020年3月13日<br />12:40<br /> <br />为什么要跨域?<br />因为WebApi服务拒绝来自不同域名的ajax请求,注意仅ajax请求。<br /> <br />怎么跨域?<br />通过引入第三方包是最便捷的选择,Microsoft.AspNet.WebApi.Cors<br /> <br />1.引入架包;<br />2.启用跨域<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034378915-0788e84e-45a2-4852-859e-172de4e4b9b4.png#height=185&width=479)<br /> <br />3.设置跨域的范围(如上图为整体跨域,如下图为控制器跨域)<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034379119-6a892d1a-4950-4dd0-b87d-59258ba62de8.png#height=129&width=313)<br /> <br /> <br /> <br /> <br /> <br /> <br />身份验证和状态持久<br />2020年3月13日<br />12:47<br /> <br />与Web不同,WebApi是无状态的,不存在如Session的概念,故它的状态持久必须额外通过一些手段处理。<br />一种方式是在每次请求的请求头中增加一个无法篡改的加密串,通过此加密串进行身份验证同时也能解析其中的数据作为持久数据。<br />即大致过程如下:<br />1.登录,服务器返回一个加密字符串,包含了必要的验证信息<br />2.客户端收到验证信息,保存(比如cookie或者本地其他方式)<br />3.再次发送请求时,请求头加上了验证信息<br />4.服务器收到了请求时,解析请求头,验证是否通过并且解析得到必要信息<br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />操作总结<br />2020年3月13日<br />13:03<br /> <br />1.实际项目的路由规则最好不采用Restful,因为方法名可以作为重要的区分手段;<br />2.HttpGet和HttpPost的选择<br />(1)虽然查询一般是Get方法,但很多情况下是需要按一个动态变化的条件来执行查询的,在这种情况下当然使用对象的方式更好<br />(2)另一种比较常见的情况是提交,但参数很可能就是一个id或者简单类型的实参,这种情况以Get是不是也可以?<br />3.参数的封装和返回值的封装<br />有利于建立统一的处理逻辑,实现统一的请求和响应处理<br /> <br /> <br />LINQ TO SQL(DLINQ)<br />2020年1月4日<br />21:52<br /> <br />**概念:**<br />不论语言或框架,最终与数据库的交互都是通过ADO层,ADO层中进行的工作是高度相似的(1)连接数据库-执行-得到执行结果(2)执行结果映射实体对象或实体对象容器<br />LINQ TO SQL做的就是对这种简化,通过以下思路进行:<br />(1)定义映射关系,标识Mode与Table之间的关联,比如表名称,字段名称,主键等等,即System.Data.Linq.Mapping;<br />(2)执行映射关系、存储,即DataContext<br />(3)定义语法,执行增删改查,即LINQ<br /> <br />**映射关系**<br />以特性Attribute实现,在System.Data.Linq.Mapping中预先定义一些特性,比如<br />以Table特性标识对应的数据表名<br />以Column特性标识表中对象的字段属性,如名称、主键、类型、约束等等;<br />    [Table(Name = "sys_role")]<br />    public partial class SysRole<br />    {<br />        [Column(IsPrimaryKey = true)]<br />        public int id { get; set; }<br /> <br />        [Column]<br />        public string rolename { get; set; }<br /> <br />        [Column]<br />        public string roledesc { get; set; }<br />    }<br />具体实现未查,不作关系,肯定是通过特性实现一个通用逻辑(反射+特性)<br /> <br />**DataContext**<br />从构造函数上就可以看出来包括两个部分:<br />1.数据库连接,字符串或者连接类型都可以<br />2.映射关系,可以以特性标识或者自己建立<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034379288-69201676-8c08-4732-8da4-760347aa5dfa.png#height=55&width=417)<br /> <br />通过DataContext.GetTable方法得到以Table类型作为容器的对象集合<br />        private void Frm_User_Load(object sender, EventArgs e)<br />        {<br />            //DataContext以字符串初始化<br />            //DataContext ctx = new DataContext(connectionString);<br />            //Table<SysUser> users = ctx.GetTable<SysUser>();<br /> <br />            //DataContext以数据库链接初始化<br />            IDbConnection conn = new SqlConnection(connectionString);<br />            DataContext ctx = new DataContext(conn);<br />            Table<SysUser> users = ctx.GetTable<SysUser>();<br />        }<br /> <br /> <br />当然不可能这样写,以强类型的上下文类进行封装<br />    public class UserContext : DataContext<br />    {<br />        public Table<SysUser> Users;<br /> <br />        public Table<SysRole> Roles;<br /> <br />        public Table<SysUserRole> UserRoles;<br /> <br />        public UserContext(IDbConnection connection) : base(connection) { }<br /> <br />        public UserContext(string connection) : base(connection) { }<br />    }<br /> <br />一个项目只需要一个强类型的上下文,还是每个大业务需要,按什么原则?<br /> <br />**DataContext执行**<br />执行仍旧是以ADO方法实现的,也就是Connection、Command等等,DataContext中也公开了这些。<br />            UserContext ctx = new UserContext(connectionString);<br /> <br />            var select = from c in ctx.Roles where c.rolename.Contains("管理") select c;<br />            DbCommand cmd = ctx.GetCommand(select);<br /> <br />            richTextBox1.Text += cmd.CommandText + Environment.NewLine;<br />            foreach (DbParameter parm in cmd.Parameters)<br />            {<br />                richTextBox1.Text += string.Format("参数名:{0},参数值:{1}<br/>", parm.ParameterName, parm.Value) + Environment.NewLine;<br />            }<br /> <br />            dataGridView1.DataSource = select;<br /> <br />LINQ自动的将对象的操作翻译,虽然能实现90%以上的TSQL功能。但是不可否认对于复杂的查询,使用TSQL能获得更好的效率,DataContext类型也提供了执行SQL语句的能力<br /> <br />            UserContext ctx = new UserContext(connectionString);<br /> <br />            //直接执行SQL命令<br />            ctx.ExecuteCommand("update sys_role set roledesc={0}", "Default");<br /> <br />            //直接执行SQL查询,疑问:查询得不到值?<br />            var result = ctx.ExecuteQuery<object>("select * from sys_role");<br />            //var result = ctx.ExecuteQuery<SysRole>("select * from sys_role");<br /> <br />增、删、改直接对Context的对象集合进行操作,再通过提交(SubmitChanges)方法执行<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034379445-bde6f85c-28d5-4d24-8f81-82ee2ed70d44.png#height=178&width=368)<br /> <br />对当前对象中集合的变化可以通过<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034379627-ca082d75-863a-47f1-9290-2390881cb5e1.png#height=88&width=389)<br />            IList<object> inserts = ctx.GetChangeSet().Inserts;<br />            IList<object> deletes = ctx.GetChangeSet().Deletes;<br />            IList<object> updates = ctx.GetChangeSet().Updates;<br /> <br />DataContext的日志<br />上面说到DataContext自动做了SQL的翻译工作,同时它将翻译的SQL记录在DataContext的Log属性中<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034379931-621215d4-5ebf-4c3d-87da-50971391fd75.png#height=95&width=391)<br /> <br />那么在外部可以对SQL日志进行记录<br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034380144-b79ec23d-3410-4941-8838-5fc35136bba8.png#height=122&width=278)<br />日志的问题,什么时候写入,在程序设计中什么位置进行写入,需要额外考虑<br /> <br /> <br />EntityFramework<br />2019年12月4日<br />8:41<br /> <br />官方定义:“实体框架是一种对象关系映射器(O/RM),它使.NET开发人员能够通过.NET对象来操作数据库。它消除了开发人员通常需要编写的大多数数据访问代码的需求。“<br /> <br />实体框架工作在业务实体(域类)和数据库之间。**它保存实体属性中的数据到数据库,也可以从数据库中检索数据并自动将其转换为实体对象。**<br /> <br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034380291-7bdebbe1-88c7-45b2-bfbd-c9a55737bedd.png#height=304&width=251)<br /> <br />EF的组成简单总结如下:<br />**1、EDM(实体数据模型):** EDM包含三个主要部分——概念模型,映射和存储模型。<br />**概念模型(entity): **概念模型包含了模型类和它们之间的关系。 这将是独立于数据库表设计。<br />**存储模型(data):** 存储模型是数据库设计模型,包括表、视图、存储过程、以及它们之间的关系和钥匙。<br />**映射(mapping): **映射由概念模型如何映射到存储模型的信息组成。<br />**2、LINQ To Entity(L2E):** L2E是一种的查询实体对象的语言, 它返回在概念模型中定义的实体。 <br />**3、Entity SQL: **Entity SQL是一个类似于L2E的查询语言。 然而,它比L2E更加复杂。<br />**4、Object Services(对象服务):**对象服务是访问数据库中的数据并返回数据的主要入口点。它负责数据实例化,把**Entity Client Data Provider**(下一层)的数据转换成实体对象。<br />**5、Entity Client Data Provider:**主要职责是将L2E或Entity Sql转换成数据库可以识别的Sql查询语句,它通过**ADO.Net Data Provider**向数据库发送或者索取数据。<br />**6、ADO.Net Data Provider:**使用标准的Ado.net与数据库通信。<br /> <br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034380462-da0ad53d-a4dd-443d-bdab-dee51fe05d18.png#height=210&width=347)<br /> <br />EF的特性<br />1.**跨平台  **<br />EF Core是一个跨平台的框架,可以在Windows,Linux和Mac上运行。<br />2.**建模   **<br />EF可以创建具有不同数据类型get / set属性的EDM(Entity Data Model/实体数据模型)。它使用此模型查询或保存底层数据库的数据。<br />3.**查询    **<br />EF允许我们使用LINQ从底层数据库中检索数据,同时也支持直接对数据库执行原始SQL查询。<br />4.**更改跟踪 **<br />EF会跟踪需要提交到数据库的实体实例(属性值)发生的更改。<br />5.**保存 **    <br />EF调用SaveChanges()方法时,根据实体发生的更改,对数据库执行INSERT,UPDATE和DELETE命令。EF还提供了异步的SaveChangesAsync()方法。<br />6.**并发    **<br />默认情况下,从数据是从数据库中提取开始,EF使用乐观并发来避免我们做的修改被其他用户覆盖。<br />7.**事务   **  <br />EF在查询或保存数据时自动执行事务管理。它还提供自定义事务管理的选项。<br />8.**缓存   **  <br />EF包括开箱即用的第一级缓存。因此,重复查询将从缓存中返回数据,而不是访问数据库。<br />9.**配置**    <br />EF允许我们使用注释属性配置EF模型,也可以使用Fluent API来覆盖默认约定。<br />10.**迁移   **<br />EF提供了一组迁移命令,我们可以在NuGet Package Manager控制台或命令行界面中执行这些命令来创建或管理底层数据库计划。<br /> <br />EF的发展过程<br />三个阶段:DBFirst==ModelFirst==CodeFirst,目前只需要考虑CodeFirst的这种方式,包括接下来的例子都是按照CodeFirst方式的。<br /> <br />以下内容主要参考博文:[https://www.cnblogs.com/lsxqw2004/p/4701979.html](https://www.cnblogs.com/lsxqw2004/p/4701979.html)<br /> <br /> <br /> <br /> <br />AutoFac<br />2020年1月23日<br />15:57<br /> <br />**Autofac 中文文档  **[https://autofaccn.readthedocs.io/zh/latest/index.html](https://autofaccn.readthedocs.io/zh/latest/index.html)<br /> <br />严重性        代码        说明        项目        文件        行        禁止显示状态<br />错误                无法安装程序包“Microsoft.Extensions.Configuration.Json 3.1.1”。你正在尝试将此程序包安装到目标为“.NETFramework,Version=v4.6.1”的项目中,但该程序包不包含任何与该框架兼容的程序集引用或内容文件。有关详细信息,请联系程序包作者。                        0        <br /> <br /> <br />MVC<br />2019年12月4日<br />8:42<br /> <br /> <br /> <br /> <br /> <br />表达式(Expression)<br />2020年3月25日<br />21:19<br /> <br />[https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.expression?view=netframework-4.8](https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.expression?view=netframework-4.8)<br /> <br />![](https://cdn.nlark.com/yuque/0/2020/png/1294764/1590034380681-16ede791-08d8-4345-9cc6-32cbfe7e8088.png#height=205&width=644)<br /> <br /> <br />下面是平常使用最多的表达式<br />ConstantExpression:常量表达式<br />ParameterExpression:参数表达式<br />UnaryExpression:一元运算符表达式<br />BinaryExpression:二元运算符表达式<br />TypeBinaryExpression:is运算符表达式<br />ConditionalExpression:条件表达式<br />MemberExpression:访问字段或属性表达式<br />MethodCallExpression:调用成员函数表达式<br />Expression<TDelegate>:委托表达式<br /> <br />来自 <[_https://www.cnblogs.com/snailblog/p/11521043.html_](https://www.cnblogs.com/snailblog/p/11521043.html)><br /> <br /> <br /> <br /> <br /> <br />表达式树和表达式里面的类型NodeType是一个枚举,一共有85个类型,有兴趣的朋友可以去了解下。<br />常用的类型如下:<br />ExpressionType.And:C#中类似于&<br />ExpressionType.AndAlso:C#中类似于&&<br />ExpressionType.Or:C#中类似于|<br />ExpressionType.OrElse:C#中类似于||<br />ExpressionType.Equal:C#中类似于==<br />ExpressionType.NotEqual:C#中类似于!=<br />ExpressionType.GreaterThan:C#中类似于><br />ExpressionType.GreaterThanOrEqual:C#中类似于>=<br />ExpressionType.LessThan:C#中类似于<<br />ExpressionType.LessThanOrEqual:C#中类似于<=<br />ExpressionType.Add:C#中类似于+<br />ExpressionType.AddChecked:C#中类似于+<br />ExpressionType.Subtract:C#中类似于-<br />ExpressionType.SubtractChecked:C#中类似于-<br />ExpressionType.Divide:C#中类似于/<br />ExpressionType.Multiply:C#中类似于*<br />ExpressionType.MultiplyChecked:C#中类似于*<br /> <br />来自 <[_https://www.cnblogs.com/snailblog/p/11521043.html_](https://www.cnblogs.com/snailblog/p/11521043.html)><br /> <br /> <br /> <br /> <br /> <br />Dapper<br />2020年4月13日<br />9:37<br /> <br />**Dapper**<br />**简述:**<br />Dapper是一个轻量的ORM,主要是对ADO层做的封装,以Emit反射进行对象反序列化。<br />和EF比较起来,功能是不足的。<br />性能是很好的,可以作为ADO的替代。全部的操作都以SQL语句为基础,不能实现对象的动态跟踪。<br />Git:[https://github.com/StackExchange/Dapper](https://github.com/StackExchange/Dapper)<br />**2020-4-13**<br />**初步熟悉**<br />1.基于IDbConnection的一系列扩展方法,核心SqlMapper.cs,可以简单的理解为对ADO执行和对结果集转换为对象的操作。<br />2.在MSSQL操作上的性能几乎一致,不必考虑。比较耗时的是对象处理,这块需要再花时间了解。<br />3.虽然Dapper自动管理数据库连接,但要注意几点问题<br />//短短三行代码即实现了dapper连接的主动管理和自动管理<br />bool wasClosed = cnn.State == ConnectionState.Closed;//判断连接是否为关闭状态<br />...<br />if (wasClosed) cnn.Open();<br />...<br />if (wasClosed) cnn.Close();<br />(1)超过连接池最大连接数,一直自动管理或手动管理可以避免,手动管理注意close<br />(2)尤其是在事务处理时,因为事务要求连接打开,要在finally块进行close<br />//注意:BeginTransaction要求连接是Open状态,否则错误:无效操作。连接被关闭<br />IDbTransaction transaction = connection.BeginTransaction();<br />**4.下面以同步方法为例,但实际是支持异步方法的**<br />**CRUD**<br />**函数定义**<br />/// <summary>Execute parameterized SQL.</summary><br />/// <param name="cnn">The connection to query on.</param><br />/// <param name="sql">The SQL to execute for this query.</param><br />/// <param name="param">The parameters to use for this query.</param><br />/// <param name="transaction">The transaction to use for this query.</param><br />/// <param name="commandTimeout">Number of seconds before command execution timeout.</param><br />/// <param name="commandType">Is it a stored proc or a batch?</param><br />/// <returns>The number of rows affected.</returns><br />public static int Execute(<br />   this IDbConnection cnn,<br />   string sql,<br />   object param = null,<br />   IDbTransaction transaction = null,<br />   int? commandTimeout = null,<br />   CommandType? commandType = null)<br />{<br />   CommandDefinition command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, new CancellationToken());<br />   return cnn.ExecuteImpl(ref command);<br />}<br />**实例**<br />string sqlCommandText = @"INSERT INTO CICUser(Username,PasswordHash)VALUES(<br />                                   @UserName,@Password<br />                               )";<br />using (IDbConnection connection = new SqlConnection(DbHelper.Connection))<br />{<br />   int result = 0;
    

    //实例对象
    result = connection.Execute(sqlCommandText, new User
    {
    UserName = “jack”,
    Password = “jack”,
    });
    Console.WriteLine(result > 0 ? “Success” : “Error”);

    //匿名对象
    result = connection.Execute(sqlCommandText, new { UserName = “Paul”, Password = “Paul” });
    Console.WriteLine(result > 0 ? “Success” : “Error”);

    //字典 DynamicParameters是Dapper对字典的一个封装类型
    DynamicParameters dynamicParameters = new DynamicParameters();
    dynamicParameters.Add(“UserName”, “Chris”);
    dynamicParameters.Add(“Password”, “Chris”);
    result = connection.Execute(sqlCommandText, dynamicParameters);
    Console.WriteLine(result > 0 ? “Success” : “Error”);

    //集合 Dapper自动进行了迭代
    var list = Enumerable.Range(0, 10).Select(i => new User()
    {
    UserName = “User” + i,
    Password = “User” + i
    });
    result = connection.Execute(sqlCommandText, list);
    Console.WriteLine(result > 0 ? “Success” : “Error”);
    }
    string sqlCommandText = @”UPDATE CICUser SET Email=@Email WHERE UserName=@UserName”;
    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {
    int result = 0;

    //实例对象
    result = connection.Execute(sqlCommandText, new User
    {
    UserName = “jack”,
    Email = “jack.com”
    });
    Console.WriteLine(result > 0 ? “Success” : “Error”);

    //匿名对象
    result = connection.Execute(sqlCommandText, new { UserName = “Paul”, Email = “Paul.com” });
    Console.WriteLine(result > 0 ? “Success” : “Error”);

    //字典 DynamicParameters是Dapper对字典的一个封装类型
    DynamicParameters dynamicParameters = new DynamicParameters();
    dynamicParameters.Add(“UserName”, “Chris”);
    dynamicParameters.Add(“Email”, “Chris.com”);
    result = connection.Execute(sqlCommandText, dynamicParameters);
    Console.WriteLine(result > 0 ? “Success” : “Error”);

    //集合 Dapper自动进行了迭代
    var list = Enumerable.Range(0, 10).Select(i => new User()
    {
    UserName = “User” + i,
    Email = “User” + i + “.com”
    });
    result = connection.Execute(sqlCommandText, list);
    Console.WriteLine(result > 0 ? “Success” : “Error”);
    }
    string sqlCommandText = @”SELECT * FROM CICUser WHERE UserName in @Names”;
    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {
    var list = connection.Query(sqlCommandText, new { Names = new string[] { “jack”, “Paul”, “Chris” } }).ToList();

    list.ForEach(item => Console.WriteLine($”UserName:{item.UserName}——Password:” + item.Password));
    }
    参数解析
    1.可传递的参数类型一般是对象(实例或匿名),字典,对象集合(**List、Array)
    2.若传递的是对象集合,则Dapper在内部进行了隐式迭代,不需要再写循环!
    条件子句
    1.In子句在sql中不再需要’()‘
    CRUD之事务**
    string sqlCommandText = @”INSERT INTO CICUser(Username,PasswordHash)VALUES(
    @UserName,@Password
    )”;
    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {
    int result = 0;

    connection.Open();

    //注意:BeginTransaction要求连接是Open状态,否则错误:无效操作。连接被关闭
    IDbTransaction transaction = connection.BeginTransaction();

    //集合 Dapper自动进行了迭代
    var list = Enumerable.Range(0, 10).Select(i => new User()
    {
    UserName = “User” + i,
    Password = “User” + i
    });

    try
    {
    Enumerable.Range(0, 10).AsList().ForEach(i =>
    {
    connection.Execute(sqlCommandText, new User()
    {
    UserName = “User” + i,
    Password = “User” + i
    }, transaction);
    });

       transaction.Commit();<br />   }<br />   catch<br />   {<br />       transaction.Rollback();<br />   }<br />}<br />**CRUD之查询进阶**<br />**函数定义**<br /> /// <summary><br />   /// Perform a multi-mapping query with 2 input types.<br />   /// This returns a single type, combined from the raw types via <paramref name="map" />.<br />   /// </summary><br />   /// <typeparam name="TFirst">The first type in the recordset.</typeparam><br />   /// <typeparam name="TSecond">The second type in the recordset.</typeparam><br />   /// <typeparam name="TReturn">The combined type to return.</typeparam><br />   /// <param name="cnn">The connection to query on.</param><br />   /// <param name="sql">The SQL to execute for this query.</param><br />   /// <param name="map">The function to map row types to the return type.</param><br />   /// <param name="param">The parameters to use for this query.</param><br />   /// <param name="transaction">The transaction to use for this query.</param><br />   /// <param name="buffered">Whether to buffer the results in memory.</param><br />   /// <param name="splitOn">The field we should split and read the second object from (default: "Id").</param><br />   /// <param name="commandTimeout">Number of seconds before command execution timeout.</param><br />   /// <param name="commandType">Is it a stored proc or a batch?</param><br />   /// <returns>An enumerable of <typeparamref name="TReturn" />.</returns><br />   public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(<br />     this IDbConnection cnn,<br />     string sql,<br />     Func<TFirst, TSecond, TReturn> map,<br />     object param = null,<br />     IDbTransaction transaction = null,<br />     bool buffered = true,<br />     string splitOn = "Id",<br />     int? commandTimeout = null,<br />     CommandType? commandType = null)<br />   {<br />     return cnn.MultiMap<TFirst, TSecond, SqlMapper.DontMap, SqlMapper.DontMap, SqlMapper.DontMap, SqlMapper.DontMap, SqlMapper.DontMap, TReturn>(sql, (Delegate) map, param, transaction, buffered, splitOn, commandTimeout, commandType);<br />   }<br />参数解析:<br />1.map<br />委托,从源类型输出新类型的方法<br />2.splitOn<br />一个字符串,表示的是区分两个对象中的起始字段,默认是id。<br />比如A表(id,name,phone) 、B表(id,grade,class),传入“id”这样就可以区分好哪些字段是哪些表的,以供对象反序列化<br />**1对1查询实例**<br />string sqlCommandText = @"SELECT c.UserId,<br />c.Username AS UserName,<br />                                c.PasswordHash AS [Password],<br />                                c.Email,<br />                                c.PhoneNumber,<br />                                c.IsFirstTimeLogin,<br />                                c.AccessFailedCount,<br />                                c.CreationDate,<br />                                c.IsActive,<br />                                r.RoleId,<br />                                r.RoleName<br />                               FROM dbo.CICUser c<br />                               INNER JOIN CICUserRole cr ON cr.UserId = c.UserId<br />                               INNER JOIN CICRole r ON r.RoleId = cr.RoleId";
    

    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {

    var list = connection.Query(sqlCommandText,
    (user, role) =>
    {
    user.Role = role;
    return user;
    },
    null,
    null,
    true,
    “RoleId”,
    null,
    null).ToList();

    list.ForEach(item => Console.WriteLine($”UserName:{item.UserName}——RoleName:” + item.Role.RoleName));
    }
    1对多查询实例
    string sqlCommandText = @”SELECT c.UserId,
    c.Username AS UserName,
    c.PasswordHash AS [Password],
    c.Email,
    c.PhoneNumber,
    c.IsFirstTimeLogin,
    c.AccessFailedCount,
    c.CreationDate,
    c.IsActive,
    r.RoleId,
    r.RoleName
    FROM dbo.CICUser c
    INNER JOIN CICUserRole cr ON cr.UserId = c.UserId
    INNER JOIN CICRole r ON r.RoleId = cr.RoleId”;
    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {
    var lookUp = new Dictionary();
    var list = connection.Query(sqlCommandText,
    (user, role) =>
    {
    User u;
    if (!lookUp.TryGetValue(user.UserId, out u))
    {
    lookUp.Add(user.UserId, u = user);
    }

                   u.Role.Add(role);<br />                   return user;<br />                 }, null, null, true, "RoleId", null, null).ToList();<br />   var result = lookUp.Values;
    

    list.ForEach(item =>
    Console.WriteLine($”UserName:{item.UserName}——RoleName:” +
    string.Join(“,”, item.Role.Select(p => p.RoleName))));
    }
    批量查询-看看就可以了
    var sql = @”select from CICUser where UserID = @UserID;
    select
    from CICRole where RoleID = @RoleID”;

    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {
    using (var multi = connection.QueryMultiple(sql, new
    {
    UserID = 43,
    RoleID = 1
    }))
    {
    var m1 = multi.Read().ToList();
    var m2 = multi.Read().ToList();
    }
    }
    查询注意点
    using (IDbConnection connection = new SqlConnection(DbHelper.Connection))
    {
    connection.QueryFirstOrDefault(“SELECT from CICUser limit 1;”); //正确
    connection.QueryFirstOrDefault(“SELECT
    from CICUser;”); //错误
    connection.Query(“SELECT from CICUser;”).FirstOrDefault(); //错误
    connection.Query(“SELECT
    from CICUser;”).ToList().FirstOrDefault();//错误
    }