本节内容,部分为补充内容。主要NuGet包:
- Dapper(Dapper操作数据库)
- System.Data.SqlClient(连接数据库)
- Microsoft.EntityFrameworkCore.SqlServer(ORM的SqlServer数据库提供者)
- Microsoft.EntityFrameworkCore.Tools(ORM迁移工具)
- Swashbuckle.AspNetCore(生成WebApi接口文档)
一、EFCore和Dapper的区别
1、无论是EFCore,还是Dapper,底层都是通过ADO.NET访问数据库。区别在于,EFCore需要先将LINQ翻译成对应数据库的T-SQl语句,而Dapper直接输入T-SQL语句。
2、由于EFCore,增加了LINQ至SQL的翻译步骤,如果复杂一些的LINQ语句,机翻的结果可能不是最优的SQL语句,同时,EFCore会对实体类状态进行跟踪,所以性能会稍低于Dapper。但是EFCore抹平了数据库差异,我们可以使用统一的LINQ语句进行多种数据库的操作,同时借助编译器的智能提示,写起来也更加流畅。
3、尽管EFCore的LINQ语句写起来非常的流畅,但对于需要关联多表的复杂查询或报表,并不适合使用LINQ。对于这种情况,有两个选择,一是使用视图+EFCore;二是直接使用Dapper。现代应用开发的经验,都要求尽量保持数据库的干净,避免使用存储过程、外键、视图等,这些功能都应该在程序端来实现,所以推荐使用Dapper。
4、EFCore+Dapper,是一套比较理想的组合方案。EFCore做增、删、改和简单查询(.NET7已经出批量操作了),Dapper做复杂查询或报表查询。当然,在VS中直接写SQL是不太适合的,我们还是要借助数据库IDE,编写和调试SQL,推荐开源免费的DBeaver。
二、Dapper常用操作
1、安装Nuget包:Dapper和System.Date.SqlClient
2、实体类、DbContext、配置映射、数据库迁移、种子数据的增加等,和EFCore的操作一样,略过
3、在WepApi的控制器中,进行Dapper的CRUD操作
4、以下案例中涉及的实体类及其DbContext的映射配置:
//文章类和评论类组成聚合,一对多双向导航 //作者类为一个单独的聚合,和文章类没有导航关系 //在文章类中创建AuthorId属性,使两个文章类和作者类建议关系 //文章类 public class Article { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public List<Comment> Comments { get; set; } = new List<Comment>(); public int AuthorId { get; set; } } //评论类 public class Comment { public int Id { get; set; } public string Message { get; set; } public Article Article { get; set; } public int ArticleId { get; set; } } //作者类 public class Author { public int Id { get; set; } public string Name { get; set; } } //文章类和作者类为非导航关系,需要创建一个新的实体类来映射查询返回数据 public class ArticleWithAuthor { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public string AuthorName { get; set; } } //文章、评论和作者类的DbContext映射关系配置 //Article映射配置(Article-Comments聚合的一端) modelBuilder.Entity<Article>(b => { b.ToTable("T_Articles"); b.Property(a => a.Title).IsRequired().HasMaxLength(50).IsUnicode(); b.Property(a => a.Content).IsRequired().HasMaxLength(500).IsUnicode(); }); //Comment映射配置(Article-Comments聚合的多端) modelBuilder.Entity<Comment>(b => { b.ToTable("T_Comments"); b.Property(c => c.Message).IsRequired().HasMaxLength(500).IsUnicode(); b.HasOne<Article>(c=>c.Article).WithMany(a=>a.Comments).HasForeignKey(c=>c.ArticleId); }); //Author映射-另外一个聚合,不和Article设置导航关系 modelBuilder.Entity<Author>(b => { b.ToTable("T_Authors"); b.Property(c => c.Name).IsRequired().HasMaxLength(50); });
5、Dapper的增改删操作
[Route("api/[controller]/[action]")] [ApiController] public class TestController : ControllerBase { //依赖注入配置服务IConfiguration private readonly IConfiguration config; public TestController(IConfiguration config) { this.config = config; } //插入数据 [HttpPost] public async Task<ActionResult<string>> AddAuthor(string name) { //创建数据库连接对象SqlConnection using var connection = new SqlConnection(config.GetConnectionString("DefaultConnection")); //SQL查询语句,插入值@Name为占位参数,调整Dapper扩展方法时再赋值 var sql = "INSERT INTO T_Authors VALUES(@Name)"; //ExecuteAsync是Dapper的扩展方法,用于执行数据库的增删改命令 //通过匿名对象new{},给SQL语句中的占位参数赋值 var result = await connection.ExecuteAsync(sql, new { Name = name}); return Ok($"成功插入{result}条数据"); } //更新数据 [HttpPut] public async Task<ActionResult<string>> UpdateAuthorName(string oldName, string newName) { using var connection = new SqlConnection(config.GetConnectionString("DefaultConnection")); var sql = "UPDATE T_Authors SET Name = @NewName WHERE Name = @OldName"; var result = await connection.ExecuteAsync(sql, new { NewName = newName, OldName = oldName }); return Ok($"成功更新{result}条数据"); } //删除数据 [HttpDelete] public async Task<ActionResult<string>> DeleteAuthorById(int id) { using var connection = new SqlConnection(config.GetConnectionString("DefaultConnection")); var sql = "DELETE FROM T_Authors WHERE Id = @Id"; var result = await connection.ExecuteAsync(sql, new { Id = id }); return Ok($"成功删除{result}条数据"); } }
6、Dapper的SQL查询操作
public class TestController : ControllerBase { ...... //简单查询 [HttpGet] public async Task<ActionResult<List<Article>>> GetAllArticles() { ...... var articles = await connection.QueryAsync<Article>("SELECT * FROM T_Articles"); return Ok(articles); } //带参数查询 [HttpGet] public async Task<ActionResult<List<Article>>> GetArticleById(int id) { ...... var article = await connection.QueryAsync<Article>("SELECT * FROM T_Articles ta WHERE ta.Id = @Id", new {Id = id}); if (!article.Any()) { return BadRequest("请求的文章不存在!"); } return Ok(article); } //关联查询:非导航关系实体,需要创建一个ArticleWithAuthor实体,与查询语句结果进行一一映射 [HttpGet] public async Task<ActionResult<List<ArticleWithAuthor>>> GetAllArticlesWithAuthor() { ...... var sql = "SELECT ta.Id ,ta.Title ,ta.Content ,ta2.Name AS AuthorName FROM T_Articles ta LEFT JOIN T_Authors ta2 ON ta.AuthorId = ta2.Id "; var articlesWithAuthor = await connection.QueryAsync<ArticleWithAuthor>(sql); if (!articlesWithAuthor.Any()) { return BadRequest("请求的文章不存在!"); } return Ok(articlesWithAuthor); } //关联查询:双向导航一对多 [HttpGet] public async Task<ActionResult<List<Article>>> GetAllArticlesWithComments() { ...... var sql = "SELECT * FROM T_Articles ta INNER JOIN T_Comments tc ON ta.Id = tc.ArticleId "; var articlesWithComments = await connection.QueryAsync<Article, Comment, Article>(sql, (article, comment) => { article.Comments.Add(comment); return article; }); if (!articlesWithComments.Any()) { return BadRequest("请求的文章不存在!"); } return Ok(articlesWithComments); } }
代码解读:以上代码都比较简单,主要是针对导航关系的关联查询会复杂一点,主要对这段代码进行说明
①Dapper的提供了Query的非泛型、泛型、同步和异步方法
②Query泛型方法有最多7个重载,QueryAsync<T1,T2,...TN,TReturn>,用于最多7张表的关联查询。其中T1...TN为关联的数据表(实体),TReturn为指定哪个实体作为返回值
③QueryAsync方法的主要参数:
- 第一个:sql字符串,必填
- 第二个:泛型委托Func<T1,T2,...TN,TReturn>,必填。其中T1,...TN,和LINQ方法的参数差不多,可以理解为迭代的泛型参数的对象,方法必须返回TRturn对象
- 第三个参数为占位参数设置,如果有占位参数,则必填
- 其它参数比较少用,包括是否缓存、执行存储过程等,一般不需要设置,保存默认值即可
特别说明:
1、本系列内容主要基于杨中科老师的书籍《ASP.NET Core技术内幕与项目实战》及配套的B站视频视频教程,同时会增加极少部分的小知识点
2、本系列教程主要目的是提炼知识点,追求快准狠,以求快速复习,如果说书籍学习的效率是视频的2倍,那么“简读系列”应该做到再快3-5倍
标签:Core,ASP,EFCore2.10,get,set,EFCore,var,Dapper,public From: https://www.cnblogs.com/functionMC/p/16840706.html