我们有两个实体框架查询,一个Include
一个查询独立查询。他们在这里
ConfigModelContainer model = new ConfigModelContainer();
var scope = model.Scopes.Include("Settings")
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
ConfigModelContainer model = new ConfigModelContainer();
var scope = model.Scopes
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
var settings = model.Settings.Where(s => s.Scope.Id == scope.Id).ToList();
另一个与第一个具有相同性能的案例(Query2)
var scope1 = model.Scopes
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
scope1.Settings.Load();
第一个运行30秒,第二个运行亚秒。这太奇怪了,我没有想法。
有谁知道为什么会这样?
编辑:实际的TSQL查询运行速度非常快(亚秒级)
编辑2 :以下是查询:
首先:
SELECT
[Project2].[Level] AS [Level],
[Project2].[Id] AS [Id],
[Project2].[Name] AS [Name],
[Project2].[ParentScope_Id] AS [ParentScope_Id],
[Project2].[C1] AS [C1],
[Project2].[Id1] AS [Id1],
[Project2].[Type] AS [Type],
[Project2].[Value] AS [Value],
[Project2].[Scope_Id] AS [Scope_Id]
FROM ( SELECT
[Limit1].[Id] AS [Id],
[Limit1].[Name] AS [Name],
[Limit1].[Level] AS [Level],
[Limit1].[ParentScope_Id] AS [ParentScope_Id],
[Extent2].[Id] AS [Id1],
[Extent2].[Type] AS [Type],
[Extent2].[Value] AS [Value],
[Extent2].[Scope_Id] AS [Scope_Id],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM (SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Level] AS [Level],
[Extent1].[ParentScope_Id] AS [ParentScope_Id]
FROM [dbo].[Scopes] AS [Extent1]
WHERE ([Extent1].[Level] = @p__linq__0) AND ([Extent1].[Name] = @p__linq__1) ) AS [Limit1]
LEFT OUTER JOIN [dbo].[Settings] AS [Extent2] ON [Limit1].[Id] = [Extent2].[Scope_Id]
) AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC
第二
SELECT
[Limit1].[Level] AS [Level],
[Limit1].[Id] AS [Id],
[Limit1].[Name] AS [Name],
[Limit1].[ParentScope_Id] AS [ParentScope_Id]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Level] AS [Level],
[Extent1].[ParentScope_Id] AS [ParentScope_Id]
FROM [dbo].[Scopes] AS [Extent1]
WHERE ([Extent1].[Level] = @p__linq__0) AND ([Extent1].[Name] = @p__linq__1)
) AS [Limit1]
SELECT
1 AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Type] AS [Type],
[Extent1].[Value] AS [Value],
[Extent1].[Scope_Id] AS [Scope_Id]
FROM [dbo].[Settings] AS [Extent1]
WHERE [Extent1].[Scope_Id] = @EntityKeyValue1
第三
SELECT
[Limit1].[Level] AS [Level],
[Limit1].[Id] AS [Id],
[Limit1].[Name] AS [Name],
[Limit1].[ParentScope_Id] AS [ParentScope_Id]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Level] AS [Level],
[Extent1].[ParentScope_Id] AS [ParentScope_Id]
FROM [dbo].[Scopes] AS [Extent1]
WHERE ([Extent1].[Level] = @p__linq__0) AND ([Extent1].[Name] = @p__linq__1)
) AS [Limit1]
SELECT
1 AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Type] AS [Type],
[Extent1].[Value] AS [Value],
[Extent1].[Scope_Id] AS [Scope_Id]
FROM [dbo].[Settings] AS [Extent1]
WHERE [Extent1].[Scope_Id] = @p__linq__0
编辑3 :
我无法在同一台机器上继续测试。这是更快的机器上的结果。这是代码和结果:
static void Main(string[] args)
{
int intLevel = 2;
string name = "fb226050-4f92-4fca-9442-f76565b33877";
Stopwatch sw = new Stopwatch();
using (CMEntities model = new CMEntities())
{
sw.Start();
for (int i = 0; i < 5; i++)
{
var scope1 = model.Scopes.Include("Settings")
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
Console.WriteLine("Query:1, Iter:{0}, Time:{1}", i, sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
}
}
Console.WriteLine();
using (CMEntities model = new CMEntities())
{
sw.Start();
for (int i = 0; i < 5; i++)
{
var scope1 = model.Scopes
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
scope1.Settings.Load();
Console.WriteLine("Query:2, Iter:{0}, Time:{1}", i, sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
}
}
Console.WriteLine();
using (CMEntities model = new CMEntities())
{
for (int i = 0; i < 5; i++)
{
var scope = model.Scopes
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
var settings = model.Settings.Where(s => s.Scope.Id == scope.Id).ToList();
Console.WriteLine("Query:3, Iter:{0}, Time:{1}", i, sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
}
}
}
}
结果:
Query:1, Iter:0, Time:2477
Query:1, Iter:1, Time:1831
Query:1, Iter:2, Time:1933
Query:1, Iter:3, Time:1774
Query:1, Iter:4, Time:1949
Query:2, Iter:0, Time:2036
Query:2, Iter:1, Time:1870
Query:2, Iter:2, Time:1921
Query:2, Iter:3, Time:1751
Query:2, Iter:4, Time:1758
Query:3, Iter:0, Time:188
Query:3, Iter:1, Time:201
Query:3, Iter:2, Time:185
Query:3, Iter:3, Time:203
Query:3, Iter:4, Time:217
编辑4 :我使用NHibernate重写了代码:
static void Main(string[] args)
{
var cfg = new StoreConfiguration();
var sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.ConnectionString("Data Source=.;Initial Catalog=CM;Integrated Security=True;MultipleActiveResultSets=True")
)
.Mappings(m =>
m.AutoMappings.Add(
AutoMap.AssemblyOf<Entities.Scope>(cfg)
.Conventions
.Add(
Table.Is(x => x.EntityType.Name + "s"),
PrimaryKey.Name.Is(x => "Id"),
ForeignKey.EndsWith("_id")
)
)
)
.BuildSessionFactory();
Stopwatch sw = new Stopwatch();
for (int i = 0; i < 5; i++)
{
sw.Start();
var session = sessionFactory.OpenSession();
int intLevel = 2;
string name = "fb226050-4f92-4fca-9442-f76565b33877";
var scope = session.CreateCriteria<Entities.Scope>()
.SetFetchMode("Settings", FetchMode.Eager)
.Add(Restrictions.Eq("Name", name))
.Add(Restrictions.Eq("Level", intLevel))
.UniqueResult<Entities.Scope>();
Console.WriteLine("Query:0, Iter:{0}, Time:{1}", i, sw.ElapsedMilliseconds);
sw.Reset();
}
}
结果是:
Query:0, Iter:0, Time:446
Query:0, Iter:1, Time:223
Query:0, Iter:2, Time:303
Query:0, Iter:3, Time:275
Query:0, Iter:4, Time:284
所以NHibernate形成适当的收集速度比EF快10倍。这真的很难过。
以下是NHibernate生成的查询:
SELECT this_.id AS id0_1_,
this_.name AS name0_1_,
this_.LEVEL AS level0_1_,
settings2_.scope_id AS scope4_3_,
settings2_.id AS id3_,
settings2_.id AS id1_0_,
settings2_.TYPE AS type1_0_,
settings2_.VALUE AS value1_0_,
settings2_.scope_id AS scope4_1_0_
FROM scopes this_
LEFT OUTER JOIN settings settings2_
ON this_.id = settings2_.scope_id
WHERE this_.name = @p0
AND this_.LEVEL = @p1
答案 0 :(得分:1)
要检查几件事......
ConfigModelContainer model = new ConfigModelContainer();
var scope = model.Scopes.Include("Settings")
.First<Scope>(s => s.Level == intLevel && s.Name == name);
我不确定Linq会以不同于您的查询来优化此查询,但可能会看到它是否恰好这样做。
关于升级到EF 4.0,我不知道如果你的项目在.NET 3.5中是多么可行。然而,当我们的项目使用.NET 3.5 / EF 1时,我从未遇到过这个问题。
答案 1 :(得分:1)
当你说实际的TSQL查询运行得很快时,你是在谈论手工编码的查询吗?
尝试使用SQL事件探查器查看EF 3.5生成的内容。也许这将说明为什么性能会如此不同,并提供一些洞察力,以了解是否以及如何提高第一个查询的性能。
此外,这里有一些博客文章提供了如何在EF 4中改进sql生成的具体示例。即使升级到EF 4不是一种选择,它们也可能提供深思熟虑。
Improvements to the Generated SQL in .NET 4.0 Beta1
Improvements to Generated SQL in .NET 4.0
修改强>
以下是我用来尝试重现结果的代码。这是使用SQL Server 2008 R2,VS 2010(无SP1)和Entity Framework 4.0。我不得不猜测架构;希望它很接近。
创建表格并填充它们:
set nocount on
create table Scopes
(
[Id] int identity primary key,
[Level] int,
[Name] nvarchar(50),
[ParentScope_Id] int foreign key references Scopes(Id)
)
create table Settings
(
[Id] int identity primary key,
[Type] nvarchar(20),
[Value] nvarchar(50),
[Scope_Id] int foreign key references Scopes(Id)
)
go
declare @scopeId int,
@scopeCount int,
@settingCount int,
@value nvarchar(50)
set @scopeCount = 0
while @scopeCount < 10
begin
insert into Scopes([Level], [Name]) values(1, 'Scope ' + cast(@scopeCount as nvarchar))
select @scopeId = @@IDENTITY
set @settingCount = 0
while @settingCount < 10000
begin
set @value = 'Setting ' + cast(@scopeId as nvarchar) + '.' + cast(@settingCount as nvarchar)
insert into Settings([Type], [Value], [Scope_Id]) values ('Test', @value, @scopeId)
set @settingCount = @settingCount + 1
end
set @scopeCount = @scopeCount + 1
end
使用控制台应用程序进行测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace so_q5205281
{
class Program
{
static void Main(string[] args)
{
using (var context = new EFTestEntities())
{
int level = 1;
string name = "Scope 4";
ExecQuery1(context, level, name);
ExecQuery1(context, level, name);
ExecQuery1(context, level, name);
ExecQuery2(context, level, name);
ExecQuery2(context, level, name);
ExecQuery2(context, level, name);
}
Console.ReadLine();
}
static void ExecQuery1(EFTestEntities context, int level, string name)
{
Stopwatch stopwatch = Stopwatch.StartNew();
var scope = context.Scopes.Include("Settings")
.Where(s => s.Level == level && s.Name == name)
.First();
int settingsCount = scope.Settings.Count();
stopwatch.Stop();
Console.WriteLine("Query 1, scope name: {0}, settings count: {1}, seconds {2}", scope.Name, settingsCount, stopwatch.Elapsed.TotalSeconds);
}
static void ExecQuery2(EFTestEntities context, int level, string name)
{
Stopwatch stopwatch = Stopwatch.StartNew();
var scope = context.Scopes
.Where(s => s.Level == level && s.Name == name)
.First();
var settings = context.Settings.Where(s => s.Scope.Id == scope.Id).ToList();
int settingsCount = scope.Settings.Count();
stopwatch.Stop();
Console.WriteLine("Query 2, scope name: {0}, settings count: {1}, seconds {2}", scope.Name, settingsCount, stopwatch.Elapsed.TotalSeconds);
}
}
}
使用默认设置创建EF模型,并从数据库更新模型:
从EF发送的第一个查询的SQL:
exec sp_executesql N'SELECT
[Project2].[Id] AS [Id],
[Project2].[Level] AS [Level],
[Project2].[Name] AS [Name],
[Project2].[ParentScope_Id] AS [ParentScope_Id],
[Project2].[C1] AS [C1],
[Project2].[Id1] AS [Id1],
[Project2].[Type] AS [Type],
[Project2].[Value] AS [Value],
[Project2].[Scope_Id] AS [Scope_Id]
FROM ( SELECT
[Limit1].[Id] AS [Id],
[Limit1].[Level] AS [Level],
[Limit1].[Name] AS [Name],
[Limit1].[ParentScope_Id] AS [ParentScope_Id],
[Extent2].[Id] AS [Id1],
[Extent2].[Type] AS [Type],
[Extent2].[Value] AS [Value],
[Extent2].[Scope_Id] AS [Scope_Id],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM (SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Level] AS [Level],
[Extent1].[Name] AS [Name],
[Extent1].[ParentScope_Id] AS [ParentScope_Id]
FROM [dbo].[Scopes] AS [Extent1]
WHERE ([Extent1].[Level] = @p__linq__0) AND ([Extent1].[Name] = @p__linq__1) ) AS [Limit1]
LEFT OUTER JOIN [dbo].[Settings] AS [Extent2] ON [Limit1].[Id] = [Extent2].[Scope_Id]
) AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC',N'@p__linq__0 int,@p__linq__1 nvarchar(4000)',@p__linq__0=1,@p__linq__1=N'Scope 4'
和第二个查询:
exec sp_executesql N'SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Level] AS [Level],
[Extent1].[Name] AS [Name],
[Extent1].[ParentScope_Id] AS [ParentScope_Id]
FROM [dbo].[Scopes] AS [Extent1]
WHERE ([Extent1].[Level] = @p__linq__0) AND ([Extent1].[Name] = @p__linq__1)',N'@p__linq__0 int,@p__linq__1 nvarchar(4000)',@p__linq__0=1,@p__linq__1=N'Scope 4'
exec sp_executesql N'SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Type] AS [Type],
[Extent1].[Value] AS [Value],
[Extent1].[Scope_Id] AS [Scope_Id]
FROM [dbo].[Settings] AS [Extent1]
WHERE [Extent1].[Scope_Id] = @p__linq__0',N'@p__linq__0 int',@p__linq__0=5
和输出:
Query 1, scope name: Scope 4, settings count: 10000, seconds 0.6657546
Query 1, scope name: Scope 4, settings count: 10000, seconds 0.1608498
Query 1, scope name: Scope 4, settings count: 10000, seconds 0.1097625
Query 2, scope name: Scope 4, settings count: 10000, seconds 0.0742593
Query 2, scope name: Scope 4, settings count: 10000, seconds 0.0551458
Query 2, scope name: Scope 4, settings count: 10000, seconds 0.0555465
答案 2 :(得分:0)
您可以打印以下结果吗?
ConfigModelContainer model = new ConfigModelContainer();
ObjectQuery<Scope> scope = model.Scopes.Include("Settings")
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
Trace.WriteLine(scope.ToTraceString());
看看它在两种情况下打印的内容。
如果你交换查询的位置,第二个运行更快还是更慢?如果这是程序的开始,那么每当您运行第一个查询时,EF将花费时间来获取元数据,验证元数据并安定下来。如果你说简单查询运行得更快,我认为它的EF初始化时间。
答案 3 :(得分:0)
我会计算两个EF生成的查询来检查它们的确切运行时间。也许你会发现一些不同。
第一个查询是有序(请参阅EF生成的查询),这应该使它比第二个querhy(不<<)慢运行 / strong>已订购)。如果您的设置很大,那么您可能正在查看这种排序的运行时间。
我还观察到,次序运行(第一次除外)比第一次迭代更快。这是预期的,因为后续运行不必实例化所有对象 - 它们都是在第一次运行时创建的。但差别很小,因此您的性能瓶颈显然不是对象实例化。
在尝试第二次查询之前,您还要处理实体上下文。
因此唯一的区别(第一个查询的排序除外)是在第一个查询中,您复制了与“设置”连接的每个记录中“范围”中的四个字段。因此,您有n个重复的四个范围字段,其中n =设置数。由于这四个字段似乎很小,这不应该产生您测量的10倍性能差异,尽管10倍差异仅为2秒。
您对该特定范围行有多少设置?