Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别


在上面一篇文章Linq To Sql进阶系列(六)中,我们提到了使用object的动态查询。本文在上文的基础上,再做更加深入的引申。同时修正上文中一些不妥的地方。

1, object的动态查询续
首先要做的事情,就是将Find的函数改成扩展方法。扩展方法只能放在静态类里,而且它的第一个参数必须带this关键字。在上文中,作者留下了一个迷题。当需要or条件时,又该如何做呢?本文也将这个问题给出回答。但是对于动态Like的条件,笔者依然还没有找到一个较好的方法。为了增加or条件,函数的声明也再一次被改动。如下:
    public static IQueryable<TEntity> Find<TEntity>(this IQueryable<TEntity> source, TEntity obj, bool isAnd) where TEntity : class
在上文中,我们还碰到了System.Nullable<int>此类类型不支持的问题。其实这个地方主要原因在于我们构造right端的Expression Tree时,没有给它参数。那么这个问题通过Expression right = Expression.Constant(p.GetValue(obj, null), p.PropertyType); 可以得到修复。那整个函数修改后,如下:

    public static IQueryable<TEntity> Find<TEntity>(this IQueryable<TEntity> source, TEntity obj,
bool
 isAnd) where TEntity : class
    
{
        
if (source == null)
            
throw new ArgumentNullException("Source can't be null!!");
        
//获得所有property的信息
        PropertyInfo[] properties = obj.GetType().GetProperties(BindingFlags.Public |
BindingFlags.Instance);

        Expression condition 
= null;
        
//先构造了一个ParameterExpression对象,这里的c,就是Lambda表达中的参数。(c=>)  
        
//本变量被移出了foreach循环
        ParameterExpression param = Expression.Parameter(typeof(TEntity), "c");
        
//遍历每个property
        foreach (PropertyInfo p in properties)
        
{
            
if (p != null)
            
{
                Type t 
= p.PropertyType;
                
//只支持value型和string型的影射
                if (t.IsValueType || t == typeof(string))
                
{
                    
//如果不为null才算做条件
                    if (p.GetValue(obj, null!= null)
                    
{
                        
//SQL Server does not support comparison of TEXT, NTEXT, XML and IMAGE ,etc
                        ///Only support BigInt,Bit,Char,Decimal,Money,NChar,Real,
                        
///Int,VarChar,SmallMoney,SmallInt,NVarChar,NVarChar(MAX),VarChar(MAX)

                        Attribute attr = Attribute.GetCustomAttribute(p, typeof(ColumnAttribute));
                        
if (attr != null)
                        
{
                            
string dbType = (attr as ColumnAttribute).DbType;
                            
if (dbType.Contains("Text"|| dbType.Contains("NText")
                                
|| dbType.Contains("Xml"|| dbType.Contains("Image")
                                
|| dbType.Contains("Binary"|| dbType.Contains("DateTime")
                                
|| dbType.Contains("sql_variant"|| dbType.Contains("rowversion")
                                
|| dbType.Contains("UniqueIdentifier"|| dbType.Contains
(
"VarBinary(MAX)"))
                            
{
                                
continue;
                            }

                        }
 
                        
//构造表达式的右边,值的一边
                        Expression right = Expression.Constant(p.GetValue(obj, null), p.PropertyType);
                        
//构造表达式的左边,property一端。
                        Expression left = Expression.Property(param, p.Name);
                        
//生成筛选表达式。即c.CustomerID == "Tom"
                        Expression filter = Expression.Equal(left, right);
                        
if (condition == null)
                        
{
                            condition 
= filter;
                        }

                        
else
                        
{
                            
if (isAnd)
                                condition 
= Expression.And(condition, filter);
                            
else
                                condition 
= Expression.Or(condition, filter);
                        }

                    }

                }

            }

        }

        
if (condition != null)
        
{
            Expression
<Func<TEntity, bool>> pred = Expression.Lambda<Func<TEntity, bool>>(condition, param);
            
return source.Where(pred);
        }
  
        
return source;
        
    }

    

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别


在这里,首先检查输入的参数是否为null。扩展方法其实是按静态方法执行的。它和静态方法唯一不同的就是系统自动为其加了一个Attribute,而这个Attribute只能通过在第一个参数加this关键字才能获得。而后,在影射类型上,修改后的函数只支持数值型和string型。其原因就是像imager等并不支持条件查询。为了简化,我们只支持数值型和string型。这里最大的变化莫过于支持or条件了。调用Expression.And或Expression.Or就可以了。还有一个变化就是ParameterExpression对象和Expression<Func<TEntity, bool>>被移出了foreach循环。这样,提高了效率,只是在最后才去生成条件。
而实际上,大家大多使用是and条件,那再重载一个方法。

    public static IQueryable<TEntity> Find<TEntity>(this IQueryable<TEntity> source, TEntity obj) where TEntity : class
    
{
        
return Find<TEntity>(source,obj,true);
    }

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别



2,限定字段在某集合中
这有点像in操作。比如where city in ('London', 'BeiJing') 也可以写成 where city = 'London' or city = 'BeiJing'。既然谈到or条件的动态构造了,那就也来构造下这个吧。看上去有点多此一举。但是,至少是个很好的学习机会。这个和上面不同的是,它条件字段是唯一的,变化的是该字段的值。那用一string将字段名成传入,并用一集合将字段值传入函数。
该函数完整的定义入下:


    
public static IQueryable<TEntity> WhereOr<TEntity, OrType>(this IQueryable<TEntity> source,
string
 propertyName, IEnumerable<OrType> values)
    
{
        
if (source == null)
            
throw new ArgumentNullException("Source can't be null!!");
        ParameterExpression param 
= Expression.Parameter(typeof(TEntity), "p");
        Expression left 
= Expression.Property(param, propertyName);
        Expression condition 
= null;
        
foreach (OrType value in values)
        
{
            Expression filter 
= Expression.Equal(left, Expression.Constant(value));
            
if (condition == null)
                condition 
= filter;
            
else
                condition 
= Expression.Or(condition,filter);
        }

        
if (condition != null)
            
return source.Where((Expression<Func<TEntity, bool>>)Expression.Lambda(condition,
param));

        
return source;
    }

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别


3, CLR与SQL在某些细节上的差别
在上文中,有一朋友提出,其值不为null才做为条件,让函数有局限性。既然提了,那笔者就再引申下。CLR与SQL中,对待null值是不同的。CLR认为两个null值是相等的,而SQL并不这么认为。比如,下面的条件就是成立的。
        if (null == null)
            throw new Exception("CLR treat Null is the same!!");
但在Sql中只能判断是不是null值,而不能对两个字段的null值直接比较。
比如下面的语句
        var q6 = db.Employees.Where(c => c.Region == null).ToList();
翻译为:

SELECT [t0].[EmployeeID][t0].[LastName][t0].[FirstName][t0].[Title][t0].
[TitleOfCourtesy][t0].[BirthDate][t0].[HireDate][t0].[Address][t0].[City
][t0].[Region][t0].[PostalCode][t0].[Country][t0].[HomePhone][t0].[Ext
ension
][t0].[Photo][t0].[Notes][t0].[ReportsTo][t0].[PhotoPath]
FROM [dbo].[Employees] AS [t0]
WHERE [t0].[Region] IS NULL

it知识库Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。