And*_*rey 5 .net entity-framework
我们有两个实体框架查询,一个有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();
Run Code Online (Sandbox Code Playgroud)
一个与第一个具有相同性能的案例(Query2)
var scope1 = model.Scopes
.Where(s => (s.Level == intLevel && s.Name == name))
.First();
scope1.Settings.Load();
Run Code Online (Sandbox Code Playgroud)
第一个运行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
Run Code Online (Sandbox Code Playgroud)
第二:
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
Run Code Online (Sandbox Code Playgroud)
第三:
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
Run Code Online (Sandbox Code Playgroud)
编辑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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
编辑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();
}
}
Run Code Online (Sandbox Code Playgroud)
结果是:
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
Run Code Online (Sandbox Code Playgroud)
所以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
Run Code Online (Sandbox Code Playgroud)
当您说实际的 TSQL 查询运行得很快时,您是在谈论手动编码的查询吗?
尝试使用 SQL Profiler 查看 EF 3.5 生成的内容。也许这将说明为什么性能会如此不同,并提供一些关于是否以及如何可以提高第一个查询的性能的见解。
另外,这里有几篇博客文章提供了如何在 EF 4 中改进 SQL 生成的具体示例。即使升级到 EF 4 不是一个选择,它们也可能提供一些值得深思的内容。
编辑
这是我用来尝试重现您的结果的代码。这是使用 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
Run Code Online (Sandbox Code Playgroud)
使用控制台应用程序进行测试:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)
对于第二个查询:
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
Run Code Online (Sandbox Code Playgroud)
和输出:
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1165 次 |
| 最近记录: |