zoukankan      html  css  js  c++  java
  • Func转Expression的方法(C#)

    这篇文章写完后,发现网上有大量关于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);
    }

    分别进行调用:

    FetchData2<Products>(m => m.ProductID > 15).ToList();
    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却不容易,再多看一下,既然实现体是一样,其实也就是声明不一样了,那么直接更改参数声明不就可以了么?上面已经演示过了,不需要任何硬编码,直接生效。

  • 相关阅读:
    MySQL之汇总数据(AVG,COUNT,MAX,MIN,SUM)
    查询两门及两门以上不及格同学的平均分
    百度乐播音乐真实地址查找api接口
    腾讯云服务器无法ssh登陆问题
    ubuntu搭建php运行环境
    阿里云腾讯云服务器ubuntu多域名配置
    PHP 快速排序算法详解
    PHP7.x新特性
    PHP的钩子实现解析
    PHP 二维数组根据某个字段排序 复制代码 array_multisort
  • 原文地址:https://www.cnblogs.com/walkerwang/p/2983153.html
Copyright © 2011-2022 走看看