全角半角符号引发的Entity Framework奇遇记
SQL Server的SQL查询不区分大小写,而LINQ查询区分大小写,所以在写LINQ代码时需要注意的是——如果这段LINQ代码将会被Entity Framework解析为SQL语句(LINQ to Entities),则不用考虑大小写问题;如果这段LINQ代码在内存中执行,就要考虑大小写的问题。
比如下面的LINQ to Entities(不用考虑大小写):
//代码自来CNBlogsTagService _unitOfWork.Set<Tag>().Where(x => tagNames.Contains(x.TagName))
而如果是LINQ,则需要这么写(通过StringComparer.OrdinalIgnoreCase忽略大小写):
content.Tags.RemoveAll(x => tagNames.Contains(x.TagName, StringComparer.OrdinalIgnoreCase) == false);
这种不一致带来的问题是——同样是写LINQ,你却要区别对待,你要考虑这段LINQ代码是在内存中执行,还是会被解析为SQL执行。
这个大小写问题是大家熟知的,解决起来也不困难。
而我们最近在实际项目中遇到了一个神奇的问题,与大小写问题是同一类问题——在SQL Server中进行SQL查询时竟然不区分全角半角,而在LINQ中是区分的。
下面我们通过CNBlogsTagService项目(一个基于Entity Framework实现的为前端应用提供Tag服务的后端服务)中的一个实际场景感受一下。
先看一段LINQ to Entities代码:
public List<Tag> GetTags(IEnumerable<string> tagNames) {var existedTags = _unitOfWork.Set<Tag>().Where(x => tagNames.Contains(x.TagName)).ToList();//... }
上面的代码是根据TagName从数据库中查询记录,然后得到对应的Tag实体。
我们遭遇问题时,tagNames的值是{ "C++" },注意这里的加号是全角,数据库中存储的TagName的值是"c++"(这里的加号是半角)。上面的代码执行后得到的结果是——existedTags[0].TagName的值为"c++"。SQL查询竟然能自动匹配全角半角,当时发现这个也是第1次知道这回事,不由感叹——好智能的SQL Server。
但是这种智能带来的不一致却让我们经历了一次艰难的问题排查过程。
再看后续的LINQ代码:
var createdTags = tagNames.Where(x => existedTags.Select(y => y.TagName).Contains(x, StringComparer.InvariantCultureIgnoreCase) == false).Select(x => new Tag { TagName = x }).ToList();
这段代码是在内存中进行LINQ查询操作的代码,用途是找出tagNames(类型是IEnumerable<string>)中存在,而且existedTags(EF的实体)不存在的TagName(也就是找出在数据库中不存在的TagName)。
根据之前的场景,tagNames的值是{ "C++" },existedTags[0].TagName的值是"c++"。既然数据库中已存在这个Tag,我们所期望的是createdTags中没有数据,但是由于LINQ区分全角半角,得到的结果却是——createdTags[0].TagName的值为"C++",在通过Entity Framework进行SaveChanges时引发了异常:
System.Data.SqlClient.SqlException: Cannot insert duplicate key row in object 'dbo.Tags' with unique index 'IX_Tags_TagName'. The duplicate key value is (C++).
本来这里的代码的目的是如果指定名称的Tag在数据库中不存在,就创建它,并保存至数据库。对应现在的场景,变成了——"C++"这个Tag在数据库中存在吗?数据库说:存在,名叫"c++";{ "C++" } 中有哪些是 { "c++" }所没有的?LINQ说:"C++";于是,EF将"C++"保存数据库,数据库却说:我这已经有了c++,"C++"请滚开。于是就有了上面的异常。
问题就出在SQL与LINQ的不一致行为上。如果事先不知道不一致的情况,出现bug时,往往最难对付!在博客中写出来看上去问题似乎很简单,但我们纠缠于这个问题时,猜测了成千上万的原因,也没想到是这个原因。最后发现时不由感叹——真是一次奇遇!
那如何解决这个问题呢?
我们想到的最简单的方法是在LINQ查询时忽略全角半角。
那如何以最简单的方法实现在LINQ查询时忽略全角半角呢?
园子里2005年空军写的一篇博文(C#中直接调用VB.NET的函数,兼论半角与全角、简繁体中文互相转化)让我们很快有了答案——在C#中调用VB.NET中的函数Strings.StrConv(x, VbStrConv.Narrow);
具体实现方法如下:
1. 在Visual Studio中为项目添加Microsoft.VisualBasic的引用
2. 将上面的LINQ代码改为如下的代码:
var createdTags = tagNames.Where(x => existedTags.Select(y => Strings.StrConv(y.TagName, VbStrConv.Narrow)).Contains(Strings.StrConv(x, VbStrConv.Narrow), StringComparer.InvariantCultureIgnoreCase) == false).Select(x => new Tag { TagName = x }).ToList();
写好这篇博客后,突然觉得也算不上什么奇遇记,可能很多朋友早就知道了这个情况。标题党只是为了表达一下解决问题后的那种兴奋的感觉。
解决问题是一种快乐,那有没有比解决问题更快乐的事情呢?有,那就是在解决问题后写一篇博客!
转载于:https://www.cnblogs.com/dudu/p/ef-linq-full-width-char.html
全角半角符号引发的Entity Framework奇遇记相关推荐
- java 判断全角_Java如何判断字符串中包含有全角,半角符号
首先介绍下全角跟半角之间的区别: 在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为"半角",相对地把一个汉字所占的位置称为"全角" ...
- Java如何判断字符串中包含有全角,半角符号
一.全角跟半角之间的区别 在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为"半角",相对地把一个汉字所占的位置称为"全角".在汉 ...
- Java如何判断字符串中包含有全角,半角符号,以及去除中文
全角:是指中GB2312-80(<信息交换用汉字编码字符集·基本集>)中的各种符号. 半角:是指英文件ASCII码中的各种符号. 全角状态下字母.数字符号等都会占两个字节的位置,也就是一个 ...
- java 如何判断字符串中包含有全角,半角符号
参考链接:http://www.cnblogs.com/Ant-soldier/p/6373766.html 1.通过字符编码的范围进行判断. ascii编码对照http://tool.oschina ...
- win7全角/半角符号切换快捷键
具体表现就是有时候不知道按到了什么,输入数字的时候就有很大空隙了,这种情况一般是输入法的锅,切换成了全角模式. 解决办法:shift键 + space键 即可切换回半角模式. 另外一种情况是写的字变成 ...
- elasticsesarch keyword忽略大小写,全角半角符号转化
1.要点讲解: normalizer只对具体单个字符起作用.故可用的过滤器只能针对单个字符. PUT index {"settings": {"analysis" ...
- java处理全角半角字符问题
1.全角:指一个字符占用两个标准字符位置. 汉字字符和规定了全角的英文字符及国标GB2312-80中的图形符号和特殊字符都是全角字符.一般的系统命令是不用全角字符的,只是在作文字处理时才会使用全角字符 ...
- Windows自带输入法中文符号或全角半角切换
问题: 最近在PC端微信中输入中文标点字符时,总是发现逗号字符间距很小,句号就是一个点,按ctrl+shift, shift+空格等快捷键,均不能切换到中文的标点符号. 解决: (1)中文标点符号切换 ...
- 我的Android进阶之旅------gt;Java全角半角的转换方法
一中文全角和半角输入的区别 1全角指一个字符占用两个标准字符位置 2半角指一字符占用一个标准的字符位置 3全角与半角各在什么情况下使用 4全角和半角的区别 5关于全角和半角 6全角与半角比较 二转半角 ...
最新文章
- EMD算法之Hilbert-Huang Transform原理详解和案例分析
- python2.7安装tensorflowgpu_Ubuntu16.04+Python2.7+CUDA9.0+cuDNN7.0+TensorFlow 1.6 安装随笔
- cpu风扇一会转一会停_空调维修|空调开机一会就停显示e1|空调维修方法
- 从菜鸟成为数据科学家的养成方案
- 循环自相关函数和谱相关密度(四)——实信号、复信号模型下的QPSK信号循环谱推导
- 多中心容灾实践:如何实现真正的异地多活?
- 分布式Tensorflow入门Demo
- Azure VMSS (1) 入门
- Linux系统管理技术(3)
- 送出15个Google Wave邀请,需要的赶快
- 红米note 4x Android 8,红米note 4X升级安卓7.0:小米MIUI8第331周公测
- 太原理工大学计算机课程设计报告,太原理工大学matlab课程设计报告
- bin文件的安装方法
- 武汉大学计算机学院期末考试时间,【通知公告】关于2018-2019学年第二学期期末考试工作安排的通知...
- android 虚拟按键自定义,Android手机底部栏虚拟按键的操作
- 复现CLOCs中spconv v1.0 (commit 8da6f96)踩坑记录
- 【Matlab学习手记】三维矢量场图
- python里row是什么意思_row python
- FFmpeg分离(解封装)视频和音频
- Prometheus架构与实践分享