编写优雅代码,从挖掉恶心的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.编写高质量异步代码的基础.本文 ...
随机推荐
- 谨慎 mongodb 关于数字操作可能导致类型及精度变化
1.问题描述 最近有一个需求,更新Mongo数据库中 原料 集合的某字段价格,更新后,程序报错了,说长度过长了,需要Truncation. 主要错误信息如下: FormatException: An ...
- Apache性能测试工具ab使用详解~转载
Apache自带性能测试工具ab使用详解 一. Apache的下载 1. http://www.apache.org/,进入Apache的官网 2. 将页面拖到最下方“Apache Project L ...
- RDIFramework.NET敏捷开发框架通过SignalR技术整合即时通讯(IM)
1.引言 即时通讯(IM)是RDIFramework.NET敏捷开发框架全新提供的一个基于Web的即时通讯.内部聊天沟通的工具.界面美观大方对于框架内部进行消息的沟通非常方便.基于RDIFramewo ...
- ansible之数据提取与Juniper实例演示
一.Ansible列表两种表达方式 基于YAML的列表 my_list: - a - b - c - d 基于Json格式的列表 {"my_list":[ "a" ...
- sql server 怎么查看blocked的线程
select spid ,blocked from master..sysprocesses
- 随笔编号-11 阿里云CentOS7系列二 -- 安装Tomcat7的方法
前面讲到了JDK在CentOS7 环境下的安装步骤.这次来分享安装Tomcat7的安装步骤: Tomcat7 安装包: 链接: http://pan.baidu.com/s/1geKwASN 密码: ...
- C++ 线程安全的单例模式总结
什么是线程安全? 在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况. 如何保证线程安全? 给共享的资源加把锁,保证每 ...
- 设计模式(C#)——12责任链模式
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 前言 在开发游戏过程中,当玩家合成一种道具的时候,对于不痛的道具,需要的碎片个数,类型是不同的.用传统的写法,就是 ...
- Mac迅雷瘦身精简教程
迅雷是个大家很熟悉的工具了,尽管吐槽的人不少,但相信大家也都是口嫌体直,边骂边用. 其实 macOS 版迅雷在界面上,相比于 Windows 的客户端来说,已经很克制了,但有些功能仍然对用户造成了干扰 ...
- 关于web.xml配置
整理自网上: web应用是一种可以通过Web访问的应用程序.在J2EE领域下,web应用就是遵守基于JAVA技术的一系列标准的应用程序. 最简单的web应用什么样? 2个文件夹.1个xml文件就能成为 ...