I have a performance issue with Entity Framework 5 and Oracle DB.
I have a simple SQL select: SELECT * FROM NOTE WHERE NOTENUMBER = '1A23456'
NOTENUMBER is included in an index on a table called NOTE, but the field is NOT primary key / unique.
-
When I execute the statement with Oracle SQL Developer, results return
quickly and query plan shows that RANGE SCAN is being used like it
should. -
When I use Entity Framework, the generated SQL takes a lot
longer (5 seconds vs. 30ms). -
When I use Entity Framework and query
with a primary key field (NOTE_KEY), results return as quickly as with SQL
Developer.
I suspect 2 things:
-
There’s some problem with EF and Oracle.DataAccess-provider not using the non-unique-index that is available. It would help if I had debug symbols for Entity Framework 5, but I can’t find them anywhere.
-
The performance problem is somewhere in EF, regarding closures and/or the way I use generic repository pattern with EF:
If I call my repository like this:
var notenumber = "1A23456";
var notes = repository.All(n => n.NOTENUMBER == notenumber).ToList();
The predicate comes in at the methodAllas:
{n => (n.NOTE == value(Tester.Program+<>c__DisplayClass0).notenumber)}
And EfProf-profiler traces the resulting SQL as: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 */)And the query takes takes ~5500ms.
On the other hand, if I call my repository like this:
var notes = repository.All(n => n.NOTENUMBER == "1A23456").ToList();
Then the predicate comes in as:
{n => (n.NOTENUMBER == "1A23456")}
And EfProf-profiler traces the resulting SQL as:SELECT "Extent1"."NOTE_KEY" AS "NOTE_KEY",
"Extent1"."NOTENUMBER" AS "NOTENUMBER",
"Extent1"."NOTETEXT" AS "NOTETEXT",
FROM "NOTE_DBA"."NOTE" "Extent1"
WHERE ('1PSA0500237500' = "Extent1"."NOTENUMBER")And the query takes ~30ms.
So the only difference is the order of the condition in the WHERE-clause, and the fact that in the latter there seems to be no parameter replaced by EF
I use VS2010 and .NET4, and reference EF5 (v4.4.0.0).
The repository’s All-method is:
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;
}
I tried to create a CompiledQuery, I tried using setOfNotes.AsNoTracking() and I tried to target .NET 4.5 – with no difference in performance.
One way I was able to get this particular query fast, was to use Oracle’s basic Data Provider for .NET (ODB.NET) and construct the query manually, but I’d rather not stick with that solution. Again, if I use a primary field in the where clause, the query is fast even with EF and the same All-method.
So the problem seems to be somewhere in EF. I feel could find out a lot more if I only had the symbols for EntityFramework.dll.
Could there be a problem with the way EF invokes predicates? How does the ‘@p_linq_0′-parameter get replaced inside EF?
I had a similar problem. The reason why the index wasn’t used in my case was that I was passing a string (unicode) from .NET as parameter. This was compared with a non-unicode database field.
The solution was to convert the string parameter to non-unicode before passing it in the where clause: