繁体   English   中英

实体框架6 DbSet.Find效率

[英]Entity Framework 6 DbSet.Find efficiency

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

有趣的是,我编写的方法比“查找”中构建的实体框架获得更好的结果。 有人知道为什么吗?

这是Entity Framework方法的代码:

public virtual T Find<T>(int id)
{
    return dbContext.Set<T>().Find(id);
}

这是我的方法的代码:

public virtual T FindById<T>(int id)
{
    return dbContext.Set<T>().Where(x => x.IsActive).AsQueryable().FirstOrDefault(x => x.Id == id);
}

这是从数据库中选择一些记录所需的时间:

基准测试

这是我的测试课:

[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);
    }
}

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

性能测试有几处错误。

  1. Pawel的回答是正确的,即EF首次运行时,会发生大量开销。 第一个EF查询花费的时间比其他查询长得多。

  2. 您没有测试相同的查询。 查找“使用给定的主键值查找实体”( https://msdn.microsoft.com/zh-cn/library/gg696418(v=vs.113).aspx )。 我假设您的IsActive标志不是主键的一部分,因此上述方法将产生不同的SQL语句。 您无法使用不同的SQL语句比较两种方法的性能。

    例如:dbContext.Set()。Find(id)会产生类似以下内容:

    选择*从Foo WHERE FooID = 123

    另一方面,dbContext.Set()。Where(f => f.IsActive).FirstOrDefault(f => f.FooID == id)会产生类似以下内容:

    SELECT * FROM Foo,FooID = 123 AND IsActive = 1

    根据SQL使用什么索引以及SQL Server决定建立哪种执行计划,这本身可以产生截然不同的结果。 如果要比较苹果与苹果,则应进行比较:

    dbContext.Set()求(123)。

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

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

如果要显式测试检索相同数据的不同方法的性能,则需要确保这些方法在做的事情完全相同。 最好的保证方法是运行SQL事件探查器,并确保所生成的基础SQL是相同的。 否则,您正在测试的是SQL Server是否可以生成两个不同的数据库查询,而这些查询所花费的时间比彼此执行的时间长或短,这是没有意义的。

另外,请勿在查询中调用无关的扩展方法。 在这种情况下,AsQueryable是无害的,但是调用错误的扩展方法也会使结果集具体化,将每个实体都带回到内存中,并对内存中的集合运行另一个查询。 这也会对性能产生巨大影响。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM