编写优雅代码,从挖掉恶心的if/else 开始
背景
长话短说, 作为开发人员经常需要根据条件灵活(过滤+排序)数据库,不管你是用rawsql 还是EFCore, 以下类似伪代码大家都可能遇到:
/// <summary>
/// 灵活过滤 能耗数据表 (rawsql)
/// </summary>
[Route("all")]
[HttpGet]
public async Task<List<CarEnergyModelEntity>> GetModeParametersAsync(
[FromQuery] string carVersion,
[FromQuery] string carId,
[FromQuery] string userId,
[FromQuery] string soVersion,
[FromQuery] string configVersion,
[FromQuery] string ConfigContent
)
{
StringBuilder strWhere = new StringBuilder(" 1=1 "); if (!string.IsNullOrEmpty(carVersion))
strWhere.Append($" and car_version='{carVersion}'");
if (!string.IsNullOrEmpty(carId))
strWhere.Append($" and car_id_='{carId}'");
if (!string.IsNullOrEmpty(userId))
strWhere.Append($" and user_id='{userId}'");
if (!string.IsNullOrEmpty(soVersion))
strWhere.Append($" and so_version='{soVersion}'");
if (!string.IsNullOrEmpty(configVersion))
strWhere.Append($" and config_version='{configVersion}'");
if (!string.IsNullOrEmpty(ConfigContent))
strWhere.Append($" and config_content='{ConfigContent}'"); var dt = new DataTable();
using (SqlConnection con = new SqlConnection("//connectStr//"))
{
var sql = $"select * from dbo.[car_energy_model] where {strWhere.ToString()}";
using (SqlCommand cmd = new SqlCommand(sql, con))
{
// TODO
}
}
}
/// <summary>
/// 灵活过滤 能耗数据表 (EFCore)
/// </summary>
[Route("all")]
[HttpGet]
public async Task<List<CarEnergyModelEntity>> GetModeParametersAsync1(
[FromQuery] string carVersion,
[FromQuery] string carId,
[FromQuery] string userId,
[FromQuery] string soVersion,
[FromQuery] string configVersion,
[FromQuery] string ConfigContent
)
{
var sqlQuery = _context.CarEnergyModels; if (!string.IsNullOrEmpty(carVersion))
sqlQuery = sqlQuery.Where(x=>x.CarVersion == carVersion);
if (!string.IsNullOrEmpty(carId))
sqlQuery = sqlQuery.Where(x => x.CarId == carId);
if (!string.IsNullOrEmpty(userId))
sqlQuery = sqlQuery.Where(x => x.UserId == userId);
if (!string.IsNullOrEmpty(soVersion))
sqlQUery = sqlQuery.Where(x => x.SoVersion == soVersion);
if (!string.IsNullOrEmpty(configVersion))
sqlQuery = sqlQuery.Where(x => x.ConfigVersion == configVersion);
if (!string.IsNullOrEmpty(ConfigContent))
sqlQuery = sqlQuery.Where(x => x.ConfigContent == ConfigContent); return sqlQuery.ToList();
}
特别是在大数据产品或者物联网产品中,字段甚多;需要 过滤/ 排序 的字段千变万化, if/else 写到死,一边写一边吐。
写出优雅漂亮的代码,从移除if/else 开始。
头脑风暴
从灵活查询的要求看,每一个字段都有为null 或 不为null 的可能, 以上伪代码6个字段, 理论上仅过滤字段最终执行查询时形成的sql 共有2^6= 64种可能, 还不算 灵活的排序字段。
现在我们要写这么多if 语法,是因为:
- 在编码阶段,强制判断字段存在, 并据此组装 rawsql
- 在编码阶段,强制判断字段存在,并据此使用lambda强类型 构造IQueryable
为了解决这个痛点, 引入动态Linq,动态Linq的不同之处在于 查询方法的参数不限于强类型的lamdba表达式,而是可以使用字符串;
使用字符串,意味着我们可在运行时动态决定过滤、排序内容
// 常规EF Linq: where条件过滤 + 倒排
_context.CarEnergyModels.Where(x=>x.CarVersion == carVersion).OrderByDescending(x=>x.UploadTime); // 动态EF Linq: where 条件过滤 + 倒排
_context.CarEnergyModels.Where("carVersion==\"ft_version_3.2\"").OrderBy("UploadTime desc");
同时由于我们在服务端可完全抓取QueryString(可一次性组装动态Linq字符串), 故动态灵活构建查询的方案呼之欲出。
编码实践
以上面伪代码业务举例, 根据条件灵活查询。
1. nuget引入DynamicLinq:
Install-Package Microsoft.EntityFrameworkCore.DynamicLinq -Version 1.0.
2. 定义EFCore 查询实体类:
public class CarModelContext : DbContext
{
public DbSet<CarEnergyModelEntity> CarEnergyModels { get; set; } public CarModelContext(DbContextOptions<CarModelContext> options) : base(options)
{
}
} [Table("car_energy_model")]
public class CarEnergyModelEntity
{
public CarEnergyModelEntity() { } [JsonIgnore]
[Key]
public Guid Id { get; set; } [Column("car_version")]
public string CarVersion { get; set; }
[Column("car_id")]
public string CarId { get; set; } [Column("user_id")]
public string UserId { get; set; } [Column("so_version")]
public string SoVersion { get; set; } [Column("config_version")]
public string ConfigVersion { get; set; } [Column("config_content")]
public string ConfigContent { get; set; } [Column("uploadtime")]
public DateTime UploadTime => DateTime.UtcNow;
}
3. Query集合抓取所有QueryString,列举字段的方式 判断字段为null, 并构造查询
[Route("all")]
[HttpGet]
public async Task<List<CarEnergyModelEntity>> GetModeParametersAsync(
[FromQuery] string carVersion,
[FromQuery] string carId,
[FromQuery] string userId,
[FromQuery] string soVersion,
[FromQuery] string configVersion,
[FromQuery] string configContent
)
{
// 这里使用列举字段的方式构造 strWhere
var query = HttpContext.Request.Query;
var validQueryArray1 = query.Where(x => (new string[] { "CarVersion", "carId", "userId", "soVersion", "configVersion", "configContent" }).Contains(x.Key, StringComparer.OrdinalIgnoreCase))
.Where(x => !string.IsNullOrEmpty(x.Value))
.Select(x => x.Key + "==\"" + x.Value + "\"").ToArray();
string strWhere = string.Join(" and ", validQueryArray1);
strWhere = string.IsNullOrEmpty(strWhere) ? " 1=1" : strWhere;
var sqlQuery = _context.CarEnergyModels.Where(strWhere);
return sqlQuery.ToList();
}

EFCore生成的SQL如下:
SELECT [c].[Id], [c].[car_id], [c].[car_version], [c].[config_content], [c].[config_version], [c].[so_version], [c].[user_id]
FROM [car_energy_model] AS [c]
WHERE (((([c].[car_version] = N'FT_Version_3.2') AND ([c].[car_id] = N'CD292FE0900X')) AND ([c].[user_id] = N'u_1960988792x')) AND ([c].[so_version] = N'so_ver1.2')) AND ([c].[config_version] = N'cv_1.2')
ok, That‘s all
以上查询还可扩展:前端组装排序字符串(orderStr:Uploadtime descending)通过QueryString传给API,API通过DyanmicLinq构造灵活的排序字段。
经过验证,以上过滤和排序都是在SqlServer层面完成的。
移除恶心的 if、else之后代码是不是看起来更优雅一些。
总结
以上场景相信很多开发者都会遇到,特别是进阶到一定水平,移除if/else 的欲望愈加强烈。
再次强化本文 知识点:
DynamicLinq 具备动态形成查询条件的能力,不再依靠lambda 强类型表达式,而是根据构造的过滤和排序字符串,内部解析成查询条件。
--------------------2019/9/23 下班前更新--------------------------------------
DynamicLinq 若动态组装String,确实存在 SQL注入问题, 使用placeholder 可避免。
更新代码:
// 构建动态查询
var query = HttpContext.Request.Query;
var validQueryArray1 = query.Where(x => (new string[] { "CarVersion", "carId", "userId", "soVersion", "configVersion", "configContent" }).Contains(x.Key, StringComparer.OrdinalIgnoreCase))
.Where(x => !string.IsNullOrEmpty(x.Value)); var predicate = validQueryArray1.Select((x,i) => $"{x.Key}==@{i}").ToArray();
var paramses = validQueryArray1.Select(x=>x.Value.ToString()).ToArray();
string strPredicate = string.Join(" and ", predicate);
strPredicate = string.IsNullOrEmpty(strPredicate) ? " 1=1" : strPredicate;
var sqlQuery = _context.CarEnergyModels.Where(strPredicate, paramses); return sqlQuery.ToList();
编写优雅代码,从挖掉恶心的if/else 开始的更多相关文章
- 最新的JavaScript核心语言标准——ES6,彻底改变你编写JS代码的方式!【转载+整理】
原文地址 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和for-of循环 生成器 Generators 模板字符串 不定参数和默认参数 解构 Destructu ...
- 最新的JavaScript核心语言标准——ES6,彻底改变你编写JS代码的方式!
原文地址 迁移到:http://www.bdata-cap.com/newsinfo/1741515.html 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和f ...
- 如果让莎士比亚、海明威编写JavaScript代码
本文作者Angus Croll是Twitter工程师.JavaScript迷.文学迷,并且非常喜欢作家海明威.他在梦中"梦见"一些名人编写JavaScript代码,不同的作家呈现出 ...
- .Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?
本文将通过场景例子演示,来通俗易懂的讲解在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码. 通过一系列优化最终达到两个效果,1.通过代码块来控制事务(分布式事务),2.通过委托优化Tran ...
- Guava 教程1-使用 Google Collections,Guava,static imports 编写漂亮代码
原文出处: oschina (API:http://ifeve.com/category/framework/guava-2/ JAR DOC Source 链接:http://pan.baidu.c ...
- 如何运用多阶构建编写优雅的Dockerfile
导读 Kubernetes要从容器化开始,而容器又需要从Dockerfile开始,本文将介绍如何写出一个优雅的Dockerfile文件. 文章主要内容包括: Docker容器 Dockerfile 使 ...
- 如何更规范化编写Java 代码
如何更规范化编写Java 代码 Many of the happiest people are those who own the least. But are we really so happy ...
- myeclipse 编写java代码提示 dead code 原因
经常使用MyEclipse或Eclipse编辑器编写java代码的程序员,可能经常遇到一个黄线警告提示:dead code:一般程序员遇到这些问题都会置之不理,反正也不影响程序的编译执行.对,这不是b ...
- 使用 Vert.X Future/Promise 编写异步代码
Future 和 Promise 是 Vert.X 4.0中的重要角色,贯穿了整个 Vert.X 框架.掌握 Future/Promise 的用法,是用好 Vert.X.编写高质量异步代码的基础.本文 ...
随机推荐
- Python机器学习之数据探索可视化库yellowbrick-tutorial
背景介绍 从学sklearn时,除了算法的坎要过,还得学习matplotlib可视化,对我的实践应用而言,可视化更重要一些,然而matplotlib的易用性和美观性确实不敢恭维.陆续使用过plotly ...
- Django Mysql数据库-基于双下划线的跨表查询
一.基于双下划线的跨表查询 Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系.要做跨关系查询,就使用两个下划线来链接模型(mode ...
- spark读取pg数据库报错操作符不存在
代码: Properties connectionProperties = new Properties(); connectionProperties.put("user", C ...
- ts 学习笔记 - 泛型
目录 泛型 举个栗子 泛型约束 多个参数时也可以在泛型约束中使用类型参数 泛型接口 泛型类 泛型参数的默认类型 泛型 泛型(Generics)是指在定义函数.接口或者类的时候, 不预先指定其类型,而是 ...
- sql中#与$取值
在mapper.xml中#与$都是用来取值的 <update id="addUrl"> update user_power set url = #{newurl} wh ...
- Python笔记_基础
1.注释 # 单行注释 """ 多行注释,一般用于类说明 """ 或 ''' 多行注释 ''' 2.工作日志 # TODO 说明性文字 记录 ...
- 非域环境下SQL Server搭建Mirror(镜像)的详细步骤
1.测试验证环境 服务器角色 机器名 IP SQL Server Ver 主体服务器 WIN-TestDB4O 172.83.XXX.XXX SQL Server 2012 - 11.0.5058.0 ...
- Codeforces 975D
题意略. 思路:我们来写一下公式: P1:(x1 + t * Vx1,y1 + t * Vy1) P2:(x2 + t * Vx2,y2 + t * Vy2) x1 + ...
- js的真值与假值
假值 结果为 false 的值称为 假值.例如,空字符串 "" 为假值,因为在布尔表达式中,"" 等于 false. false == 0返回:true fal ...
- 企业代码版本管理之争:TrunkBased vs GitFlow vs AoneFlow vs OneFlow vs ExeFlow
目录 引言 TrunkBased GitFlow AoneFlow OneFlow ExeFlow 综述 引言 网络上版本管理系统之争持久而喧嚣,依照声量来讲目前应该是Git占了较大的优势.不过我们本 ...