首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架6 DbSet.Find效率

实体框架6 DbSet.Find效率
EN

Stack Overflow用户
提问于 2016-02-19 05:03:58
回答 2查看 679关注 0票数 1

我有两种根据Id查找实体的方法。第一个使用IQueryable对象,通过一个λ表达式根据Id查找对象,另一个使用内置的实体框架DbSet.Find()方法。我在Visual Studio中编写了几个单元测试,为这两种方法创建速度基准,以确定哪种方法更好。

有趣的是,我编写的方法比Find中内置的实体框架获得了更好的结果。有人知道为什么吗?

以下是Entity Framework方法的代码:

代码语言:javascript
复制
public virtual T Find<T>(int id)
{
    return dbContext.Set<T>().Find(id);
}

下面是我的方法的代码:

代码语言:javascript
复制
public virtual T FindById<T>(int id)
{
    return dbContext.Set<T>().Where(x => x.IsActive).AsQueryable().FirstOrDefault(x => x.Id == id);
}

下面是从数据库中选择几条记录所需的时间:

下面是我的测试类:

代码语言:javascript
复制
[TestClass]
public class EntityBenchmarks
{
    EdiDataStore target;

    [TestInitialize]
    public void Start()
    {
        target = new EdiDataStore();
    }

    [TestCleanup]
    public void Cleanup()
    {
        target.Dispose();
    }

    //this method is only here because i want to make sure that the 
    //database context is loaded into memory so that we can compare 
    //Find and FindById on an even scale.  Without this method, the first 
    //time benchmark that runs is hit with the overhead of loading the model.
    [TestMethod]
    public void Control()
    {
        var entities = target.GetAgencies();
    }

    [TestMethod]
    public void FindBenchmark()
    {
        bool isSuccess = true;

        for (int i = 9177; i <= 9187; i++)
        {
            var entity = target.Find<Spot>(i);
            isSuccess = isSuccess && entity != null;
        }

        Assert.IsTrue(isSuccess);
    }

    [TestMethod]
    public void FindByIdBenchmark()
    {
        bool isSuccess = true;

        for (int i = 9177; i <= 9187; i++)
        {
            var entity = target.FindById<Spot>(i);
            isSuccess = isSuccess && entity != null;
        }

        Assert.IsTrue(isSuccess);
    }
}
EN

回答 2

Stack Overflow用户

发布于 2016-02-19 05:11:41

在不知道如何设置测试的情况下,甚至不可能尝试回答问题。会不会是FindBenchmark先运行,并且时间包含了引导EF的开销?EF在第一个查询上做了一些相当繁重的工作,这些查询与实际查询无关,但与延迟初始化有关,因此您不能真的将第一个查询与随后的查询进行比较。另一方面,Find首先在EF跟踪的实体中查找实体,而FindById将始终访问数据库-同样,如果您使用相同的上下文并切换顺序,结果可能完全不同,因为FindById将带来实体,而Find将不会访问数据库。

票数 1
EN

Stack Overflow用户

发布于 2016-02-19 05:50:45

性能测试有几处地方出了问题。

  1. Pawel的回答是正确的,在EF第一次运行时,会产生大量的开销。第一个EF查询比其他查询花费的时间要长得多。
  2. 你测试的不是同一个查询。Find“查找具有给定主键值的实体”(https://msdn.microsoft.com/en-us/library/gg696418(v=vs.113).aspx。我假设您的SQL标志不是主键的一部分,因此上面的方法将生成不同的IsActive语句。不能比较使用不同SQL语句的两种方法的性能。

例如: dbContext.Set().Find(id)将产生类似如下的结果:

SELECT * FROM Foo WHERE FooID = 123

另一方面,dbContext.Set().Where(f => f.IsActive).FirstOrDefault(f => f.FooID == id)将生成如下内容:

SELECT * FROM FooID FooID= 123和IsActive =1

这本身可能会产生截然不同的结果,这取决于SQL使用什么索引以及SQL Server决定构建什么样的执行计划。如果你想比较不同的苹果,你应该比较:

dbContext.Set().Find(123);

dbContext.Set().Single(f => f.FooID == 123)

  • 您的FindByID正在执行无关代码。您正在执行Where(...)然后对Where的结果调用AsQueryable(),然后调用FirstOrDefault()。IQueryable(T).Where()扩展方法已返回IQueryable(T)。在此之后调用AsQueryable()是不必要的。

如果要显式测试检索相同数据的不同方法的性能,则需要确保这些方法执行完全相同的操作。确保这一点的最佳方法是运行SQL事件探查器,并确保生成的底层SQL是相同的。否则,您正在测试的就是SQL Server是否可以生成两个不同的数据库查询,这两个查询的执行时间或多或少都不同,这是没有意义的。

此外,不要对查询调用无关的扩展方法。在这种情况下,AsQueryable是无害的,但是调用错误的扩展方法也可以实现结果集,将每个实体重新放回内存中,并对内存中的集合运行另一个查询。这也会对性能产生巨大的影响。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35492367

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档