Transactions with ASP.NET Identity UserManager

2019-05-10 18:48发布

问题:

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;
}

回答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;
    }
}


回答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.