Dynamic Linq 的Like扩展

     在上几节Linq动态组合查询时,在肖坤的Linq动态查询与模糊查询(带源码示例)时看到了微软的《Linq to SQL Dynamic 动态查询》,但是楼主说“可惜Dynamic.cs也是不能使用like的,恨啊!”。于是我下载了Dynamic .cs仔细研究了下源码,一步一步的调试,本想在微软的类库里添加如like的支持,但是调试了半天,还是无从下手。但是发现了DLinq支持对方法的调用,支持 DataContext.Orders.Where("ShipCity.Contains(@0) ","test");于是想了下我能否用正则表达式匹配Like表达式,转化为string.Contains()。就尝试Code。说句其实微软已经支持了,只是我们有一些还是习惯于Like ‘str’形式吧了。

    赶快进入主题-Code:

 

/// <summary>
    /// 操作like帮助类,自己增加的;
    /// </summary>
    public class DynamicQueryableLikeHelper
    {
        public static string CheckLikeSattement(string predicate, ref  object[] values)
        {
            MatchCollection matches = Regex.Matches(predicate.ToLower(),
@"(?<item>(\s*like\s*((?<param1>(@[\d+]))|(?<param2>(((\')?\w+\'?))))))\s*((and)|(or)?)");
            string s = predicate;
            if (matches != null)
            {
                int offset = 0;
                foreach (Match item in matches)
                {
                    Group gitem = item.Groups["item"];
                    if (!string.IsNullOrEmpty(item.Groups["param1"].Value))
                    {
                        Group g = item.Groups["param1"];
                        string insertstr = ".Contains(" + g.Value + ")";
                        s = DynamicQueryableLikeHelper.ChangePredicate(s, insertstr, gitem, ref offset);

                    }
                    else if (!string.IsNullOrEmpty(item.Groups["param2"].Value))
                    {
                        Group g = item.Groups["param2"];
                        string insertstr = ".Contains(@" + values.Length + ")";
                        s = DynamicQueryableLikeHelper.ChangePredicate(s, insertstr, gitem, ref offset);
                        ArrayList list = new ArrayList(values);
                        list.Add(g.Value.Replace("\'", ""));
                        values = list.ToArray();//like对字符串操作所以没有考虑其他类型

                    }

                }
            }
            return s;
        }

        public static string ChangePredicate(string s, string insertstr, Group gitem, ref int offset)
        {
            string strremove = s.Remove(gitem.Index + offset, gitem.Length);
            s = strremove.Insert(gitem.Index + offset, insertstr);
            offset += insertstr.Length - gitem.Length;
            return s;
        }
    }

 

 

         在上面代码就是正则表达式的运用,很简单。其中唯一花费了我很多时间就是我在正则表达式匹配时和修改后的string的index不一致,本想用个假单方法实现,最后还是么有办法,就只有加入了一个offset偏移量,每次删除插入的差值的和。这里就把like ‘value’ 转化为 property.Contains(value);其实和like还是有差别的Contains其实只等于 ‘%value%’。但是这里提供了一个方案,同理我们可以实现为‘%value’ 为EndWith,‘vakue%’为StartWith。由于在我们的应用中一般常用的为Contains所以暂时还么有扩展其他两个,等有时间再做(实现也简单就是判断有么有%和他的位置选择我们所转化的函数名)。

     为了在Dlinq里扩展我在where方法里面加入了传入条件字符串的转化。并加入了参数ignoreLike,默认为False。我怕的扩展有某些问题暂时还么有测试出来了,还有就是不想改变原类库。

    在我的扩展中支持ShipCity like @1 、ShipCity like 'don'、ShipCity like @0 两种形式,原来类库里面只支持第三种形式。我的前两者形式都是当做字符串处理的,因为我们的like操作一般都是对字符串操作,如果要支持其他类型可以转化类型,我以前写了一个类型属性转化的类,可以实现,但是我想这个问题么有必要。

在类库里面该的方法有(已经注释了,还是贴出来看看调用):

 

public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
        {
            return (IQueryable<T>)Where((IQueryable)source, predicate, false, values);
            //TODO:添加False
        }

        public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate,
                bool ignoreLike, params object[] values)
        //TOD:增加的方法,ignoreLike
        {
            return (IQueryable<T>)Where((IQueryable)source, predicate, ignoreLike, values);
        }

        public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
        //TOD:增加的方法,ignoreLike,下面方法的忽略ignoreLike写法;
        {
            return Where(source, predicate, false, values);
        }
        public static IQueryable Where(this IQueryable source, string predicate,
                         bool ignoreLike, params object[] values)
        //TODO:改变参数ignoreLike
        {
            if (source == null) throw new ArgumentNullException("source");
            if (predicate == null) throw new ArgumentNullException("predicate");
            if (!ignoreLike)
            {
                predicate = DynamicQueryableLikeHelper.CheckLikeSattement(predicate, ref values);
                //TODO:增加语句;
            }
            LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType,
                        typeof(bool), predicate, values);
            return source.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable), "Where",
                    new Type[] { source.ElementType },
                    source.Expression, Expression.Quote(lambda)));
        }

 

 

   只是TODO处就是我改变的。下面该测试测试下(数据库Northwind的orders表)。

 


代码

IQueryable<Orders> q = DataContext.Orders
.Where("ShipCity like @1 and ShipCity like @0 or ShipCity like 'don' and ShipCity like 'don'
          and ShipCity like 'don'  or EmployeeID1 =1", "test ", "test").OrderBy("ShipCity");
            //IQueryable<Orders> q = DataContext.Orders.Where("ShipCity.Contains(@0) ","test");
            Console.WriteLine(DataContext.GetCommand(q).CommandText);
     

 

 

为了测试多个我邮箱投个懒就把ShipCity like 'don' 赋值了多次。 
输出sql为: 


代码

SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID] AS [EmployeeID1], [t 0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[F reight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion] , [t0].[ShipPostalCode], [t0].[ShipCountry] FROM [dbo].[Orders] AS [t0] WHERE (([t0].[ShipCity] LIKE @p0) AND ([t0].[ShipCity] LIKE @p1)) OR (([t0].[Shi pCity] LIKE @p2) AND ([t0].[ShipCity] LIKE @p3) AND ([t0].[ShipCity] LIKE @p4)) OR ([t0].[EmployeeID] = @p5) ORDER BY [t0].[ShipCity]

 

 

如果在类库中有和你的字符串等冲突就可以用ignoreLike为真,避免我的扩展。

最后希望大家一起帮我测试下,如果有什么问题请留言和Email:破狼Email指出,我好改变。

附带:扩展后的Dynamic.cs下载

作者:破  狼 
出处:http://www.cnblogs.com/whitewolf/ 
本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。该文章也同时发布在我的独立博客中-个人独立博客博客园--破狼51CTO--破狼。http://www.cnblogs.com/whitewolf/archive/2010/08/03/1790954.html

时间: 2024-08-03 00:30:39

Dynamic Linq 的Like扩展的相关文章

如何正确看待Linq的DistinctBy扩展和ForEach扩展

在微软标准的Linq中,并没有DistinctBy扩展和ForEach扩展,但在平时使用工作中却又经常需要使用到这 两个功能,照理来说,微软在Linq中应该包含这两个扩展才对,可事实上为什么并没有呢?本文我就来说说自 己对这两个扩展的理解! 关于DistinctBy扩展 顾名思义,DistinctBy扩展就是根据一个键值进 行唯一性的筛选,将有重复键值的元素剔除,仅保留一个!当然Linq中有Distinct扩展,但其功能简直是弱爆 了!用过的同志相信都对Distinct扩展吐槽无数遍了吧!如果你

【LINQ技术】扩展特性和LINQ操作符

LINQ特有的编程结构 LINQ就像是嵌入到C#中的强类型查询语言,尽管和SQL查询很像,但语法却并不相同,甚至还有截然相反的一面. LINQ是在.NET发展到3.5版的时候被引进的,C#和VB语言都为此做了许多工作,扩展了大量新的编程结构. 一.隐式类型本地变量 var--一个如此小巧的关键字却有着强大的力量. var varInt=1; var varBool=True; var varString="String, String, String"; Console.WriteLi

扩展LINQ to SQL

ORM框架在删除数据方面一直有个尴尬,那就是无法通过指定条件批量删除数 据(当然这本不是ORM的问题,只是使用上感觉不方便).于是对于一些删除操 作,我们不得不写SQL语句或者执行存储过程,例如: ItemDataContext db = new ItemDataContext(); db.ExecuteCommand( "DELETE FROM Item WHERE [CreateTime] < {0}", DateTime.UtcNow.AddMonths(-1)); 我始终

NHibernate3.0剖析:Query篇之NHibernate.Linq自定义扩展

系列引入 NHibernate3.0剖析系列分别从Configuration篇.Mapping篇.Query篇.Session策略篇.应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本.如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧. NHibernate专题:http://kb.cnblogs.com

一起谈.NET技术,NHibernate3.0剖析:Query篇之NHibernate.Linq自定义扩展

系列引入 NHibernate3.0剖析系列分别从Configuration篇.Mapping篇.Query篇.Session策略篇.应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本.如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧. NHibernate专题:http://kb.cnblogs.com

动态Linq的逻辑与和逻辑或的条件查询

最近在做一个数据检索的工作,对一个数据库中的宽表进行多个条件的检索.为了简单方便快捷的完成这个功能,我使用LINQ to SQL+ReportView的方式来完成. 首先需要做的是一个查询界面和写一个数据库查询方法.用户在输入框中输入多个指标,将根据指标的格式生成LINQ的Where语句.这个很容易实现,比如输入"2003 北京 人口",那么就根据空格将这个字符串分成3个字符串,第一个字符串格式是年份,所以用表中的Year字段进行匹配,第二个字段是地区,所以再用表中的Location进

《圣殿祭司的ASP.NET4.0专家技术手册》----2-12 扩展方法

2-12 扩展方法 圣殿祭司的ASP.NET4.0专家技术手册 扩展方法(Extension Methods)允许针对现有类型加入自定义方法,而不必用传统方式,先继承然后再实现方法,最后还要再进行编译,完全省略了这些不必要的步骤. 然而,什么时候需使用到扩展方法?通常有两个时机: (1)需扩展类型额外的方法,就可通过扩展方法加入额外的方法: (2)希望直接使用系统已建立好的扩展方法,这种情况在LINQ中尤其明显. 范例2-12 使用扩展方法扩展string类型方法 假设要将阿拉伯数字"2266&

如何快速上手LINQ to XML

在我们的程序中,我们经常需要将一些系统的数据.信息保存在文件中,而不是保存在数据库中,在.NET中,我通常都是选择将这些系统的数据.信息保存在XML中. 操作XML的技术有很多种: 1)DOM(Document Object Model,文档对象模型),它为XML文档提供了一个标准的解析. 2)XPath和XSLT,它们提供了查询和格式化XML的功能. 3).NET框架中提供了一些对XML操作的类(在System.XML命名空间下). 4)LINQ to XML. 在我看来有了LINQ to X

扩展 Entity Framework支持复杂的过滤条件(多个关键字模糊匹配)_实用技巧

之前遇到一个棘手的Linq to EF查询的技术问题,现有产品表Product,需要根据多个关键字模糊匹配产品名称, 现将解决方案分享出来. 问题描述 根据需求,我们需要编写如下的SQL语句来查询产品 复制代码 代码如下: select * from dbo.Product where (ProductName like 'Product1%' or ProductName like 'Product2%') 如何将以上的SQL语句转换成EF的写法呢? 方案一 可以使用Union,将以上SQL语