How to simulate regular expressions in LINQ-to-SQL

后端 未结 5 1588
生来不讨喜
生来不讨喜 2020-12-08 12:20

I have a database table with customer account numbers. Within the same table are test accounts that don\'t match the production formatting: say, \'A1111\' is production but

相关标签:
5条回答
  • 2020-12-08 12:40

    I changed the query to use the following in place of the Regex.IsMatch:

    where SqlMethods.Like(cust.Acct, ProductionMask)  
    

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

    the equivalent RegEx is: ^[B,G]\d{4}$

    If anyone sees that the 2 masks should not produce the same results, please let me know...

    0 讨论(0)
  • 2020-12-08 12:43

    special thanks to Roman Khramtsov and db_developer for reference information, and thanks to Microsoft :P

    RegExpLike Extension For Sql Server

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

    Step1: Compile SqlRegularExpressions.cs to generate 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);
        }
    
    }
    

    Step 2: Run DbInstall.sql SQL on the database

    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
    

    Step 3: On model diagram right click, select “Update Model from Database...”, use update wizard to add stored functions to model.
    Model diagram context menu Update wizard Model browser

    Step 4: Create imported functions in entity context class.

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

    Step 5: Finally you can use regular expressions on 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();
    
    0 讨论(0)
  • 2020-12-08 12:44

    I've had the same problem, but managed to get rid of it. I know it's slow but works, any optimization/bugfix hint will be welcomed :) The code gathers the data first then processes, so you need to filter as much as you can before calling toarray() or buy more ram :)
    hope it helps, enjoy

    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. This is a fast way for processing light data tables, you can improve it by just fetching needed columns for processing and just return ID columns for full access to rows. You can use my last post for a more general heavy duty scenarios. Choice is yours.

    0 讨论(0)
  • 2020-12-08 12:49

    Are you using LINQ-to-SQL? If so, MSDN forums state the following:

    LINQ to SQL cannot translate regular expressions to SQL because there's no support for Regex at the SQL end.

    It does give 3 alternatives though.

    0 讨论(0)
  • 2020-12-08 13:06

    Could you replace the Regex.IsMatch with

    where cust.AcctNum.StartsWith(ProductionMask)
    

    Or Contains / EndsWith depending on your needs

    0 讨论(0)
提交回复
热议问题