I want to perform multiple Identity-related actions in a transaction. There is some guidance in the docs for EF in general, but not for Identity in particular.
This approach is used in many SO posts:
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
var result1 = await _userManager.Foo();
if (!result1.Succeeded) throw new Exception("Could not foo.");
var result2 = await _userManager.Bar();
if (!result2.Succeeded) throw new Exception("Could not bar.");
await transaction.CommitAsync();
}
catch (Exception e)
{
_logger.LogError(e, "Something bad happened, but changes were rolled back.");
// ...handle error
}
Ordinarily, without Identity, if execution enters the catch block then the transaction is not committed and is disposed (and thus automatically rolled back).
But:
- Identity calls
SaveChangesafter every action, so at face value it seems like the rollback would accomplish nothing. - I don't know whether
_contextis the same one used internally by_userManager? I think the context is registered by default as scoped (per request), so I assume that within an ASP.NET Core HTTP request, the same context will be used by both Identity and my code.
Is that correct?
UPDATE:
I can't find definitive documentation for these issues, and am uncomfortable relying on internal implementation details for something so critical. So I opened a docs issue on the repo, asking for official guidance. Please upvote it if this concerns you too.
Update: they closed that docs request without proper consideration. So I opened another on the main repo. Please upvote that.
Your understanding is correct. In ASP.NET Core, the default behavior is to register the
DbContextas scoped, meaning that within the same HTTP request, the same instance of the context will be shared across all components that participate in that request's scope, including the_userManager.So, in your code, both
_contextand_userManagerwill typically share the same instance of theDbContextwithin the same HTTP request. Therefore, using a transaction to perform multiple actions involving both_contextand_userManagershould work as expected.Your code looks fine in terms of using transactions to perform multiple identity-related actions within a single unit of work. However, there are a couple of points to consider for improvement:
Error Handling: Your error handling approach is good, as it ensures that if any of the actions fail, the transaction is rolled back. However, throwing a generic
Exceptionmay not provide enough information about what exactly went wrong. It's a good practice to use more specific exception types or to provide more context about the error.Async/Await Pattern: You're using async/await pattern correctly, which is good for performance and scalability in ASP.NET Core applications.
Logging: You're logging errors, which is important for diagnosing issues. However, make sure to log enough information to facilitate troubleshooting.
Dependency Injection: Ensure that
_userManagerand_contextare properly registered and injected into your class. It seems you're doing it correctly, but it's worth mentioning.Overall, your approach seems solid for performing multiple identity-related actions within a transaction in ASP.NET Core. Just make sure to handle errors effectively and provide enough logging and context for debugging purposes.