zoukankan      html  css  js  c++  java
  • Entity framework 中Where、First、Count等查询函数使用时要注意

    在.Net开发中,Entity framework是微软ORM架构的最佳官方工具。我们可以使用Lambda表达式在Entity framework中DbSet<T>类上直接做查询(比如使用DbSet<T>类的Where、First、Count等查询函数)返回数据库结果实体。

    不知道大家有没有注意到DbSet<T>类上的很多查询函数都有两种类型的重载,就拿Where这个查询函数举例:

    一种是传入Func<Tsource, bool>委托作为参数


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

    这种Where函数的重载返回的是IEnumerable<TSource>集合类型。

    调用的示例如下面代码所示:

    //CustomerDbContext为Entity framework中的DbContext
    using (var customerDbContext = new CustomerDbContext())
    {
        //显示Entity framework底层调用的Sql语句到Visual Studio的输出窗口
        customerDbContext.Database.Log = (message) => {
            Debugger.Log(0, "Sql", message);
        };
    
        Func<Mid_TriaBalance, bool> func = r => r.ID == 1;//使用lambda表达式查询ID为1的数据库数据
        var triaBalance = customerDbContext.Mid_TriaBalance.Where(func).First();
    }

    我们在上面的代码中使用了DbContext的Log委托,显示Entity framework生成的底层Sql语句到Visual Studio,运行该代码,我们来看看生成的Sql语句是什么,结果如下截图:

    结果让人大跌眼镜,我们发现实际上Entity framework生成的Sql没有包含任何Where条件,是将整张Mid_TriaBalance表的数据都返回到C#代码后,再过滤出ID等于1的这一行数据。这种方式当Mid_TriaBalance表的数据比较少的时候还好,但是一旦Mid_TriaBalance表的数据很大比如100万行,那么我们为了查询ID为1的这一行数据,就需要将Mid_TriaBalance表中的100万行数据从数据库中取出先放到内存中,再去筛选ID为1的这一行数据,效率低下可想而知,还有可能造成服务器内存溢出。


    一种是传入Expression<Func<TSource, bool>>类型作为参数:


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

    这种Where函数的重载返回的是IQueryable<TSource>集合类型。

    调用的示例如下面代码所示:

    //CustomerDbContext为Entity framework中的DbContext
    using (var customerDbContext = new CustomerDbContext())
    {
        //显示Entity framework底层调用的Sql语句到Visual Studio的输出窗口
        customerDbContext.Database.Log = (message) => {
            Debugger.Log(0, "Sql", message);
        };
    
        Expression<Func<Mid_TriaBalance, bool>> exp = r => r.ID == 1;//使用lambda表达式查询ID为1的数据库数据
        var triaBalance = customerDbContext.Mid_TriaBalance.Where(exp).First();
    }

    同样我们在上面的代码中使用了DbContext的Log委托,显示Entity framework生成的底层Sql语句到Visual Studio,运行该代码,我们来看看生成的Sql语句是什么,结果如下截图:

    这次我们看到Where函数使用Expression<Func<TSource, bool>>类型传入Lambda表达式后,Entity framework在底层生成了我们期望的Sql语句,包含了Where条件去限制数据库只查询ID为1的数据。很明显这种方式只会从数据库返回ID为1的一行数据到C#代码,效率和性能明显优于传入Func<Tsource, bool>委托的重载方式。

    总结 

    我们可以看到这两种调用方式乍看之下觉得差别不大,最终都是返回Mid_TriaBalance表中ID为1的这一行数据。但是后台生成的Sql语句却有天壤之别,查询性能也有天壤之别。应该在Entity framework的Where、First、Count等查询函数中,避免使用传入Func<Tsource, bool>委托这种重载,因为这种重载在后台生的Sql语句中是不带任何Where限制条件的,是将整张表的数据先从数据库查出来后,放到C#代码内存中再做过滤,非常低效。

    此外Expression<Func<TSource, bool>>类的构造函数无法直接调用,我们是无法去直接new一个Expression<Func<TSource, bool>>对象的,只有通过将lambda表达式直接赋值给Expression<Func<TSource, bool>>类型做隐式转换,C#会自动生一个Expression<Func<TSource, bool>>对象如下面代码所示:

    Expression<Func<Mid_TriaBalance, bool>> exp = r => r.ID == 1;//使用lambda表达式直接给Expression<Func<TSource, bool>>类型赋值,C#会帮助我们生成一个Expression<Func<TSource, bool>>对象

    另外注意,不能将Func<TSource, bool>委托直接赋给Expression<Func<TSource, bool>>类型,这两种类型没法做类型转换,如下图所示,代码编译会报错:

  • 相关阅读:
    希腊字母写法
    The ASP.NET MVC request processing line
    lambda aggregation
    UVA 10763 Foreign Exchange
    UVA 10624 Super Number
    UVA 10041 Vito's Family
    UVA 10340 All in All
    UVA 10026 Shoemaker's Problem
    HDU 3683 Gomoku
    UVA 11210 Chinese Mahjong
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/8143972.html
Copyright © 2011-2022 走看看