4

Using asp.net identity RTW version.

I need to perform several actions in a transaction, including both UserMananger function calls and other operations on my DbContext (example: create new user, add it to group and perform some business-logic operations).

How should I do this?

My thoughts follow.

TransactionScope

using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
    // Do what I need
    if (everythingIsOk) scope.Complete();
}

The problem is: UserManager functions are all async, and TransactionScope was not designed to work with async/await. It seems to be solved in .Net Framework 4.5.1. But I use Azure Web Sites to host my project builds, so I cannot target 4.5.1 yet.

Database transaction

public class SomeController : Controller
{
    private MyDbContext DbContext { get; set; }
    private UserManager<User> UserManager { get; set; }

    public AccountController()
    {
        DbContext = new MyDbContext()
        var userStore = new UserStore<IdentityUser>(DbContext);
        UserManager = new UserManager<IdentityUser>(userStore);
    }

    public async ActionResult SomeAction()
    {
        // UserManager uses the same db context, so they can share db transaction
        using (var tran = DbContext.Database.BeginTransaction())
        {
            try
            {
                // Do what I need
                if (everythingIsOk)
                    tran.Commit();
                else
                {
                    tran.Rollback();
                }
            }
            catch (Exception)
            {
                tran.Rollback();
            }
        }
    }
}

That seems to work, but how can I unit-test it?

UserManager<> constructor accepts IUserStore<>, so I can easily stub it.

UserStore<> constructor accepts DbContext, no idea how I can stub this.

Community
  • 1
  • 1
aleyush
  • 644
  • 1
  • 6
  • 19

1 Answers1

0

You can implement your own test user store that can be stubbed out for your unit test.

If you want to use the actual EF UserStore in your tests, that also will work, but it will be creating a database using the DefaultConnection string by default. You could specify a DatabaseInitializer to always drop/recreate your tables in your tests if you wanted to ensure a clean db for every test.

Hao Kung
  • 28,040
  • 6
  • 84
  • 93