4

I'm using .NET entity framework 4.1 with code-first approach to effectively solve the following problem, here simplified.

  • There's a database table with tens of thousands of entries.
  • Several users of my program need to be able to
    • View the (entire) table in a GridRow, which implied that the entire Table has to be downloaded.
    • Modify values of any random row, changes are frequent but need not be persisted immediately. It's expected that different users will modify different rows, but this is not always true. Some loss of changes is permitted, as users will most likely update same rows to same values.
    • On occasion add new rows.

Sounds simple enough. My initial approach was to use a long-running DbContext instance. This one DbContext was supposed to track changes to the entities, so that when SaveChanges() is called, most of the legwork is done automatically. However many have pointed out that this is not an optimal solution in the long run, notably here. I'm still not sure if I understand the reasons, and I don't see what a unit-of-work is in my scenario either. The user chooses herself when to persist changes, and let's say that client always wins for simplicity. It's also important to note that objects that have not been touched don't overwrite any data in the database.

Another approach would be to track changes manually or use objects that track changes for me, however I'm not too familiar with such techniques, and I would welcome a nudge in the right direction.

What's the correct way to solve this problem?

I understand that this question is a bit wishy-washy, but think of it as more fundamental. I lack fundamental understanding about how to solve this class of problems. It seems to me that long living DbContext is the right way, but knowledgeable people tell me otherwise, which leads me to confusion and imprecise questions.

EDIT1 Another point of confusion is the existance of Local property on the DbSet<> object. It invites me to use a long running context, as another user has posted here.

Community
  • 1
  • 1
Gleno
  • 15,466
  • 11
  • 59
  • 81

2 Answers2

3

Problem with long running context is that it doesn't refresh data - I more discussed problems here. So if your user opens the list and modify data half an hour she doesn't know about changes. But in case of WPF if your business action is:

  • Open the list
  • Do as many actions as you want
  • Trigger saving changes

Then this whole is unit of work and you can use single context instance for that. If you have scenario where last edit wins you should not have problems with this until somebody else deletes record which current user edits. Additionally after saving or cancelling changes you should dispose current context and load data again - this will ensure that you really have fresh data for next unit of work.

Context offers some features to refresh data but it only refreshes data previously loaded (without relations) so for example new unsaved records will be still included.

Perhaps you can also read about MS Sync framework and local data cache.

Community
  • 1
  • 1
Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • Actually you have already provided me with means to refresh context which seems to work fine in my scenario. But I'd rather do it cleanly as you suggest. Thanks again. – Gleno May 15 '11 at 14:49
  • could you weigh in on the discussion in the answer below? I would really appreciate some insight into what's really happening. I don't know why I'm so sure, and that makes me question everything. :) – Gleno May 23 '11 at 09:39
1

Sounds to me like your users could have a copy (cached) of the data for an indefinate period of time. The longer the users are using cached data the greater the odds that they could become disconnected from the database connection in DbContext. My guess is EF doesn't handle this well and you probably want to deal with that. (e.g. occaisionally connected architecture). I would expect implementing that may solve many of your issues.

Peter Ritchie
  • 33,368
  • 9
  • 74
  • 96
  • I don't think you are right on this one. In fact it's a myth of sorts. Any query through DbContext can have a connection failure, and retry logic is always needed to verify that this didn't happen. But it *will* get a new connection if you retry your operation. – Gleno May 15 '11 at 14:47
  • Myth? Just tried it, created a context, got a row, caused SQL server to disconnect and got an exception in SaveChanges. – Peter Ritchie May 15 '11 at 15:16
  • And what happens if you do SaveChanges() again? – Gleno May 15 '11 at 15:50
  • Well, you are supposed to handle that exception. The exception is just a dud connection. Next time you do SaveChanges(), the context would have magicked up a new one. This is to the best of my understanding of recommended guidelines. – Gleno May 22 '11 at 07:58
  • If that is indeed true, and "best of [your] undestanding" doesn't mean you are guessing; please post your source. Otherwise, this is a bit silly. EntityException is the only exception throw--which offers no good way to decide if the exception was indeed due to a disconnection and whether to decide if "retrying" wouldn't just send the application into an endless loop. – Peter Ritchie May 23 '11 at 05:54
  • My source is "Transient Fault Handling Framework for SQL Azure" (1) and associated documentation, and the reason I call it myth, is because I've seen it mentioned and debunked on stackoverflow. From the (1) source code, it's apparent that the EntityException is expected to have a SqlException as InnerException, which has a Number property. Errors No. 40501, 10053 and 10054 seem to indicate faulty connection. It's expected of developers to retry operation on those errors, but they happen so seldom that nobody bothers. Although some errors like "server busy" happen more frequently on Azure. – Gleno May 23 '11 at 08:01
  • Ehm, I've just run a few tests. It seems to work fine, although for some reason I generated error No. 121, instead of those mentioned in the Transient Fault Handling Framework. Oh well, best to put it in... :) – Gleno May 23 '11 at 09:05
  • Hmm, wasn't aware we switched to SQL Azure; but that does bring up an excellent point. At best your recomendation is fragile. Expects an inner exception of specific type with properties of specific values. At worst it couples my code to a specific type of database. One of the reasons for using EF is to decouple an application from the database more. I really doubt as more databases are supported by EF that disconnections in those will have inner exceptions of type SqlClient.SqlException... – Peter Ritchie May 23 '11 at 14:43
  • My "point" was that SaveChanges() can faulter, and it's expected of the developer to retry that operation. I'm going to pose the question, Ladislav will settle it for us. :) – Gleno May 23 '11 at 18:04