Search code examples
c#entity-frameworkorleans

Bubbling up Update Concurrency Exceptions to view


What is a good way to bubble up a DbUpdateConcurrencyException to the view from the grain?

I'm currently working on an Orlean's prototype that has a custom state that I'm using Entity Framework Core to communicate with the DB and using the optimistic concurrency patterns built into EF Core to manage the concurrency issues.

Where I'm having an issue is that I want to bubble up my Exception from the grain to the view and am not receiving it on the view end.

I'm trying to accomplish this because I want to deal with some of the concurrency issues that are more pressing on the view so that the user can decide or at least be alerted to the issue.

I brought this up on the Orlean's Gitter, but didn't get many ideas from it.

Example of my code for updating:

 public Task UpdateUser(User user)
    {
        //Reason for second try/catch is to bubble the error to controller
        try
        {
            userState = new User
            {
                Username = this.GetPrimaryKeyString(),
                accountType = user.accountType,
                FName = user.FName,
                LName = user.LName,
                RowVersion = user.RowVersion,
                CreatedDate = user.CreatedDate
            };
            UpdateState();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            throw ex;
        }
        return Task.CompletedTask;
    }


 public Task UpdateState()
    {
        using (var context = new OrleansContext())
        {
            context.users.Update(userState);
            try
            {
                context.SaveChanges();
            }
            catch ( DbUpdateConcurrencyException ex)
            {
                var entry = ex.Entries.Single();
                var clientValues = (User)entry.Entity;
                var databaseEntry = entry.GetDatabaseValues();
                //Make sure the row wasn't deleted
                if(databaseEntry != null)
                {
                    var databaseValues = (User)databaseEntry.ToObject();
                    if(clientValues.accountType != databaseValues.accountType)
                    {
                        //Bubble up the exception to controller for proper handling
                        throw ex;
                    }
                    //Update Row Version to allow update
                    userState.RowVersion = databaseValues.RowVersion;
                    context.SaveChanges();
                }                
            }
        }
        return Task.CompletedTask;
    }

I'm open to any suggestions on this as long as it allows the user to be alerted to the Exception and can view their data and the current DB values.


Solution

  • There is a chance that the exception is not being serialized or deserialized correctly. The primary reasons for this could be:

    1. The Exception class does not correctly implement the ISerializable pattern.
    2. The assembly which contains the Exception class is not present on the client, so the client does not understand how to create the Exception type.

    In this case, I would lean towards the second reason, because most (but not all!) Exception classes do correctly implement the ISerializable pattern.

    In either case, you can catch your exception and turn it into a generic exception. You could create a helper method to do this using the LogFormatter.PrintException(Exception) method from Orleans to format the exception as a string.

    public static void ThrowPlainException(Exception e) =>
        throw new Exception(Orleans.Runtime.LogFormatter.PrintException(e));