C# Linq到实体表达式/Func之间的性能差异

C# Linq到实体表达式/Func之间的性能差异,c#,performance,linq,entity-framework,C#,Performance,Linq,Entity Framework,我刚刚测试了一个简单的查询,我正在以不同的方式访问它,但是每个查询的速度最多可以相差2秒。我希望有人能澄清为什么会这样。我的项目还处于非常早期的阶段,所以我想我应该确保在它变得太大之前我做得很好 诚然,我的测试风格并不完美,但我认为这已经足够好了 我使用的是一个通用存储库和UnitofWork,在这个while语句中,我点击了DB(本地机器上的sqlexpress)10000次。该表只有64条记录。测试在发布模式下运行 [TestMethod] public void MyTestMethod(

我刚刚测试了一个简单的查询,我正在以不同的方式访问它,但是每个查询的速度最多可以相差2秒。我希望有人能澄清为什么会这样。我的项目还处于非常早期的阶段,所以我想我应该确保在它变得太大之前我做得很好

诚然,我的测试风格并不完美,但我认为这已经足够好了

我使用的是一个通用存储库和UnitofWork,在这个
while
语句中,我点击了DB(本地机器上的sqlexpress)10000次。该表只有64条记录。测试在发布模式下运行

[TestMethod]
public void MyTestMethod()
{
    using (var u = new UnitOfWork())
    {
        TestA(u);
        TestB(u);
    }
}
测试(功能):

Single()
func(IEnumerable)

with
Expression
参数是
IQueryable
上的扩展方法,由查询提供程序使用,如LINQ to实体。您提供的表达式树将转换为适当的SQL查询,并将其发送到DB,只将必要的行返回到应用程序

with
Func
参数是对
IEnumerable
的一种扩展方法,LINQ使用它来处理对象,这意味着数据库中的所有记录都将被提取到应用程序内存中,然后元素将作为内存中的查询进行搜索,这是一种线性搜索

您肯定应该使用
IQueryable
中的一个,因为它将更加高效(因为数据库已优化以执行查询)。

with
Expression
参数是
IQueryable
上的一个扩展方法,由查询提供程序使用,如LINQ to实体。您提供的表达式树将转换为适当的SQL查询,并将其发送到DB,只将必要的行返回到应用程序

with
Func
参数是对
IEnumerable
的一种扩展方法,LINQ使用它来处理对象,这意味着数据库中的所有记录都将被提取到应用程序内存中,然后元素将作为内存中的查询进行搜索,这是一种线性搜索


您肯定应该使用
IQueryable
中的一个,因为它将更有效(因为数据库已优化以执行查询)。

我将列出一些测试,您可能希望帮助您缩小操作之间的差异

检查实际的SQL代码

打开查询的调试日志或在SSE日志上检查它。这一点很重要,因为EF引擎应该优化语句,您可以看到真正发送到DB的内容

正如您所说的,
First
操作应该更快,因为有优化的SQL操作符。
单个
应该较慢,因为它必须验证所有的值,并且会根据行的数量进行缩放

使用数据库上的真实SQL进行参考测试

一旦有了真正的SQL,您还可以直接检查数据库上经过的时间差。在DB上执行相同的C#测试,可能是一个Sotred过程,看看会发生什么

尝试内置LINQ进行比较

我不知道你是否已经做了测试,但是试着使用本地LINQ进行比较

我在这里使用LINQ做了很多测试,并且您提供的两个语句之间没有差异,所以它实际上可能是表达式。(我使用了SS CE btw)

另外,为了说明这一点,请remmember为涉及繁重操作的列创建索引;) EF 6.1现在内置了此功能

  [Index]
  public String MyProperty{ get; set; }

让我知道它是否有用。

我将列出一些测试,您可能希望帮助您缩小操作之间的差异

检查实际的SQL代码

打开查询的调试日志或在SSE日志上检查它。这一点很重要,因为EF引擎应该优化语句,您可以看到真正发送到DB的内容

正如您所说的,
First
操作应该更快,因为有优化的SQL操作符。
单个
应该较慢,因为它必须验证所有的值,并且会根据行的数量进行缩放

使用数据库上的真实SQL进行参考测试

一旦有了真正的SQL,您还可以直接检查数据库上经过的时间差。在DB上执行相同的C#测试,可能是一个Sotred过程,看看会发生什么

尝试内置LINQ进行比较

我不知道你是否已经做了测试,但是试着使用本地LINQ进行比较

我在这里使用LINQ做了很多测试,并且您提供的两个语句之间没有差异,所以它实际上可能是表达式。(我使用了SS CE btw)

另外,为了说明这一点,请remmember为涉及繁重操作的列创建索引;) EF 6.1现在内置了此功能

  [Index]
  public String MyProperty{ get; set; }

让我知道它是否有用。

这不是答案,只是为了确保测试结果更可靠

尝试这样编写测试:

public long TestA()
{
    using (var u = new UnitOfWork())
    {
        var s = Stopwatch.StartNew();
        var x = 0;
        var repo = u.Repository<MyEntity>();
        var code = "ABCD".First().ToString();
        while (x < 10000)
        {
            var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
            x++;
        }
        s.Stop();
        return s.ElapsedMilliseconds;
    }
}
public int TestA()
{
    var gc0 = GC.CollectionCount(0);
    using (var u = new UnitOfWork())
    {
        var s = Stopwatch.StartNew();
        var x = 0;
        var repo = u.Repository<MyEntity>();
        var code = "ABCD".First().ToString();
        while (x < 10000)
        {
            var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
            x++;
        }
        s.Stop();
    }
    return GC.CollectionCount(0) - gc0;
}
现在你的结果可能会更准确。现在让我们知道时间


现在,尝试如下更改您的测试:

public long TestA()
{
    using (var u = new UnitOfWork())
    {
        var s = Stopwatch.StartNew();
        var x = 0;
        var repo = u.Repository<MyEntity>();
        var code = "ABCD".First().ToString();
        while (x < 10000)
        {
            var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
            x++;
        }
        s.Stop();
        return s.ElapsedMilliseconds;
    }
}
public int TestA()
{
    var gc0 = GC.CollectionCount(0);
    using (var u = new UnitOfWork())
    {
        var s = Stopwatch.StartNew();
        var x = 0;
        var repo = u.Repository<MyEntity>();
        var code = "ABCD".First().ToString();
        while (x < 10000)
        {
            var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
            x++;
        }
        s.Stop();
    }
    return GC.CollectionCount(0) - gc0;
}
public int TestA()
{
var gc0=GC.CollectionCount(0);
使用(var u=new UnitOfWork())
{
var s=Stopwatch.StartNew();
var x=0;
var repo=u.Repository();
var code=“ABCD”.First().ToString();
而(x<10000)
{
var testCase=repo.Single(w=>w.Code==Code&&w.CodeOrder==0);
x++;
}
s、 停止();
}
返回GC.CollectionCount(0)-gc0;
}
这应该确定执行了多少次第0代垃圾回收。这可能表明性能问题与测试有关,而与SQL无关
public long TestA()
{
    using (var u = new UnitOfWork())
    {
        var s = Stopwatch.StartNew();
        var x = 0;
        var repo = u.Repository<MyEntity>();
        var code = "ABCD".First().ToString();
        while (x < 10000)
        {
            var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
            x++;
        }
        s.Stop();
        return s.ElapsedMilliseconds;
    }
}
[TestMethod]
public void MyTestMethod()
{
    var dummyA = TestA();
    var dummyB = TestB();

    var realA = 0L;
    var realB = 0L;
    for (var i = 0; i < 10; i++)
    {
        realA += TestA();
        realB += TestB();
    }

    Console.WriteLine("TESTA: " + realA.ToString());
    Console.WriteLine("TESTB: " + realA.ToString());
}
public int TestA()
{
    var gc0 = GC.CollectionCount(0);
    using (var u = new UnitOfWork())
    {
        var s = Stopwatch.StartNew();
        var x = 0;
        var repo = u.Repository<MyEntity>();
        var code = "ABCD".First().ToString();
        while (x < 10000)
        {
            var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
            x++;
        }
        s.Stop();
    }
    return GC.CollectionCount(0) - gc0;
}