• Entity Framework where语句起作用的真正用法


    之前对Entity Framework的理解不深,只是图方便,作为linqtosql的替代,但是碰到数据库内容非常多(10万-100万条)时,分页效率很低,用 profiler查看sql语句,才发现其竟然是一次性读取全部数据到内容,然后再执行where之后的语句,真是恐怖。

    在网上查询很久,终于发现以下文章,遂记录如下:

    这篇文章写完后,发现网上有大量关于Expresstion和Func的讨论,可以不看我的,看这几篇,是一样的,还更深入一些:

    http://fascinatedwithsoftware.com/blog/post/2012/01/10/More-on-Expression-vs-Func-with-Entity-Framework.aspx

    http://fascinatedwithsoftware.com/blog/post/2011/12/02/Falling-in-Love-with-LINQ-Part-7-Expressions-and-Funcs.aspx

    http://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct

    http://q.cnblogs.com/q/37952/

    为什么有这个需求,先看如下两个扩展方法:

    public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
    

      

    这就是我们用Linq, EntityFramework的时候常用的Where方法而已,平时也没去注意过它们的差别:

    传入Func,得到的是IEnumerable对象,传入Expression,得到的是IQueryable对象,那么它们之间到底有什么差别?

    如下示例:

    我要查NorthWind数据库里的Products表里ProductId大于15的所有产品,很简单的一句话,为了分别传入Func和Expression,我们做如下封装:

    //接受Func参数,返回IEnumerable
    private IEnumerable<T> FetchData2<T>(Func<T, bool> f) where T : class
    {
        var context = new NorthwindEntities();
        return context.Set<T>().Where(f);
    }
     
    //接受Expression<Func>参数,返回IQueryable
    private IQueryable<T> FetchData<T>(Expression<Func<T, bool>> f) where T : class
    {
        var context = new NorthwindEntities();
        return context.Set<T>().Where(f);
    }
    

      

     

    分别进行调用:

    1 FetchData2<Products>(m => m.ProductID > 15).ToList();
    2 FetchData<Products>(m => m.ProductID > 15).ToList();

    结果当然没什么区别,我们要的是SQL:

    --以Func为参数进行的查询,可见没有生成where语句
    SELECT
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[ProductName] AS [ProductName], 
    [Extent1].[SupplierID] AS [SupplierID], 
    [Extent1].[CategoryID] AS [CategoryID], 
    [Extent1].[QuantityPerUnit] AS [QuantityPerUnit], 
    [Extent1].[UnitPrice] AS [UnitPrice], 
    [Extent1].[UnitsInStock] AS [UnitsInStock], 
    [Extent1].[UnitsOnOrder] AS [UnitsOnOrder], 
    [Extent1].[ReorderLevel] AS [ReorderLevel], 
    [Extent1].[Discontinued] AS [Discontinued]
    FROM [dbo].[Products] AS [Extent1]
     
    --以Expression为参数进行的查询,如愿生成了where语句
    SELECT
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[ProductName] AS [ProductName], 
    [Extent1].[SupplierID] AS [SupplierID], 
    [Extent1].[CategoryID] AS [CategoryID], 
    [Extent1].[QuantityPerUnit] AS [QuantityPerUnit], 
    [Extent1].[UnitPrice] AS [UnitPrice], 
    [Extent1].[UnitsInStock] AS [UnitsInStock], 
    [Extent1].[UnitsOnOrder] AS [UnitsOnOrder], 
    [Extent1].[ReorderLevel] AS [ReorderLevel], 
    [Extent1].[Discontinued] AS [Discontinued]
    FROM [dbo].[Products] AS [Extent1]
    WHERE [Extent1].[ProductID] > 15
    

      

    这个结果是惊人的,居然传入func的情况下是把数据全部取到内存里再进行枚举过滤(linq to entity)~~~,这一下吃惊不小,可见平时的开发中这一点是必须要时刻注意的。

    其实这就是Linq to Sql和Linq to Entity的区别,由于重载的原因,都做到同样的方法上来,一旦参数传错了,就是别的方法了。

    比较还没完,因为一般的企业框架或接口,是不会暴露底层数据库操作出来的,对外的都是业务方法,比如我们上面的示例,目的是获取ID大于某值的所有产品,那么我们假定暴露成如下方法(接口):

    //获取ID大于某值的产品列表
    private IEnumerable<Products> GetProducts(int id)
    {
        var datas = FetchData<Products>(m => m.ProductID > id);
        return datas;
    }
    

      

    调用:

    GetProducts(15);
    

      

    生成SQL:

    exec sp_executesql N'SELECT 
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[ProductName] AS [ProductName], 
    [Extent1].[SupplierID] AS [SupplierID], 
    [Extent1].[CategoryID] AS [CategoryID], 
    [Extent1].[QuantityPerUnit] AS [QuantityPerUnit], 
    [Extent1].[UnitPrice] AS [UnitPrice], 
    [Extent1].[UnitsInStock] AS [UnitsInStock], 
    [Extent1].[UnitsOnOrder] AS [UnitsOnOrder], 
    [Extent1].[ReorderLevel] AS [ReorderLevel], 
    [Extent1].[Discontinued] AS [Discontinued]
    FROM [dbo].[Products] AS [Extent1]
    WHERE [Extent1].[ProductID] > @p__linq__0',N'@p__linq__0 int',@p__linq__0=15
    

      

    可以看一下跟上面生成的sql的差别,已经变成存储过程了。

    好吧,其实说到这里还没点题,上面只是说到了几种不同语法的差别,但是为什么有我标题这一说呢?因为来自于我的项目底层的某个封装:

    IEnumerable<T> GetEntities(Func<T, bool> exp);
    

      

    显然我用了Func传参,结果你们也知道了,于是我开始寻找Func转Expression的方法,一遍海搜之下,用了各种转化方式,才发现如下两句话都是成立的:

    Expression<Func<Products, bool>> g = m => m.ProductID > 15;
    Func<Products, bool> t = m => m.ProductID > 15;
    

      

    但是把t转成g却不容易,再多看一下,既然实现体是一样,其实也就是声明不一样了,那么直接更改参数声明不就可以了么?上面已经演示过了,不需要任何硬编码,直接生效。

  • 相关阅读:
    基础操作
    需要注意
    简单操作
    git指令-版本回退
    设计模式-代理模式
    在idea下遇到的问题汇总
    maven笔记--持续更新
    poi简介
    Win10添加右键在此处打开命令行
    Ajax&Json案例
  • 原文地址:https://www.cnblogs.com/4kapple/p/3822479.html
Copyright © 2020-2023  润新知