在项目中我们经常遇到将数据库的数据取到后再次进行筛选过滤的情况。LINQ to Entity提供了统一的查询接口并且可以高效的完成工作,但是对于我们常在sql中使用的%和_这样的通配符并没有支持。我们只能通过String.Contains方法来实现简单的通配。使用String.Contains方法是无法达到在查询串中使用通配符的目的的。正则表达式虽然晦涩难懂,但功能十分强大,解决个统配符绰绰有余。
代码如下:
publicstaticclassLINQHelper { ///<summary> ///TheallregexMetachars ///</summary> privatestaticstring[]REGEX_Meta_CHARS={"\\",".","^","$","*","+","?","{","}","(",")","[","]"}; ///<summary> ///Likemethodworkassqllike ///</summary> ///<paramname="searchString">Thesearchstring</param> ///<paramname="sqlPattern">Thesqlpattern</param> ///<returns>Whethermatchornot</returns> publicstaticboolLike(thisstringsearchString,stringsqlPattern) { if(searchString==null) { returnfalse; } else { stringconvertedPattern=EscapeRegexMetaChars(sqlPattern).Replace("_",".").Replace("%",".*"); convertedPattern=String.Format("^{0}$",convertedPattern); returnRegex.IsMatch(searchString,convertedPattern,RegexOptions.Singleline); } } ///<summary> ///Likemethodworkassqllike ///</summary> ///<paramname="searchString">Thesearchstring</param> ///<paramname="sqlPattern">Thesqlpattern</param> ///<paramname="escapeChar">Theescapechar</param> ///<returns>Whethermatchornot</returns> publicstaticboolLike(thisstringsearchString,stringsqlPattern,charescapeChar) { if(searchString==null) { returnfalse; } else { stringconvertedPattern=EscapeRegexMetaChars(sqlPattern); convertedPattern=ReplaceWildcards(convertedPattern,'_',escapeChar); convertedPattern=ReplaceWildcards(convertedPattern,'%',".*",escapeChar); convertedPattern=String.Format("^{0}$",RegexOptions.Singleline); } } ///<summary> ///Replacewildcards ///</summary> ///<paramname="replacement">Thereplacementstring</param> ///<paramname="wildcard">Thewildcard</param> ///<paramname="replaceTo">Thereplacewildcharto</param> ///<paramname="escapeChar">Theescapechar</param> ///<returns>Theconvertedsearchvalue</returns> privatestaticstringReplaceWildcards(stringreplacement,charwildcard,stringreplaceTo,charescapeChar) { stringregexExpression=String.Format("(^|[^{0}])({1}+)",escapeChar,wildcard); returnRegex.Replace(replacement,regexExpression,match=>String.Format("{0}{1}",match.Groups[1].Value,match.Groups[2].Value.Replace(wildcard.ToString(),replaceTo))) .Replace(string.Format("{0}{1}",wildcard),wildcard.ToString()); } ///<summary> ///EscaperegexMetachars ///</summary> ///<paramname="replacement">Thereplacementstring</param> ///<returns>Theconvertedsearchvalue</returns> privatestaticstringEscapeRegexMetaChars(stringreplacement) { stringresultString=replacement; foreach(stringMetaCharinREGEX_Meta_CHARS) { resultString=resultString.Replace(MetaChar,string.Format(@"\{0}",MetaChar)); } returnresultString; } }
首先,要将查询串中所有正则表达式的元字符转义为普通字符,这样才能安全的使用正则表达式进行匹配。
然后,将”_”和”%”替换成相应的正则表达式,即”_”替换成”.”,”%”替换成”.*”。这里还考虑到sql的LIKE语句也有转义符功能,即如果使用ESCAPE子句则LIKE串中转义符后的”_”和”%”变为普通字符而不是通配符。所以当使用转义符时处理如下:
以下是几个转换的例子:
LIKE ‘A_B’ 转换为 A.B
LIKE ‘A%B’ 转换为 A.*B
LIKE ‘A~_B’ ESCAPE ‘~’ 转换为 A_B
LIKE ‘A.B’ 转换为 A/.B
优点:我们可以在LINQ语句的条件中方便的使用Like方法去过滤数据,LINQ语句整体上会保持很好的可读性。
缺点:Like 方法会被调用n次(n取决于数据量),解析sql pattern到正则表达式pattern的代码就要被重复执行n次。因此当数据量过大时解析pattern会消耗一定的资源。当然这可以通过一些方法去解决,如缓存解析结果,或改为传入就是解析好的正则表达式等。