赞
踩
前一篇博客介绍了EFCore
中常见的一些查询操作,使用Linq
或Lambda
结合实体类的操作相当方便。但在某些特殊情况下,我们仍旧需要使用原生SQL
来获取数据。好在EFCore
中提供了完整的方法支持原生SQL
,下面开始介绍。
与之前一样,还是使用Author
和Book
数据表,它们是一对多的关系,AuthorId
为Book
表中的外键。
Author
表数据如下所示:
Id | Name | Gender | Age | |
---|---|---|---|---|
1 | 张三 | 男 | 35 | 11111111@qq.com |
2 | 李四 | 女 | 40 | 22222222@qq.com |
3 | 王五 | 男 | 37 | 33333333@qq.com |
Book
表数据如下所示:
Id | Title | Press | PublicationTime | Price | AuthorId |
---|---|---|---|---|---|
1 | 《C程序设计》 | A出版社 | 2022-01-01 | 30 | 1 |
2 | 《C++程序设计》 | B出版社 | 2022-02-02 | 45 | 1 |
3 | 《Java程序设计》 | C出版社 | 2022-03-03 | 60 | 2 |
4 | 《C#程序设计》 | D出版社 | 2022-04-04 | 55 | 2 |
Author
代码如下:
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; // Code scaffolded by EF Core assumes nullable reference types (NRTs) are not used or disabled. // If you have enabled NRTs for your project, then un-comment the following line: // #nullable disable namespace App.Models { public partial class Author { public Author() { Book = new HashSet<Book>(); } /// <summary> /// 主键 /// </summary> [Key] public int Id { get; set; } /// <summary> /// 姓名 /// </summary> [StringLength(20)] public string Name { get; set; } /// <summary> /// 性别 /// </summary> [StringLength(2)] public string Gender { get; set; } /// <summary> /// 年龄 /// </summary> public int? Age { get; set; } /// <summary> /// 邮箱 /// </summary> [StringLength(30)] public string Email { get; set; } /// <summary> /// 导航属性 /// </summary> [InverseProperty("Author")] public virtual ICollection<Book> Book { get; set; } } }
Book
代码如下:
using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; // Code scaffolded by EF Core assumes nullable reference types (NRTs) are not used or disabled. // If you have enabled NRTs for your project, then un-comment the following line: // #nullable disable namespace App.Models { public partial class Book { /// <summary> /// 主键 /// </summary> [Key] public int Id { get; set; } /// <summary> /// 书名 /// </summary> [StringLength(20)] public string Title { get; set; } /// <summary> /// 出版社 /// </summary> [StringLength(20)] public string Press { get; set; } /// <summary> /// 出版时间 /// </summary> [Column(TypeName = "datetime")] public DateTime? PublicationTime { get; set; } /// <summary> /// 价格 /// </summary> [Column(TypeName = "money")] public decimal? Price { get; set; } /// <summary> /// 外键:AuthorId /// </summary> public int? AuthorId { get; set; } /// <summary> /// 导航属性 /// </summary> [ForeignKey(nameof(AuthorId))] [InverseProperty("Book")] public virtual Author Author { get; set; } } }
DaoDbContext
代码如下:
using App.Models; using Microsoft.EntityFrameworkCore; // Code scaffolded by EF Core assumes nullable reference types (NRTs) are not used or disabled. // If you have enabled NRTs for your project, then un-comment the following line: // #nullable disable namespace App.Context { public partial class DaoDbContext : DbContext { public DaoDbContext() { } public DaoDbContext(DbContextOptions<DaoDbContext> options) : base(options) { } public virtual DbSet<Author> Author { get; set; } public virtual DbSet<Book> Book { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!optionsBuilder.IsConfigured) { optionsBuilder.UseSqlServer("Data Source=DSF-PC;Initial Catalog=Dao;User ID=sa;Password=123456;"); } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Book>(entity => { entity.HasOne(d => d.Author) .WithMany(p => p.Book) .HasForeignKey(d => d.AuthorId) .OnDelete(DeleteBehavior.Cascade) .HasConstraintName("FK_Book_Author"); }); OnModelCreatingPartial(modelBuilder); } partial void OnModelCreatingPartial(ModelBuilder modelBuilder); } }
如果是针对单表的查询操作,可以使用FromSqlInterpolated
方法,但是该方法有以下局限性:
Join
下面将查询查询Author
表中Name='张三'
且Age>30
的记录,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<List<Author>> Get() { return GetAuthors("张三", 30); } private List<Author> GetAuthors(string name, int age) { return _dbContext.Set<Author>() .FromSqlInterpolated(@$"select * from Author where Name={name} and Age>{age}") .ToList(); } } }
运行结果如下所示:
[{"id":1,"name":"张三","gender":"男","age":35,"email":"11111111@qq.com","book":[]}]
如果希望查询出Author
对应的Book
,也可以通过Include
实现,EFCore
支持FromSqlInterpolated
与Lambda
一起使用,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<List<Author>> Get() { return GetAuthors("张三", 30); } private List<Author> GetAuthors(string name, int age) { return _dbContext.Set<Author>() .FromSqlInterpolated(@$"select * from Author where Name={name} and Age>{age}") .Include(p => p.Book) .ToList(); } } }
运行结果如下所示:
[ { "id": 1, "name": "张三", "gender": "男", "age": 35, "email": "11111111@qq.com", "book": [ { "id": 1, "title": "《C程序设计》", "press": "A出版社", "publicationTime": "2021-01-01T00:00:00", "price": 30.0000, "authorId": 1 }, { "id": 2, "title": "《C++程序设计》", "press": "B出版社", "publicationTime": "2021-02-02T00:00:00", "price": 45.0000, "authorId": 1 } ] } ]
针对单表的查询也可以使用FromSqlRaw
方法,与FromSqlInterpolated
类似,该方法也必须返回全部列,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<List<Author>> Get() { return GetAuthors("张三", 30); } private List<Author> GetAuthors(string name, int age) { SqlParameter[] parameters = { new SqlParameter(@"Name", name), new SqlParameter(@"Age", age) }; return _dbContext.Set<Author>() .FromSqlRaw("select * from Author where Name=@Name and Age>@Age", parameters) .ToList(); } } }
运行结果如下所示:
[{"id":1,"name":"张三","gender":"男","age":35,"email":"11111111@qq.com","book":[]}]
FromSqlRaw
方法也可以与Lambda
一起使用,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<List<Author>> Get() { return GetAuthors(1); } private List<Author> GetAuthors(int id) { SqlParameter[] parameters = { new SqlParameter(@"Id", id), }; return _dbContext.Set<Author>() .FromSqlRaw("select * from Author where Id>@Id", parameters) .OrderByDescending(p => p.Age) .Include(p => p.Book) .ToList(); } } }
运行结果如下所示:
[ { "id": 2, "name": "李四", "gender": "女", "age": 40, "email": "22222222@qq.com", "book": [ { "id": 3, "title": "《Java程序设计》", "press": "C出版社", "publicationTime": "2021-03-03T00:00:00", "price": 60.0000, "authorId": 2 }, { "id": 4, "title": "《C#程序设计》", "press": "D出版社", "publicationTime": "2021-04-04T00:00:00", "price": 55.0000, "authorId": 2 } ] }, { "id": 3, "name": "王五", "gender": "男", "age": 37, "email": "33333333@qq.com", "book": [] } ]
如果希望执行增删改等非查询操作,可以使用ExecuteSqlInterpolated
方法,下面给Author
表添加一条记录,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<string> Get() { int result = AddAuthor(new Author { Name = "AAA", Gender = "男", Age = 33, Email = "44444444@qq.com" }); return result > 0 ? "添加数据成功" : "添加数据失败"; } private int AddAuthor(Author author) { string name = author.Name; string gender = author.Gender; int age = author.Age.HasValue ? author.Age.Value : 0; string email = author.Email; return _dbContext.Database.ExecuteSqlInterpolated(@$"insert into Author(Name,Gender,Age,Email) values({name},{gender},{age},{email})"); } } }
运行结果如下所示:
添加数据成功
ExecuteSqlRaw
方法也可以执行增删改等非查询操作,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<string> Get() { int result = AddAuthor(new Author { Name = "BBB", Gender = "女", Age = 42, Email = "55555555@qq.com" }); return result > 0 ? "添加数据成功" : "添加数据失败"; } private int AddAuthor(Author author) { SqlParameter[] parameters = { new SqlParameter(@"Name", author.Name), new SqlParameter(@"Gender", author.Gender), new SqlParameter(@"Age", author.Age), new SqlParameter(@"Email", author.Email) }; return _dbContext.Database.ExecuteSqlRaw("insert into Author(Name,Gender,Age,Email) values(@Name,@Gender,@Age,@Email)", parameters); } } }
运行结果如下所示:
添加数据成功
如果希望执行数据库事务,可以使用BeginTransaction
方法,下面代码执行了一个包含Insert
和Update
的事务:
using App.Context; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<string> Get() { using (var transaction = _dbContext.Database.BeginTransaction()) { try { _dbContext.Database.ExecuteSqlRaw("insert into Author(Name,Gender,Age,Email) values('CCC','男',45,'66666666@qq.com')"); _dbContext.Database.ExecuteSqlRaw("update Author set Name='张三三' where Name='张三'"); transaction.Commit(); return "执行事务成功"; } catch { transaction.Rollback(); return "执行事务失败"; } } } } }
运行结果如下所示:
执行事务成功
上面的查询方法都必须返回全部列,而在实际开发过程中往往是按需查询列,因此我们还是需要自行封装一个SqlHelper
,代码如下:
SqlHelper.cs
代码:
using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Data; using System.Data.Common; namespace App { public class SqlHelper { /// <summary> /// 执行查询操作,返回DataTable /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="commandText">命令语句</param> /// <param name="commandType">命令类型</param> /// <param name="parameters">格式化参数集合</param> /// <returns>DataTable</returns> public static DataTable ExecuteQuery(DbContext dbContext, string commandText, CommandType commandType, params SqlParameter[] parameters) { DbConnection connection = dbContext.Database.GetDbConnection(); if (connection.State != ConnectionState.Open) { connection.Open(); } // 设置Command using DbCommand command = connection.CreateCommand(); command.CommandText = commandText; command.CommandType = commandType; if (parameters != null && parameters.Length > 0) { command.Parameters.AddRange(parameters); } // 查询数据 using SqlDataAdapter adapter = new SqlDataAdapter(command as SqlCommand); try { DataTable dataTable = new DataTable(); adapter.Fill(dataTable); return dataTable; } catch { return null; } finally { command.Parameters.Clear(); } } /// <summary> /// 执行查询操作,返回DbDataReader /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="commandText">命令语句</param> /// <param name="commandType">命令类型</param> /// <param name="parameters">格式化参数集合</param> /// <returns>DbDataReader</returns> public static DbDataReader ExecuteReader(DbContext dbContext, string commandText, CommandType commandType, params SqlParameter[] parameters) { DbConnection connection = dbContext.Database.GetDbConnection(); if (connection.State != ConnectionState.Open) { connection.Open(); } // 设置Command using DbCommand command = connection.CreateCommand(); command.CommandText = commandText; command.CommandType = commandType; if (parameters != null && parameters.Length > 0) { command.Parameters.AddRange(parameters); } // 返回DataReader try { return command.ExecuteReader(); } catch { return null; } finally { command.Parameters.Clear(); } } /// <summary> /// 执行查询操作,返回第一行第一列 /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="commandText">命令语句</param> /// <param name="commandType">命令类型</param> /// <param name="parameters">格式化参数集合</param> /// <returns>第一行第一列</returns> public static object ExecuteScalar(DbContext dbContext, string commandText, CommandType commandType, params SqlParameter[] parameters) { DbConnection connection = dbContext.Database.GetDbConnection(); if (connection.State != ConnectionState.Open) { connection.Open(); } // 设置Command using DbCommand command = connection.CreateCommand(); command.CommandText = commandText; command.CommandType = commandType; if (parameters != null && parameters.Length > 0) { command.Parameters.AddRange(parameters); } // 返回第一行第一列 try { return command.ExecuteScalar(); } catch { return null; } finally { command.Parameters.Clear(); } } /// <summary> /// 执行非查询操作,返回受影响的行数 /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="commandText">命令语句</param> /// <param name="commandType">命令类型</param> /// <param name="parameters">格式化参数集合</param> /// <returns>受影响的行数</returns> public static int ExecuteNonQuery(DbContext dbContext, string commandText, CommandType commandType, params SqlParameter[] parameters) { DbConnection connection = dbContext.Database.GetDbConnection(); if (connection.State != ConnectionState.Open) { connection.Open(); } // 设置Command using DbCommand command = connection.CreateCommand(); command.CommandText = commandText; command.CommandType = commandType; if (parameters != null && parameters.Length > 0) { command.Parameters.AddRange(parameters); } // 返回受影响的行数 try { return command.ExecuteNonQuery(); } catch { return 0; } finally { command.Parameters.Clear(); } } /// <summary> /// 执行数据库事务,返回受影响的行数 /// </summary> /// <param name="dbContext">数据库上下文</param> /// <param name="commands">命令集合</param> /// <returns>受影响的行数</returns> public static int ExecuteTransaction(DbContext dbContext, List<SingleCommand> commands) { DbConnection connection = dbContext.Database.GetDbConnection(); if (connection.State != ConnectionState.Open) { connection.Open(); } // 开启事务 using DbTransaction transaction = connection.BeginTransaction(); try { foreach (var item in commands) { DbCommand command = connection.CreateCommand(); command.CommandText = item.CommandText; command.CommandType = CommandType.Text; command.Transaction = transaction; if (item.Parameters.Count > 0) { command.Parameters.AddRange(item.Parameters.ToArray()); } command.ExecuteNonQuery(); } // 提交事务 transaction.Commit(); return 1; } catch { // 回滚事务 transaction.Rollback(); return 0; } } } }
SingleCommand.cs
代码:
using Microsoft.Data.SqlClient; using System.Collections.Generic; namespace App { public class SingleCommand { /// <summary> /// 命令语句 /// </summary> public string CommandText { get; set; } /// <summary> /// 格式化参数集合 /// </summary> public List<SqlParameter> Parameters { get; set; } } }
最后在Controller
调用即可,代码如下:
using App.Context; using App.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.SqlClient; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; namespace App.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthorController : ControllerBase { protected readonly DaoDbContext _dbContext; public AuthorController(DaoDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public ActionResult<DataTable> GetAuthorsById() { SqlParameter parameter = new SqlParameter(@"Id", 1); return SqlHelper.ExecuteQuery(_dbContext, "select Id,Name,Age from Author where Id>@Id", CommandType.Text, parameter); } [HttpGet] public ActionResult<List<Author>> GetAuthorsByAge() { List<Author> list = new List<Author>(); SqlParameter parameter = new SqlParameter(@"Age", 35); DbDataReader reader = SqlHelper.ExecuteReader(_dbContext, "select Id,Name,Age from Author where Age>@Age", CommandType.Text, parameter); while (reader.Read()) { list.Add(new Author { Id = Convert.ToInt32(reader["Id"]), Name = reader["Name"] == DBNull.Value ? null : Convert.ToString(reader["Name"]), Age = reader["Id"] == DBNull.Value ? new Nullable<int>() : Convert.ToInt32(reader["Age"]) }); } return list; } [HttpGet] public ActionResult<int> GetAuthorsCount() { object obj = SqlHelper.ExecuteScalar(_dbContext, "select count(*) from Author", CommandType.Text); return Convert.ToInt32(obj); } [HttpGet] public ActionResult<string> UpdateAuthorById() { SqlParameter[] parameters = { new SqlParameter(@"Id", 1), new SqlParameter(@"Email", "12345678@163.com") }; int result = SqlHelper.ExecuteNonQuery(_dbContext, "update Author set Email=@Email where Id=@Id", CommandType.Text, parameters); return result > 0 ? "修改邮箱成功" : "修改邮箱失败"; } [HttpGet] public ActionResult<string> GetTransactionResult() { List<SingleCommand> commands = new List<SingleCommand> { new SingleCommand() { CommandText = "insert into Author values(@Name,@Gender,@Age,@Email)", Parameters = new List<SqlParameter> { new SqlParameter(@"Name", "赵六"), new SqlParameter(@"Gender", "女"), new SqlParameter(@"Age", 39), new SqlParameter(@"Email", "12345678@163.com") } }, new SingleCommand() { CommandText = "update Author set Age=@Age where Name=@Name", Parameters = new List<SqlParameter> { new SqlParameter(@"Name", "张三"), new SqlParameter(@"Age", 59) } }, }; int result = SqlHelper.ExecuteTransaction(_dbContext, commands); return result > 0 ? "事务执行成功" : "事务执行失败"; } } }
GetAuthorsById
结果如下:
[
{"id":2,"name":"李四","age":40},
{"id":3,"name":"王五","age":37}
]
GetAuthorsByAge
结果如下:
[
{"id":2,"name":"李四","gender":null,"age":40,"email":null,"book":[]},
{"id":3,"name":"王五","gender":null,"age":37,"email":null,"book":[]}
]
GetAuthorsCount
结果如下:
3
UpdateAuthorById
结果如下:
修改邮箱成功
GetTransactionResult
结果如下:
事务执行成功
本文主要介绍了如何在EFCore
中执行原生SQL
的方法。在某些特殊情况下,直接执行原生语句往往会更方便且更高效,因此EFCore
虽好,也别忘了SQL
。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。