我遇到了 Entity Framework 5 和 Oracle DB 的性能问题。
我有一个简单的 SQL 选择:SELECT * FROM NOTE WHERE NOTENUMBER = '1A23456'
NOTENUMBER
包含在名为 NOTE 的表的索引中,但该字段不是主键/唯一的。
当我使用 Oracle SQL Developer 执行该语句时,结果会快速返回,并且查询计划显示正在使用 RANGE SCAN。
当我使用实体框架时,生成的 SQL 需要更长的时间(5 秒对 30 毫秒)。
当我使用 Entity Framework 并使用主键字段(NOTE_KEY) 进行查询时,结果返回的速度与使用 SQL Developer 一样快。
我怀疑两件事:
EF 和 Oracle.DataAccess-provider 没有使用可用的非唯一索引存在一些问题。如果我有 Entity Framework 5 的调试符号会有所帮助,但我在任何地方都找不到它们。
性能问题存在于 EF 中,关于闭包和/或我在 EF 中使用通用存储库模式的方式:
如果我这样调用我的存储库:
var notenumber = "1A23456";
var notes = repository.All(n => n.NOTENUMBER == notenumber).ToList();
谓词以如下方式出现在方法All
中:
{n => (n.NOTE == value(Tester.Program+<>c__DisplayClass0).notenumber)}
并且 EfProf-profiler 将生成的 SQL 跟踪为:SELECT "Extent1"."NOTE_KEY" AS "NOTE_KEY",
"Extent1"."NOTENUMBER" AS "NOTENUMBER",
"Extent1"."NOTETEXT" AS "NOTETEXT",
FROM "NOTE_DBA"."NOTE" "Extent1"
WHERE ("Extent1"."NOTENUMBER" = '1PSA0500237500' /* @p__linq__0 */)
查询需要~5500ms。
另一方面,如果我这样调用我的存储库:
var notes = repository.All(n => n.NOTENUMBER == "1A23456").ToList();
那么谓词如下:
{n => (n.NOTENUMBER == "1A23456")}
并且 EfProf-profiler 将生成的 SQL 跟踪为:SELECT "Extent1"."NOTE_KEY" AS "NOTE_KEY",
"Extent1"."NOTENUMBER" AS "NOTENUMBER",
"Extent1"."NOTETEXT" AS "NOTETEXT",
FROM "NOTE_DBA"."NOTE" "Extent1"
WHERE ('1PSA0500237500' = "Extent1"."NOTENUMBER")
查询需要~30ms。
所以唯一的区别是 WHERE 子句中条件的顺序,而在后者中似乎没有参数被 EF 替换
我使用 VS2010 和 .NET4,并参考 EF5 (v4.4.0.0)。存储库的 All-method 是:
public IQueryable<NOTE> All(Expression<Func<NOTE, bool>> predicate = null)
{
var setOfNotes = GetDbSet<NOTE>();
var notesQuery = from note in setOfNotes select note;
if (predicate != null)
{
notesQuery = notesQuery.Where(predicate);
}
return notesQuery;
}
我尝试创建一个 CompiledQuery,尝试使用setOfNotes.AsNoTracking()
并尝试以 .NET 4.5 为目标 - 性能没有差异。
我能够快速获得此特定查询的一种方法是使用 Oracle 的基本 Data Provider for .NET (ODB.NET) 并手动构建查询,但我宁愿不坚持使用该解决方案。同样,如果我在 where 子句中使用主字段,即使使用 EF 和相同的 All-method,查询也会很快。
所以问题似乎出在EF的某个地方。如果我只有 EntityFramework.dll 的符号,我觉得可以找到更多信息。
EF 调用谓词的方式会不会有问题?'@p_ linq _0'参数如何在 EF 中被替换?