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

时间:2011-04-19 18:32:54

标签: c# regex linq-to-sql

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

        getCustomerDistribution = CompiledQuery.Compile<DataContext, String, DateTime, IEnumerable<ServerLoad>>(
            (context, region, processDate) => (from cust in context.GetTable<tbl_CustomerDistro>()
                                               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()
                                               )));

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

  

Method'Boolean IsMatch(System.String,   System.String)'不受支持   转换为SQL。

我正在查看用户定义的函数:

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

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

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

更新

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

where SqlMethods.Like (cust.AcctNum, ProductionMask)

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

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

有没有办法用SqlMethods.Like(...)?

来做到这一点

更新2:

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

6 个答案:

答案 0 :(得分:10)

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

where SqlMethods.Like(cust.Acct, ProductionMask)  

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

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

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

答案 1 :(得分:6)

您使用的是LINQ-to-SQL吗?如果是,MSDN forums说明以下内容:

  

LINQ to SQL无法定期翻译   SQL的表达式,因为没有   在SQL端支持Regex。

它确实提供了3种选择。

答案 2 :(得分:5)

特别感谢Roman Khramtsov和db_developer的参考信息, 并感谢微软: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

Step1:编译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

/// <summary>
/// Class that allows to support regular expressions in MS SQL Server 2005/2008
/// </summary>
public partial class SqlRegularExpressions
{
    /// <summary>
    /// Checks string on match to regular expression
    /// </summary>
    /// <param name="text">string to check</param>
    /// <param name="pattern">regular expression</param>
    /// <returns>true - text consists match one at least, false - no matches</returns>
    [SqlFunction]
    public static bool Like(string text, string pattern, int options)
    {
        return (Regex.IsMatch(text, pattern, (RegexOptions)options));
    }

    /// <summary>
    /// Gets matches from text using pattern
    /// </summary>
    /// <param name="text">text to parse</param>
    /// <param name="pattern">regular expression pattern</param>
    /// <returns>MatchCollection</returns>
    [SqlFunction(FillRowMethodName = "FillMatch")]
    public static IEnumerable GetMatches(string text, string pattern, int options)
    {
        return Regex.Matches(text, pattern, (RegexOptions)options);
    }

    /// <summary>
    /// Parses match-object and returns its parameters 
    /// </summary>
    /// <param name="obj">Match-object</param>
    /// <param name="index">TThe zero-based starting position in the original string where the captured
    ///     substring was found</param>
    /// <param name="length">The length of the captured substring.</param>
    /// <param name="value">The actual substring that was captured by the match.</param>
    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:在模型图上右键单击,选择“从数据库更新模型...”,使用更新向导将存储的功能添加到模型中。
Model diagram context menu Update wizard Model browser

第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();

答案 3 :(得分:1)

可以用

替换Regex.IsMatch吗?
where cust.AcctNum.StartsWith(ProductionMask)

或包含/结束,具体取决于您的需求

答案 4 :(得分:0)

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

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);
 }

P.S。这是处理光数据表的快捷方式,您可以 只需获取所需的列进行处理即可改进它 返回ID列以完全访问行。你可以用我的上一篇文章 对于更一般的重型场景。选择权归你所有。

答案 5 :(得分:0)

在 EF 6.2 到 6.4.4 中使用 DbFunctions.Like