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>>类型,这两种类型没法做类型转换,如下图所示,代码编译会报错:

  • 相关阅读:
    典型用户和场景总结
    排球的详细记分规则
    js 实现全选
    博客搬家啦!
    leetcode #980 不同路径||| (java)
    ADV-299 宰羊 (java,过了30%)
    ADV-292 计算行列式 java
    ADV-302 秘密行动 java
    ADV-297 快速排序 java
    ADV-298 和谐宿舍2 动态规划
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/8143972.html
Copyright © 2011-2022 走看看