如何在LINQ-to-SQL中模拟正则表达式

我有一个包含客户帐号的数据库表。 在同一个表中是与生产格式不匹配的测试帐户:例如,’A1111’是生产,但’JTest’不是。 我有正则表达式只会拉我的生产帐户。 我需要一个特定的编译查询来只提取生产帐户。 该查询按地区和日期为我提供了客户数量; 和每个地区的概念计数:

getCustomerDistribution = CompiledQuery.Compile<DataContext, String, DateTime, IEnumerable>( (context, region, processDate) => (from cust in context.GetTable() where cust.ProcessedDate.Date == processDate.Date where cust.Region == region where Regex.IsMatch(cust.AcctNum, ProductionMask) group cust by new { cust.Region, cust.Concept } into custDistro orderby custDistro.Key.Region select new CustomerDistro ( custDistro.Key.Region, custDistro.Key.Concept, custDistro .Where(c => c.Concept == custDistro.Key.Concept) .Select(c => c.Concept).Count() ))); 

问题是我在运行时收到以下消息:

方法’Boolean IsMatch(System.String,System.String)’没有支持的SQL转换。

我在看用户定义的函数:

 static Func IsProduction = (AcctNum) => Regex.IsMatch(AcctNum, ProductionMask); 

这也不起作用。 我不想迭代检索到的记录以进一步过滤,除非没有其他方法可以做到这一点。

有没有办法用Predicate Builder做到这一点?

更新:

我认为另一种选择是使用:

where SqlMethods.Like (cust.AcctNum, ProductionMask)

但是,我的ProductionMask是为Regex编写的:

 ^[B,G]\d{4}$ 

有没有办法用SqlMethods.Like(…)做到这一点?

更新2:

这是一个非常慢的查询。 我有3个区域,这个查询运行,记录计数和返回时间是:
263:903ms
342:822ms
146:711ms

我更改了查询以使用以下代替Regex.IsMatch

 where SqlMethods.Like(cust.Acct, ProductionMask) 

ProductionMask = "[bBgG][0-9][0-9][0-9][0-9]"

等价的RegEx是: ^[B,G]\d{4}$

如果有人发现2个面具不应该产生相同的结果,请告诉我…

你在使用LINQ-to-SQL吗? 如果是这样, MSDN论坛声明如下:

LINQ to SQL无法将正则表达式转换为SQL,因为SQL端不支持Regex。

它确实提供了3种选择。

特别感谢Roman Khramtsov和db_developer的参考信息,感谢Microsoft:P

适用于Sql Server的RegExpLike扩展

参考链接:
http://www.codeproject.com/Articles/42764/Regular-Expressions-in-MS-SQL-Server-2005-2008
http://msdn.microsoft.com/en-us/library/dd456847.aspx

步骤1:编译SqlRegularExpressions.cs以生成SqlRegularExpressions.dll

 // SqlRegularExpressions.cs // © Copyright 2009, Roman Khramtsov / Major League - SqlRegularExpressions using System; using System.Data.SqlTypes; //SqlChars using System.Collections; //IEnumerable using System.Text.RegularExpressions; //Match, Regex using Microsoft.SqlServer.Server; //SqlFunctionAttribute ///  /// Class that allows to support regular expressions in MS SQL Server 2005/2008 ///  public partial class SqlRegularExpressions { ///  /// Checks string on match to regular expression ///  /// string to check /// regular expression /// true - text consists match one at least, false - no matches [SqlFunction] public static bool Like(string text, string pattern, int options) { return (Regex.IsMatch(text, pattern, (RegexOptions)options)); } ///  /// Gets matches from text using pattern ///  /// text to parse /// regular expression pattern /// MatchCollection [SqlFunction(FillRowMethodName = "FillMatch")] public static IEnumerable GetMatches(string text, string pattern, int options) { return Regex.Matches(text, pattern, (RegexOptions)options); } ///  /// Parses match-object and returns its parameters ///  /// Match-object /// TThe zero-based starting position in the original string where the captured /// substring was found /// The length of the captured substring. /// The actual substring that was captured by the match. public static void FillMatch(object obj, out int index, out int length, out SqlChars value) { Match match = (Match)obj; index = match.Index; length = match.Length; value = new SqlChars(match.Value); } } 

步骤2:在数据库上运行DbInstall.sql SQL

DbInstall.sql

 sp_configure 'clr enabled', 1 reconfigure go --needs full path to DLL create assembly SqlRegularExpressions from '..\SqlRegularExpressions.dll' with PERMISSION_SET = SAFE go create function RegExpLike(@Text nvarchar(max), @Pattern nvarchar(255), @Options int = 0) returns bit as external name SqlRegularExpressions.SqlRegularExpressions.[Like] go create function RegExpMatches(@text nvarchar(max), @pattern nvarchar(255), @Options int = 0) returns table ([Index] int, [Length] int, [Value] nvarchar(255)) as external name SqlRegularExpressions.SqlRegularExpressions.GetMatches go 

DbUninstall.sql

 drop function RegExpLike drop function RegExpMatches drop assembly SqlRegularExpressions go sp_configure 'clr enabled', 0 reconfigure go 

步骤3:在模型图上右键单击,选择“从数据库更新模型…”,使用更新向导将存储的函数添加到模型中。
模型图上下文菜单更新向导模型浏览器

第4步:在实体上下文类中创建导入的函数。

 public class TheCompanyContext : Entities { // Please check your entity store name [EdmFunction("TheCompanyDbModel.Store", "RegExpLike")] public bool RegExpLike(string text, string pattern, int options) { throw new NotSupportedException("Direct calls are not supported."); } } 

第5步:最后,您可以在LINQ to Entities上使用正则表达式:)

 User[] qry = (from u in context.Users where u.ApplicationName == pApplicationName && context.RegExpLike(u.Username, usernameToMatch, (int)RegexOptions.IgnoreCase) orderby u.Username select u) .Skip(startIndex) .Take(pageSize) .ToArray(); 

你能用Regex.IsMatch替换吗?

 where cust.AcctNum.StartsWith(ProductionMask) 

或包含/结束根据您的需要

我遇到了同样的问题,但设法摆脱它。 我知道它很慢但有效,任何优化/错误修正提示都会受到欢迎:)代码首先收集数据然后进行处理,所以你需要在调用toarray()或购买更多ram之前尽可能多地过滤toarray() 🙂
希望它有所帮助,享受

 Regex rx = LikeToRegEx(emailToMatch); User[] qry = (from u in context.Users where u.ApplicationName == pApplicationName orderby u.Username select u) .ToArray() .Where(u => rx.IsMatch(u.Email)) .ToArray(); // -- LikeToRegEx : Converts SQL like match pattern to a regular expression -- public Regex LikeToRegEx(string likestr, RegexOptions opt = RegexOptions.None) { likestr = likestr .Replace("*", ".") .Replace("+", ".") .Replace("(", ".") .Replace("[", ".") .Replace("/", ".") .Replace("\\", ".") .Replace("^", ".") .Replace("$", ".") .Replace("_", ".") .Replace("%", ".*"); return new Regex(likestr, opt); } 

PS这是一种处理轻量数据表的快速方法,您只需获取所需的列进行处理即可改进它,只需返回ID列即可完全访问行。 您可以将我的上一篇文章用于更一般的重型场景。 选择是你的。