Search code examples
c#asp.net-mvcnhibernateexceptionoptimistic-locking

Where to handle StaleObjectStateException


Should I wrap calls to a repository with try-catch block (aiming to catch/handle StaleObjectStateException) inside a corresponding controller in ASP.NET MVC application or should it take place inside a repository implementation?

Also how do I handle the exception, inform user. As far as I understand no rollback is intented?

Thanks!


Solution

  • The problems boils down to a different question: where and how to handle concurrent modification of entities? That is: user A and user B edit the same record and when the one that saves the record later (user B) gets a StaleObjectStateException because the version he edited is out of date now.

    Here are some ideas:

    1. Forcefully make the version of user B the "right" one by brute force, e.g. by retrieving the current version of the record from the DB and apply the whole state of user B's version to it. This is problematic if user A has changed e.g. the "E-Mail address" field and user B has changed the "User name" field. With this approach everything user A has done is gone. In this approach you'd catch StaleObjectStateException and fix everything inside the repository.

    2. The "smart" approach: similarly to approach 1 everything is fixed inside the repository (ie. it catches and handles StaleObjectStateException completely), but it uses domain knowledge to selectively apply only some of the changes user B did. E.g. if user A changed the e-mail address and user B changed the user name, those changes don't exclude each other, so the repository could only update the e-mail address. This works well if two aspects of the record have been changed concurrently that do not directly rely on each other. Implementing this solution can be rather complex depending on how "smart" you want to be.

    3. Reject concurrent changes inside the repository. In this case, if the StaleObjectStateException occurs the repository needs to report back that it couldn't save the record. It could actually just let the exception bubble up, but then you're leaking NHibernate up to e.g. the controller. Instead you could just throw your own exception with more useful details that are meaningful to your domain. In this situation, the controller is a good place to catch that exception. You then have different options of what to do, e.g.:

      • Inform the user that the record couldn't be saved due to concurrent changes by another user and throw all changes away, forcing him to do everything from scratch. This is of course painful for the user and should only be done if this really occurs rarely.
      • Inform the user about the issue and let him decide what to do, e.g. force his changes or start over.

    While this goes beyond your question, I hope this still helps you.