Wlkr.Core.EFCore

逆天,我在ef core中使用ado.net!

老派Sql之必要

  • 当你开发生涯中基本只用一两种数据库
  • 当你觉得用EF的类写报表时很别扭
  • 当你觉自己的Sql( Server)语句写得出神入化
  • 当你觉自己的Sql( Server)语句比EF生成的更优化
  • 当你刚从.net framework转.net core,还不知道sqlsugar和dapper

如上面所说,本项目在几年前,笔者刚转到.net core 3.1的开发中,编写了此项目。

当时觉得ef core这的很强大,编写业务代码时,效率提升极高,层次结构、逻辑代码都很清晰统一。

但是到了开发报表时,多表关联,奇葩条件组合就显示很别扭,各种奇怪的select,join,where等操作用于类中,令人抓狂,在sql语句中可能几行能写完的东西,夸张点可能得写上几十行,即便有linq和lambda的辅助,也没有直接写sql好用。

于是便诞生了此项目。

食用方式

EFCoreQueryHelper此类基本功能分为SqlQuery,SqlNonQuery,SqlScalar,Reader及DataSet。

每个类型有3中接口

  • 第一种直接使用string,不能防止SQL注入。
  • 第二种使用FormattableString,能防止SQL注入
  • 第三种我封装的SqlFormatter,实现像StringBuilder一样拼接FormattableString

Console项目“EFCoreSample”里面有一些使用示例

环境准备

  • 你需要Sql server,.net6 SDK
  • 在Console中默认没有从appsettings.json读取config的功能,此处我们自己先构造一个config。
//读取Config
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory) // 设置基础路径为应用程序根目录
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) // 加载 appsettings.json 文件
.AddEnvironmentVariables() // 可以添加环境变量的配置
.AddCommandLine(args) // 可以添加命令行参数的配置
.Build();
  • 然后构造一个DbContext

    平时都是web api项目里注入,Console差点不会写……
//配置DbContext
EFCoreQueryHelper.Configuration = configuration;
string constr = "Default";
DbContextOptionsBuilder<EFCoreSampleDbContext> optionsBuilder = new DbContextOptionsBuilder<EFCoreSampleDbContext>
();
optionsBuilder.UseSqlServer(configuration.GetConnectionString(constr));
using EFCoreSampleDbContext dbContext = new EFCoreSampleDbContext(optionsBuilder.Options);
  • 再执行迁移及种子数据
//自动迁移
if (dbContext.Database.GetPendingMigrations().Any())
dbContext.Database.Migrate();
//种子数据
void AddSeedData()
{
if (dbContext.TestModels.Any(t => t.Id == 1))
return;
for (var i = 1; i < 100; i++)
{
TestModel testModel = new TestModel()
{
//Id = i,
S = "S" + i.ToString(),
L = i,
B = i % 2 == 0 ? true : false,
D = i,
F = i,
G = Guid.NewGuid(),
CreateDate = DateTime.Now
};
dbContext.Add(testModel);
}
dbContext.SaveChanges();
}
AddSeedData();

测试环境已准备好,下面进入正题,该如何使用。

如何防SQL注入

防SQL注入的核心,是使用FormattableString,从中提取格式化的字符串,及其参数变量,从而转换为SqlParameter[],在ado.net里执行。

使用FormattableString

  • 方法名带Interpolated后缀的,都是防SQL注入
//查询示例
Console.WriteLine("防注入:");
List<TestModel> full = dbContext.SqlQueryDynamicInterpolated<TestModel>($"select * from TestModel where id = {id}");
Console.WriteLine("full:");
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(full));
Console.WriteLine();
//部分字段
List<TestModel> part = dbContext.SqlQueryDynamicInterpolated<TestModel>($"select S from TestModel where id = {id}");
Console.WriteLine("part:");
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(part));
Console.WriteLine();
//动态对象,本质是ExpandoObject
IEnumerable<dynamic> dyn = dbContext.SqlQueryDynamicInterpolated($"select S from TestModel where id = {id}");
Console.WriteLine("dyn:");
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(dyn));
Console.WriteLine();
  • 要注意区分FormattableString($"")和string("")的使用

    下面方法不带Interpolated,虽然也是用了FormattableString,但它最终会是string,没法防SQL注入。

    在传统的ado.net中,下面的{id}应该改为@p0,参数化才能防SQL注入。
//以下是纯粹的拼接字符串,不能防Sql注入,不推荐
full = dbContext.SqlQueryDynamic<TestModel>($"select * from TestModel where id = {id}", new SqlParameter[] { });
Console.WriteLine("不防注入:");
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(full));
Console.WriteLine();

使用SqlFormatter

$"" 的缺点是不能拼接如$"" + $"",它会变为string,导致不能防SQL注入。

此时需要想StringBuilder一样,封装一个可以Append字符串的类。

SqlFormatter sqlFormatter = new SqlFormatter("select * from TestModel where 1=1 ");
//参数化条件
if (true)
sqlFormatter.AppendLine_FmtStr($"and id = {id}");
//不需要参数化的条件
if (true)
sqlFormatter.AppendLine_Str("and S = 'S1'");
//主要看{},这也是参数化
if (true)
sqlFormatter.AppendLine_FmtStr($"and S = {"S" + id}");
sqlFormatter.AppendLine_FmtStr($"or L = {2}");
sqlFormatter.AppendLine_FmtStr($"or F = {3}");
sqlFormatter.AppendLine_FmtStr($"or D = {4}");
sqlFormatter.AppendLine_FmtStr($"or (B = {false} and id in (5,7,9) )");
Console.WriteLine(sqlFormatter.FormatedSql);
full = dbContext.SqlQueryDynamic<TestModel>(sqlFormatter.FormatedSql, sqlFormatter.Parameters);
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(full));
Console.WriteLine();

可以看到sqlFormatter.FormatedSql中已经将{p}转化为@p。

使用SqlPaging

报表开发中用的最多的当然是分页了,结合SqlFormatterSqlPagingPagingUtil,实现分页查询。

//分页例子
Console.WriteLine("SqlPaging分页:");
//先构建条件,约等于id为奇数的数据
sqlFormatter = new SqlFormatter();
sqlFormatter.AppendLine_FmtStr($"and t.B = {false}"); //除了等号逗号,写法基本与sql语句一致
SqlPaging sqlPaging = new SqlPaging()
{
db = dbContext,
Select = "*",
From = "TestModel t",
WhereBuilder = sqlFormatter,
OrderBy = " t.id desc"
}; //每页10条
full = sqlPaging.Execute<TestModel>();
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(sqlPaging.PagingUtil));
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(full));
Console.WriteLine(); //每页5条,第三页
sqlPaging.PagingUtil.PageSize = 5;
sqlPaging.PagingUtil.PageIdx = 3;
full = sqlPaging.Execute<TestModel>();
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(sqlPaging.PagingUtil));
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(full));
Console.WriteLine();

EF、SqlSugar、Dapper对比

除了开始不知道后面两个外,我喜欢EF的Migration功能,它比SqlSugar强。

而且使用EF,显得相当清真,它又是我自己写,自己用的类库,它不需要它复杂的功能,满足我日常使用即可。

反射方面的性能没有对比,暂略。

其他

既然是老派SQL,当然少不了DbFirst。

后续补充DbFirst转CodeFirst开发模式。

To Be Continue...

Author Info

DimWalker

2023 广州市增城区黯影信息科技部

https://www.dimtechstudio.com/

老派Sql之必要,逆天,我在ef core中使用ado.net!的更多相关文章

  1. EF Core中执行Sql语句查询操作之FromSql,ExecuteSqlCommand,SqlQuery

    一.目前EF Core的版本为V2.1 相比较EF Core v1.0 目前已经增加了不少功能. EF Core除了常用的增删改模型操作,Sql语句在不少项目中是不能避免的. 在EF Core中上下文 ...

  2. EF Core中Join可以进行子查询

    我们来看看下面的代码,这个代码是一个INNER JOIN的EF Core查询,其中用SubCategory表INNER JOIN了SubCategoryLanguage表,但是我们需要在SubCate ...

  3. EF Core 中DbContext不会跟踪聚合方法和Join方法返回的结果,及FromSql方法使用讲解

    EF Core中: 如果调用Queryable.Count等聚合方法,不会导致DbContext跟踪(track)任何实体. 此外调用Queryable.Join方法返回的匿名类型也不会被DbCont ...

  4. EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况

    使用EF Core时,如果多次从数据库中查询一个表的同一行数据,DbContext中跟踪(track)的实体到底有几个呢?我们下面就分情况讨论下. 数据库 首先我们的数据库中有一个Person表,其建 ...

  5. EF Core中如何正确地设置两张表之间的关联关系

    数据库 假设现在我们在SQL Server数据库中有下面两张表: Person表,代表的是一个人: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ...

  6. 第五节:EF Core中的三类事务(SaveChanges、DbContextTransaction、TransactionScope)

    一. 说明 EF版本的事务介绍详见: 第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges.DBContextTransaction.TransactionScope). 本节主 ...

  7. 在ef core中使用postgres数据库的全文检索功能实战之中文支持

    前言 有关通用的postgres数据库全文检索在ef core中的使用方法,参见我的上一篇文章. 本文实践了zhparser中文插件进行全文检索. 准备工作 安装插件,最方便的方法是直接使用安装好插件 ...

  8. [小技巧]EF Core中如何获取上下文中操作过的实体

    原文地址:https://www.cnblogs.com/lwqlun/p/10576443.html 作者:Lamond Lu 源代码:https://github.com/lamondlu/EFC ...

  9. EF Core中避免贫血模型的三种行之有效的方法(翻译)

    Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...

  10. EF Core中的多对多映射如何实现?

    EF 6.X中的多对多映射是直接使用HasMany-HasMany来做的.但是到了EF Core中,不再直接支持这种方式了,可以是可以使用,但是不推荐,具体使用可以参考<你必须掌握的Entity ...

随机推荐

  1. 自然语言处理 Paddle NLP - 结构化数据问答-理论

    NLP问答任务 相似度和规则匹配,都是早期的方法,现在主流的方法,都是基于生成的方法 结构化数据问答,有两种形式,一种是知识图谱形式.一种是关系型数据库形式. 主要应用在企业中,减少销售的成本 应用于 ...

  2. 使用numpy实现bert模型,使用hugging face 或pytorch训练模型,保存参数为numpy格式,然后使用numpy加载模型推理,可在树莓派上运行

    之前分别用numpy实现了mlp,cnn,lstm,这次搞一个大一点的模型bert,纯numpy实现,最重要的是可在树莓派上或其他不能安装pytorch的板子上运行,推理数据 本次模型是随便在hugg ...

  3. 添加.gitignore不生效问题

    1. 解决.gitignore不生效问题 把某些目录或文件加入忽略规则,按照上述方法定义后发现并未生效,原因是.gitignore只能忽略那些原来没有被追踪的文件,如果某些文件已经被纳入了版本管理中, ...

  4. asp.net core如何获取客户端IP地址

    客户端直接访问服务器 直接通过HttpContext.Connection.RemoteIpAddress获取客户端Ip [HttpGet] [Route("GetClientIP" ...

  5. MariaDB start 报错:mysql-bin.index' not found (Errcode: 2) (Errcode: 13)

    问题是修改配置log-bin=/data/mysql/binlog/mysql-bin后出现的. 报错:Errcode: 2 mkdir -p /data/mysql/binlog ## 和正常的DB ...

  6. 层叠样式表(CSS)1

    一.css的简介 1.层叠样式表的含义 层叠样式表:css是不仅是表现HTML的语言.还是进行样式修饰的语言 层叠:是对一个元素多次设置同一个样式,层层叠加覆盖,如不同的样式对一html标签进行修饰, ...

  7. Crawpy - 一款python写的网站目录扫描工具

    国外网站看到的. 简贴一下谷歌翻译的介绍 是什么让这个工具与其他工具不同: 它被写入异步工作,允许达到最大限制.所以它非常快. 校准模式,自行应用过滤器 有一堆标志可以帮助你详细地模糊 给定状态代码和 ...

  8. 在 ubuntu 中安装或升级最新版本的 Elasticsearch

    1. 安装 java 8 首先最新版本的elasticsearch需要java8的支持,那先来安装一下. $ sudo add-apt-repository -y ppa:webupd8team/ja ...

  9. HTML5CSS3基础

    目录 HTML5CSS3基础 1 2D 转换 1.1 二维坐标系 1.2 2D 转换之移动 translate 1.3 2D 转换之旋转 rotate 1.4 2D 转换中心点 transform-o ...

  10. Mysql高级7-存储过程

    一.介绍 存储过程是事先经过编译并存储在数据库中的一段sql语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的.存储过程思 ...