When I first designed KenHaggerty.Com, I used a different DbContext for related entities. This involved separate entity
configurations for each DbContext > OnModelCreating. I also had issues with database migrations when I implemented
an entity foreign key to the user. I refactored KenHaggerty.Com to use a single DbContext, but I use service classes which
implement database functions for related entities.
You need to create an interface of the service's public properties and methods for dependency injection.
I include the interface at the top of my class file. You can put the interface in it's own file but I find having
the class and interface in the same window aids development. The service class must implement the interface.
public class UserService : IUserService
You inject the interface and implementation type into the service container at Startup.cs > ConfigureServices.
The Users Without Identity Project implements trapping a concurrency conflict with the
DbUpdateConcurrencyException. The service needs to propagate exceptions to the invoker. The
UserService implements the SaveChangesAsync() in a private method where I trap and throw any
exception as System. Exception. The public add and update methods trap and throw
the exception. The update method can throw 2 types of exceptions,
Microsoft. EntityFrameworkCore. DbUpdateConcurrencyException and
Microsoft. EntityFrameworkCore. DbUpdateException. I use a try catch to trap the 2
types in the update code. The add method will never throw a DbUpdateConcurrencyException.
_ = await _userService.UpdateAppUserAsync(appUser, Input.RowVersion);
catch (DbUpdateConcurrencyException ex)
var entry = ex.Entries.Single();
var databaseEntry = entry.GetDatabaseValues();
if (databaseEntry == null)
"Unable to save changes. The User was deleted by another user. Click Cancel.");
ModelState.Clear(); // required to update Input model
ModelState.AddModelError(string.Empty, "The record you attempted to edit " +
"was modified by another user after you got the original values. The " +
"edit operation was canceled and the current values in the database " +
"have been displayed. You can continue to edit then Save again. " +
"Otherwise click Cancel.");
var databaseValues = (AppUser)databaseEntry.ToObject();
Input = new EditUserInputModel()
Id = databaseValues.Id,
LoginName = databaseValues.LoginName,
MustChangePassword = databaseValues.MustChangePassword,
IsAdmin = databaseValues.IsAdmin,
RowVersion = databaseValues.RowVersion
catch (RetryLimitExceededException /* dex */)
// Retry Limit = 6
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem " +
"persists, see your system administrator.");
throw new InvalidOperationException("DbUpdateException occurred creating a new user.");
_ = await _userService.AddAppUserAsync(newUser);
throw new InvalidOperationException("DbUpdateException occurred creating a user.");
The interface provides the method signatures to intellisense.
I added a xml description to the method. This is easily done by entering 3 slashes (///) on the empty line
above the method which generates a template with parameters defined. I completed the empty fields and
added my exceptions to the description.
I enjoy writing these articles. It often enhances and clarifies my coding. I create research projects
to analyze and compose the articles before I publish them. The projects are the result of
a lot of refactoring and are provided with a MIT license. Registered users can download the
projects from Manage > Assets.