0

我在使用通用方法进行数据聚合时遇到了实体框架的性能问题。当使用通用方法查询具有几十万行的表的(索引)Id 列的最大值时,性能下降很大。我正在使用代码生成的 int 键而不是 sql 标识,此代码用于获取下一个新 Id。

这是示例插图。MaxTyped使用Maxon db.Posts(即DbSet<Post>),而MaxGeneric使用泛型方法max来做同样的事情。

static int MaxTyped()
{
    using (var db = new BloggingContext())
    {                    
        return db.Posts.Max(p => p.PostId);
    }
}

static int MaxGeneric()
{
    using (var db = new BloggingContext())
    {
        return max(db.Posts, p => p.PostId);
    }
}

static int max<T>(DbSet<T> set, Func<T, int> func) where T : class
{
    // intellisense says its IEnumerable.Max
    return set.Max(func);
}

在我的不太旧的家用桌面上,硬盘速度很快,MaxTyped100k 行的运行时间为 0.5 秒,而MaxGeneric大约 6.5 秒。这慢了一个数量级。

在我办公室的旧测试服务器上,我们有几秒钟的时间而不是近 10 分钟。

我发现的问题的唯一痕迹是两种情况下 Intellisense 对 Max 方法的输出的差异:在MaxTyped它将方法标识为IQueryable.Max时,在它使用的 max 中MaxGeneric表示它IEnumerable.Max,这可能意味着 EF 在所有加载的实体上执行 Max 和不在数据库中。我尝试转换setIQueryable<T>,但没有任何改变。为什么会有这样的差异?如何解决?

4

1 回答 1

3

因为在MaxTyped“服务器端”执行,所以只返回一行,而MaxGeneric在“客户端”执行,所以返回表的所有行,“反序列化”到int“max(ed)”(最后一个一步是最快的)

真正的成本在于 SQL Server 和 .NET 应用程序之间的数据传递:必须传递的数据越少越好。

现在试试

static int max<T>(DbSet<T> set, Expression<Func<T, int>> func) where T : class
{
    // intellisense says its IEnumerable.Max
    return set.Max(func);
}

并查看是否set.Max使用IEnumerable<T>.Max()IQueriable<T>.Max()。如果它使用第二个,它的速度应该在 0.5 秒左右。

不同之处在于,IQueriable<T>.Max()仅接受Expression<Func<T, int>>,而IEnumerable<T>.Max()接受Func<T, int>,并且当您编写时,p => p.PostId它可以是 anExpression<Func<>>或 a ,Func<>具体取决于它放入的变量的类型。

于 2013-08-15T16:29:04.567 回答