动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处。而Linq的推出,是为了弥补编程中的 Data != Object 的问题。我们又该如何实现用object的动态查询呢?

1,用object的查询是什么?
我们可以简单的举这么一个例子。我们到公安局查找一个人。首先,我们会给出他的一些特征,比如,身高多少,年龄多少,性别,民族等。那么,我们把这个人的一些特征输入电脑。我们希望,电脑能给我们返回这个人的信息。而实际上,有相同特征的人太多了,常常返回一个集合。那让我们把这个过程抽象到程式里。我们需要new出来一个对象。这个对象包含了我们能知道的基本信息。而后,把这个对象传给Linq To Sql,等待返回结果。

根据这些基本的需求,我们来定义下面的函数,为了实现这个函数对任何实体都是有用的,我们把它定义为generic的。为了不破坏Linq To Sql延迟加载的规矩,我们把它的返回类型定义为IQueryable。如下:

public IQueryable<TEntity> Find<TEntity>(TEntity obj) where TEntity : class

思路出来了,先new出来一个对象,然后把对象传给这个函数,我们渴望它能返回与这个对象匹配的结果集。为了让它和DataContext有关系,我们把这个函数放到DataContext的partial类里。鼠标右击Linq To Sql文件,选择view code,这个时候,vs会为你创造一个DataContext的partial类,其扩展名比影射文件少了中间的desiger。大家要注意,你如果想自己修改影射文件,请放到这个文件里。这样当影射code被刷新时,才不会冲掉你自己的修改。先大体描述下我们的思路。

NorthwindDataContext db = new NorthwindDataContext();
//先new出一个对象            
Customer c = new Customer();
//添入我们知道的最基本的信息,可以从ui获得
c.City = "London";
c.Phone = "23236133";
//call函数find返回结果
var q = db.Find<Customer>(c);

2,原理
Linq To Sql支持用户动态生成lambda表达式。本文中所实现的方法,正是反射加lambda动态表达式。我们先来看如何动态生成lambda表达式。在Linq 中,lambda表达式会首先转化为Expression Tree,本文并不详解Expression Tree。Expression Tree是lambda表达式从code的形式转化为data的结果,是一种更高效的在内存中的数据结构。比如:  
Func<int,int> f = x => x + 1;                               // Code

Expression<Func<int,int>> e = x => x + 1;       // Data

第二个,其实也就是第一个转化后的形式。那好了,有了这个前提,我们就可以动态构造这个Expression Tree了。
// 先构造了一个ParameterExpression对象,这里的c,就是Lambda表达中的参数。(c=>)                            
ParameterExpression param = Expression.Parameter(typeof(TEntity), "c"); 
//构造表达式的右边,值的一边
Expression right = Expression.Constant(p.GetValue(obj, null));
//构造表达式的左边,property一端。
Expression left = Expression.Property(param, p.Name);
//生成筛选表达式。即c.CustomerID == "Tom"
Expression filter = Expression.Equal(left, right);
//生成完整的Lambda表达式。
Expression<Func<TEntity, bool>> pred = Expression.Lambda<Func<TEntity, bool>>(filter, param);
//在这里,我们使用的是and条件。
query = query.Where(pred);

3,反射在本方法中的作用
因为我们采用了模板,也就是说,我们并不知道传进来的对象会有那些property,那反射在这里就提供一个很好的方法。我们可以通过反射去遍历每一个property,只有判断出该property的值不为null时,才将其视为条件。该函数完整的代码如下:

        public IQueryable<TEntity> Find<TEntity>(TEntity obj) where TEntity : class
        {
            //获得所有property的信息
            PropertyInfo[] properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            //构造初始的query
            IQueryable<TEntity> query = this.GetTable<TEntity>().AsQueryable<TEntity>();
            //遍历每个property
            foreach (PropertyInfo p in properties)
            {
                if (p != null)
                {
                    Type t = p.PropertyType;
                    //加入object,Binary,和XDocument, 支持sql_variant,imager 和xml等的影射。
                    if (t.IsValueType || t == typeof(string) || t == typeof(System.Byte[])
                        || t == typeof(object) || t == typeof(System.Xml.Linq.XDocument)
                        || t == typeof(System.Data.Linq.Binary))
                    {
                        //如果不为null才算做条件
                        if ( p.GetValue(obj, null) != null)
                        {
                            ParameterExpression param = Expression.Parameter(typeof(TEntity), "c");
                            Expression right = Expression.Constant(p.GetValue(obj, null));
                            Expression left = Expression.Property(param, p.Name);
                            Expression filter = Expression.Equal(left,right);

                            Expression<Func<TEntity, bool>> pred = Expression.Lambda<Func<TEntity, bool>>(filter, param);
                            query = query.Where(pred);
                        }
                    }
                }
            }
            return query;
        }

4,测试用例及反思
我们用下面的例子来测试下这个函数

            Customer c = new Customer();
            c.City = "London";
            c.Phone = "23236133";

            var q = db.Find<Customer>(c).ToList();

其生成的sql语句为:

SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
FROM [dbo].[Customers] AS [t0]
WHERE ([t0].[Phone] = @p0) AND ([t0].[City] = @p1)
-- @p0: Input NVarChar (Size = 8; Prec = 0; Scale = 0) [23236133]
-- @p1: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [London]

我们可以看到,其sql语句中,只有city和phone两个条件。并且他们之间是and的关系。我们最开始的设想实现了,但是,它是完美的吗?如果是or条件该怎么办呢?更多的时候,我们是在用模糊查询,那又该怎么办呢?这个问题,就留于下篇。

最后,介绍一种写log的方法。stream流使用static为避免多个datacontext的同时在使用log.txt文件。

    partial class DataMappingDataContext
    {
        private static StreamWriter sw = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "log.txt"),true);

        /// <summary>
        /// Try to create DataContext with log. 
        /// </summary>
        /// <param name="withLog"></param>

        public DataMappingDataContext(bool withLog)
            : this()
        {
            OnCreated();
            if (withLog)
            {
                if (sw == null)
                {
                    sw = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "log.txt"), true);
                }
                this.Log = sw;
            }
        }

        /// <summary>
        /// try to close streamwriter
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            sw.Flush();

        }
    }

在dispose函数里,把输出流flush。使用时,如下
using(northwind db = new norhwind(true))
{
//do something......
}
好,就先讲到这里。

Linq To Sql进阶系列(六)用object的动态查询与保存log篇的更多相关文章

  1. SQL进阶系列之6用关联子查询比较行与行

    写在前面 使用SQL对同一行数据进行列间的比较很简单,只需要在WHERE子句里写上比较条件就可以了,对于不同行数据进行列间比较需要使用自关联子查询. 增长.减少.维持现状 需要用到行间比较的经典场景是 ...

  2. Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图

    Bing Maps进阶系列六:使用Silverlight剪切(Clip)特性实现Bing Maps的迷你小地图 Bing Maps Silverlight Control虽然为我们提供了简洁.方面的开 ...

  3. .NET深入实战系列—Linq to Sql进阶

    最近在写代码的过程中用到了Linq查询,在查找资料的过程中发现网上的资料千奇百怪,于是自己整理了一些关于Linq中容易让人困惑的地方. 本文全部代码基于:UserInfo与Class两个表,其中Cla ...

  4. SQL进阶系列之7用SQL进行集合运算

    写在前面 集合论是SQL语言的根基,因为这种特性,SQL也被称为面向集合语言 导入篇:集合运算的几个注意事项 注意事项1:SQL能操作具有重复行的集合(multiset.bag),可以通过可选项ALL ...

  5. SQL进阶系列之12SQL编程方法

    写在前面 KISS -- keep it sweet and simple 表的设计 注意命名的意义 英文字母 + 阿拉伯数字 + 下划线"_" 属性和列 编程的方针 写注释 注意 ...

  6. SQL进阶系列之10HAVING子句又回来了

    写在前面 HAVING子句的处理对象是集合而不是记录 各队,全队点名 --各队,全体点名! CREATE TABLE Teams (member CHAR(12) NOT NULL PRIMARY K ...

  7. SQL进阶系列之11让SQL飞起来

    写在前面 SQL的性能优化是数据库使用者必须面对的重要问题,本节侧重SQL写法上的优化,SQL的性能同时还受到具体数据库的功能特点影响,这些不在本节讨论范围之内 使用高效的查询 参数是子查询时,使用E ...

  8. SQL进阶系列之9用SQL处理数列

    写在前面 关系模型的数据结构里,并没有顺序的概念,但SQL处理有序集合也有坚实的理论基础 生成连续编号 --生成连续编号 CREATE TABLE Digits (digit INTEGER PRIM ...

  9. SQL进阶系列之8EXISTS谓词的用法

    写在前面 支撑SQL和关系数据库的基础理论:数学领域的集合论和逻辑学标准体系的谓词逻辑 理论篇 什么是谓词?谓词是返回值为真值(true false unknown)的函数 关系数据库里,每一个行数据 ...

随机推荐

  1. 【转载】CentOS 7安装Python3.5,并与Python2.7兼容并存

    CentOS 7下安装Python3.5 CentOS7默认安装了python2.7.5,当需要使用python3的时候,可以手动下载Python源码后编译安装. 1.安装python3.5可能使用的 ...

  2. 九度oj 题目1398:移动次数

    题目描述: 众所周知JOBDU旗下的JOBBALA公司是一家以个性.亲民著称的IT公司.在JOBBALA公司成立50周年的日子里,公司CEO组织全体员工登山旅游.按照往常的习惯,导游通常要求游客按照身 ...

  3. 使用Unity做2.5D游戏教程(二)

    最近在研究Unity 3D,看了老外Marin Todorov写的教程很详细,就翻译过来以便自己参考,翻译不好的地方请多包涵. 这是使用Unity 游戏开发工具制作一个简单的2.5D 游戏系列教程的第 ...

  4. 【Luogu】P3052摩天大楼里的奶牛(遗传算法乱搞)

    一道状压题,但今天闲来无事又用遗传乱搞了一下. 设了一个DNA数组,DNA[i]记录第i个物品放在哪个组里.适应度是n-这个生物的组数+1. 交配选用的是轮盘赌和单亲繁殖——0.3的几率单点变异.(事 ...

  5. LightOJ1125 Divisible Group Sums

    Divisible Group Sums Given a list of N numbers you will be allowed to choose any M of them. So you c ...

  6. codevs——1570 去看电影

    1570 去看电影  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 农夫约翰带着他的一些奶牛去看电影.而他的 ...

  7. 面试题:oracle数据库行转列的问题

    今天我一个学弟问了一个面试题: 有表A,结构如下:A: p_ID p_Num s_id1 10 011 12 022 8 013 11 013 8 03其中:p_ID为产品ID,p_Num为产品库存量 ...

  8. Effective Java P2 Creating and Destroying Objects

    This chapter concerns creating and destorying objects : when and how to create them, when and how to ...

  9. Java Enum枚举的用法(转)

    说明:Java的枚举比dotnet的枚举好用,至少支持的方式有很多. 用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以 ...

  10. react 起手式

    http://blog.csdn.net/zhouzhiande/article/details/52349344 http://blog.csdn.net/zhouzhiande/article/d ...