zoukankan      html  css  js  c++  java
  • Linq To Sql进阶系列(六)用object的动态查询与保存log篇

    动态的生成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] = @p0AND ([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......
    }
    好,就先讲到这里。

    TrackBack:http://www.cnblogs.com/126/archive/2007/09/09/887723.html

  • 相关阅读:
    python——多进程
    python—mariadb自动部署主从
    docker镜像的操作
    docker容器的操作
    docker——数据卷volume:文件共享
    数据库介绍
    数据库的基本配置与操作
    数据库的增删改
    ELK的搭建
    数据库的查——select的基本使用
  • 原文地址:https://www.cnblogs.com/hdjjun/p/1327096.html
Copyright © 2011-2022 走看看