Transactions with ASP.NET Identity UserManager

11,189

Solution 1

EFDbContext in your examples are the same - in both cases you resolve them from OWIN context, so this is not an issue. However, Identity is written in storage-agnostic fashion, meaning storage mechanism can be replaced by non SQL Server. This calls for lack of transactions inside of AppUserManager. So you need to create your own.

I'm routinely using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled) in my production applications (only with a bit more architecture):

using(var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
    try
    {
        AppUserManager appUserManager = HttpContext.GetOwinContext().GetUserManager<AppUserManager>();

        AppUser member = await appUserManager.FindByIdAsync(User.Identity.GetUserId());

        member.HasScheduledChanges = true;

        IdentityResult identityResult = appUserManager.Update(member);
        scope.Complete();
    }
    catch (Exception ex)
    {
        scope.Dispose();
        throw;
    }
}

Solution 2

the complete solution for transaction commit / rollback using asp.net identity UserManager

var appDbContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
using (var identitydbContextTransaction = appDbContext.Database.BeginTransaction())
{
   try
   {
       var result = await UserManager.CreateAsync(user, "password");
       if (result.Succeeded)
       {
         var userinfo = await UserManager.FindByNameAsync("Email");
         var userId = user.Id;
         await UserManager.AddToRoleAsync(userId, "rolename");

         identitydbContextTransaction.Commit();
       }
  }
  catch (Exception)
  {
        identitydbContextTransaction.Rollback();
  }
}

it may help you, to make transaction using asp.net identity UserManager. but its working me, if any error occurred in transaction, it will rollback all transaction.

Share:
11,189
nmit026
Author by

nmit026

Updated on June 09, 2022

Comments

  • nmit026
    nmit026 about 2 years

    I'm trying to update a user.

    AppUserManager appUserManager = HttpContext.GetOwinContext().GetUserManager<AppUserManager>();
    
    AppUser member = await appUserManager.FindByIdAsync(User.Identity.GetUserId());
    
    member.HasScheduledChanges = true;
    
    IdentityResult identityResult = appUserManager.Update(member);
    

    If a subsequent call to a Web API fails, I need to roll back any changes to the user. I know about transactions, like this:

    using (var context = HttpContext.GetOwinContext().Get<EFDbContext>())
     {
        using (var dbContextTransaction = context.Database.BeginTransaction())
        {      
            try
            {   
                // Changes
    
                member.HasScheduledChanges = true;
    
                // Would this be transactional?
                IdentityResult identityResult = appUserManager.Update(member);               
    
                context.SaveChanges();
    
                dbContextTransaction.Commit();
            }
            catch //(Exception ex)
            {
    
                // dbContextTransaction.Rollback(); no need to call this manually.
            }
        }
    }
    

    But will operations done with AppUserManager inside the try block be transactional? Also, do they use the same instance of EFDbContext? In other words, I don't know if var context at the start of the second code example would be used by the appUserManager "Update" method call in the try block.

    Also, AppUserManager is created like this:

    public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
    {           
    
        EFDbContext db = context.Get<EFDbContext>();
    
        AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db));
    
        // etc.
    
        return manager;
    }
    
  • nmit026
    nmit026 about 8 years
    Thank you! My first thought was to manipulate the AspNetUsers table directly using a dbContext that supports transactions, but I don't think it's possible. I think one has to use UserManager to update users in the identity system, is there no other way?
  • trailmax
    trailmax about 8 years
    Yes, you can manipulate user records directly via dbContext. That's what AppUserManager does, only it adds a bit more validation
  • Suhail Mumtaz Awan
    Suhail Mumtaz Awan almost 8 years
    this answer solved my problem, i didn't wanted to use "Transaction Scope", i had to add reference to OWIN "using Microsoft.AspNet.Identity.Owin;"
  • Leandro De Mello Fagundes
    Leandro De Mello Fagundes over 7 years
    But doing it at the controller is the right way? Controller with a transaction?
  • nmit026
    nmit026 over 7 years
    You're an expert on Owin and Identity, perhaps you could look at this: stackoverflow.com/questions/43037450/…
  • span
    span over 6 years
    Interesting, what are the advantages of doing this instead of using the accepted answer. I'm looking to do pretty much the same thing.
  • Michael Brown
    Michael Brown over 6 years
    scope.Dispose() is completely redundant in your exception handler.
  • habib
    habib about 6 years
    It is an easier method for a user to start and complete transactions themselves within an existing DbContext – allowing several operations to be combined within the same transaction and hence either all committed or all rolled back as one. It also allows the user to more easily specify the isolation level for the transaction.
  • Marie
    Marie almost 6 years
    @trailmax To expand on Michaels comment the using statement wraps the contents in a try finally and calls scope.dispose(). Catching and rethrowing the exception serves no purpose except slowing the failure down a bit because it actually has to catch the exception.
  • Marie
    Marie almost 6 years
    I might be wrong but isnt the Rollback here redundant? If an exception happens the transaction will be disposed and the changes lost.