• 理解LINQ预编译查询(Compiled LINQ)


    从上一篇NHibernate与Entity Framework性能比较中可以看出来LINQ预编译查询可以大大提高性能。本文对预编译查询做简单介绍。 

    首先了解什么是匿名方法(anonymous function)和Func<>  

    1. 命名方法,匿名方法和Lambda表达式

    在C#2.0之前,对委托赋值的唯一方式是通过的命名方法。C#2.0引入匿名方法以内联(in-line)方式使之变得直观和方便,而且匿名方法可以省略参数列表,可以将匿名方法转换为不同签名的委托。

    C#3.0中Lambda表达式的语法跟匿名方法的非常相似,可用于构建委托或者LINQ表达式(Expression)。后面的例子中都会使用Lambda表达式对委托赋值。

    class Test
    {
    delegate void TestDelegate(string s);
    static void M(string s)
    {
    Console.WriteLine(s);
    }

    static void Main(string[] args)
    {
    // Original delegate syntax required initialization with a named method.
    TestDelegate testDelA = new TestDelegate(M);

    // C# 2.0: A delegate can be initialized with inline code, called an "anonymous method." This
    // method takes a string as an input parameter.
    TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };

    // C# 3.0. A delegate can be initialized with a lambda expression. The lambda also takes a string
    // as an input parameter (x). The type of x is inferred by the compiler.
    TestDelegate testDelC = (x) => { Console.WriteLine(x); };
            // Invoke the delegates.
    testDelA("Hello. My name is M and I write lines.");
    testDelB("That's nothing. I'm anonymous and ");
    testDelC("I'm a famous author.");
    }
    }

    2. Linq.Expression 和 Func<>

    Linq.Expression就是LINQ表达式,继承Linq.Expressions.LambdaExpression

    Func<>是一种委托,在LINQ中Func<>代表对应LINQ表达式的委托。

    Linq表达式要变成可执行的代码(不是指SQL,而是指C#代码), 需要将Expression编译Func<>。我们通过一下代码来测试

                //此时sqrExpression只是一个Expression,并不能执行
    Expression<Func<double, double>> sqrExpression = (x => x * x);

    //Compile成可执行的Func<>
    Func<double, double> sqr = sqrExpression.Compile();

    Console.Write(sqr(3));

    3. 预编译LINQ

    Compile是一步耗时的操作,在执行普通Linq查询的时候,每执行一次都自动Compile,即使同样的查询(参数可能不一样)已经被执行过多次。

    EF对此有缓存机制,在同一个context内会将compile后的结果缓存起来,但是在一般场景下使用的都是短生命周期(short-life)的context,此缓存的作用是杯水车薪。

     //普通Linq查询       
    public IList<Product> SearchByName(string name)
    {
    using (NorthWindEntities ctx = new NorthWindEntities())
    {
          //每次执行是都会自动compile
    return ctx.Products.Where(o => o.ProductName.Contains(name.Trim())).ToList();
    }
    }

     

    对此我们可以考虑将编译后的结果(Func<>)保存起来以重用

          private static Func<NorthWindEntities, string, IQueryable<Product>> searchByNameQuery =
    CompiledQuery.Compile<NorthWindEntities, string, IQueryable<Product>>
    ((NorthWindEntities ctx, string name) => ctx.Products.Where(o => o.ProductName.Contains(name.Trim())));
            public IList<Product> SearchByNameCompiled(string name)
    {
    using (NorthWindEntities ctx = new NorthWindEntities())
    {
    return searchByNameQuery(ctx, name).ToList();
    }
    }

    上面的例子我们把编译后的Func<>放到一个static变量中,这样可以保证全局范围内只需要compile一次。当然也可以在此基础上再做改进,比如Lazy-load Singleton。

    为了提高程序性能,你几乎可以在所用地方都使用预编译LINQ,当然查询语句越复杂的时候效果就越明显。

  • 相关阅读:
    问题集
    第04次作业-树
    06-图
    05-查找
    04-树
    03-栈和队列
    02-线性表
    01-抽象数据类型
    C语言--总结报告
    C语言--函数嵌套
  • 原文地址:https://www.cnblogs.com/hiteddy/p/understanding_Compiled_LINQ.html
Copyright © 2020-2023  润新知