Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Injecting DbContext into Identity UserManager

I have a Question model that references a AppUser model. It's a 1 to * relationship because 1 AppUser has many Questions and a question belong to 1 AppUser. My Question class looks like this:

public class Question
{
    public int Id { get; set; }
    public string Subject { get; set; }
    public string Text { get; set; }
    public DateTime Date { get; set; }
    public int NumOfViews { get; set; }
    public AppUser LastAnswerBy { get; set; }

    public AppUser AppUser { get; set; }
    public ICollection<Comment> Comments { get; set; }
}

So i try to add a new Question to the database in my controller like this:

[HttpPost]
    public ActionResult PostQuestion(Question question)
    {
        if (ModelState.IsValid)
        {
            var id = User.Identity.GetUserId();
            var user = UserManager.Users.FirstOrDefault(x => x.Id == id);
            question.Date = DateTime.Now;
            question.AppUser = user;

            _context.Questions.Add(question);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(question);
    }

    private AppUserManager UserManager
    {
        get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); }
    }

With this code i get an exception like this: An entity object cannot be referenced by multiple instances of IEntityChangeTracker. So after searching around a bit, it seems the problem is that my controller and AppUserManager class have 2 different instances of my DbContext and the solution is to inject it into those classes. Injecting it into my controller is no problem, but i have no idea how to inject it into my UserManager class that looks like this:

public class AppUserManager : UserManager<AppUser>
{

    public AppUserManager(IUserStore<AppUser> store)
        : base(store)
    { }

    public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options,
    IOwinContext context)
    {
        AppIdentityDbContext db = context.Get<AppIdentityDbContext>();
        AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db));

        return manager;
    }
}

It is called from my IdentityConfig class that looks like this:

public class IdentityConfig
{
    public void Configuration(IAppBuilder app)
    {
        app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create);
        app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
        app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
        });

        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}

My problem is probably that i don't understand the identity part that well. Any help is most appreciated

Solution: As Tobias said i was using 2 different contexts to update the database. Getting the context like this works:

private AppIdentityDbContext Context
    {
        get { return HttpContext.GetOwinContext().Get<AppIdentityDbContext>(); }
    }
like image 716
Aeterna Avatar asked Apr 11 '15 19:04

Aeterna


1 Answers

You problem is most likely that you're trying to do one thing, with two different Contexts. You're finding the user, using one context, and trying to update the database, using another. To fix this, you should instantiate your context in your controller like this:

_context = HttpContext.GetOwinContext().Get<AppIdentityDbContext>();

This way, you get the same context, which was used to instantiate your AppUserManager.

Please comment, if I'm mistaken, or if it's not clear. :)

like image 191
Tobias Avatar answered Oct 16 '22 02:10

Tobias