31

I am in the process of upgrading an application from EF1 to EF4.1 I created a DbContext and a set of POCOs using the "ADO.NET DbContext Generator" templates.

When I query the generated DbContext the database part of the query takes 4ms to execute (validated with EF Profiler). And then it takes the context about 40 seconds (in words: FORTY!) to do whatever it does before it returns the result to the application.

EF1 handles the same query in less than 2 seconds.

Turning off AutoDetectChanges, LazyLoading and ProxyGeneration wins me 2-3 seconds.

When I use the AsNoTracking() extension method I am able to reduce the total execution time to about 3 seconds.

That indicates that ChangeTracking is the culprit.

But ChangeTracking is what I need. I must be able to eventually persist all changes without having to handpick which entities were modified.

Any ideas how I could solve that performance issue?

cb1295
  • 733
  • 4
  • 18
  • 36
Sebastian Weber
  • 6,766
  • 2
  • 30
  • 49
  • 1
    It was discussed here several times. It looks like a bug in EFv4.1 – Ladislav Mrnka May 12 '11 at 07:46
  • It's even worse when I use EF4.0 and the "ADO.NET POCO Entity Generator" template. Then it takes over 80 seconds until I have some result. So the bug was even more severe with a previous version and MS not able to fix it properly in EF4.1? – Sebastian Weber May 12 '11 at 08:05
  • 3
    You can try to turn off `AutoDetectChanges` / use `AsNoTracking` but in the same time create tracking proxies (all properties must be virtual). I wonder if this track changes or not and I cannot test it myself now. – Ladislav Mrnka May 12 '11 at 08:12
  • You cannot even run the query. You get an exception that tells you a property cannot be set because it is already assigned a value of type EntityCollection as soon as I modify the T4 template to make every property virtual. – Sebastian Weber May 12 '11 at 08:34
  • How did you create entities? This error says that you are trying to set collection to navigation property which is already initialized by `EntityCollection`. – Ladislav Mrnka May 12 '11 at 08:36
  • As I said: I modified the T4 template for the model to make every public property virtual. Thats it. Nothing more. The error occurs in the constructor of the generated entity where it tries to initialize the navigation properties with HashSets. My guess is that the proxy already set those properties to something else. But I don't know why making primitive properties virtual interferes with the navigation properties. – Sebastian Weber May 12 '11 at 08:52
  • 1
    I see that you have also asked this question on MSDN forum. Add details that it performs also bad when using EFv4 with POCOs. – Ladislav Mrnka May 12 '11 at 08:59
  • Modifying the T4 template so that the ctor only initializes the navigation properties when they are still NULL solved the problem with the exception. But the ChangeTracking does not work and SaveChanges() would not persist any changes. – Sebastian Weber May 12 '11 at 09:02
  • how large and complexe is your result set ? – Alexandre Brisebois Jun 08 '11 at 17:13
  • Marked all properties as virtual? – BennyM Jul 18 '11 at 08:21
  • I hope your interface to EF4 (or specifically your data repository) has been abstracted into one project with the actual implementation in another project. I am experiencing the same issues and am oh so close to switching to NHibernate 3. – 37Stars Aug 29 '11 at 22:26
  • Is this query executed in a brand new context? – ladenedge Aug 31 '11 at 18:44
  • 1
    @Programmers having a problem with their navigation properties made virtual trying having ProxyGeneration = true: Don't instantiate and assign any collection. The proxy generation will take care of that. However, to get your proxy-object you need to create it with DbSet.Create. Simply instantiating with "new" will not suffice. – Alex Maker Sep 01 '11 at 17:13

2 Answers2

1

Is the technique at the end of this documentation useful? Alternatively, I've avoided many of the performance pitfalls using a fluent interface to declaratively state which entities in a given transaction for sure won't change vs. might change (immutable vs. immutable). For example, if the entities I am saving are aggregate roots in which the root or its entities refer to "refdata" items, then this heuristic prevents many writes because the immutable items don't need to be tracked. The mutable items all get written without check (a weakness... One which may or may not be acceptable).

I'm using this with a generic repository pattern precisely because I don't want to track changes or implement a specific strategy for each case. If that's not enough, perhaps rolling your own change tracking outside of the context and adding entities in as needed will work.

Kit
  • 20,354
  • 4
  • 60
  • 103
0

Without seeing the query, I can't say for sure what the problem might be. Could this be related?

Why does the Contains() operator degrade Entity Framework's performance so dramatically?

Depending on the LINQ operators being used, it appears that EF has a tough time converting some queries to SQL. Maybe you're running up against a similar situation here.

Community
  • 1
  • 1
Mike
  • 7,500
  • 8
  • 44
  • 62