This is MVC 5/ EF6. So I have the following classes:
public class User : IdentityUser
{
public User()
{
Levels = new List<Level>();
}
[Required, MaxLength(200)]
public string FirstName { get; set; }
[Required, MaxLength(200)]
public string LastName { get; set; }
public virtual ICollection<Level> Levels { get; set; }
}
and
public class Level
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In addition to regular MVC5 membership tables it creates 2 more: Levels
and UserLevels
(with User_Id and Level_Id columns). Levels
table has a static data (i.e. 1 - Excellent, 2 - Good, etc) and is kind of a library, I don't want to insert in this table.
What I'm trying to do is when user registers on the site and chooses the level it would go ahead and retrieve it from DB so that UserLevels
table is populated with new UserID
and selected LevelID
. Here is my code:
Level level = DBContext.Levels.Where(s => s.Name == model.Level.Name).SingleOrDefault();
if (level == null)
ModelState.AddModelError("", "Invalid Level.");
if (ModelState.IsValid)
{
var user = new User() {
UserName = model.UserName,
FirstName = model.FirstName,
LastName = model.LastName
};
user.Levels.Add(level);
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
return View(model);
It throws an exception on this line: An entity object cannot be referenced by multiple instances of IEntityChangeTracker..
var result = await UserManager.CreateAsync(user, model.Password);
I'm guessing it has something to do with it trying to insert into Levels table the level that already exists in there? Of course it might be something else... Any advice? Thanks in advance!
If you're using UserManager
the way 'it came out of the box' then it's probably instantiated like this:
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDbContext())))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
It means that if you don't provide a UserManager
instance to the AccountController
constructor (and it's probably the case here), a new MyDbContext
is created for the UserManager
store.
Yet, you have another instance of MyDbContext
, as i can infer from this line in your code:
Level level = DBContext.Levels.Where(s => s.Name == model.Level.Name).SingleOrDefault();
All it means is that you have to make your UserManager
use the same context:
public AccountController() : this(null)
{
}
public AccountController(UserManager<User> userManager = null)
{
DBContext = new MyDBContext();
if (userManager == null)
userManager = new UserManager<User>(new UserStore<User>(DBContext));
UserManager = userManager;
}
public UserManager<User> UserManager { get; private set; }
public MyDBContext DBContext;
This way, you're creating the (only) MyDbContext
instance first, then passing it to the UserManager
constructor (as the IUserStore<User>
).
Also, you can definitely make a good use of Dependency Injection here and have the MyDbContext
instance injected for you by a DI container and keeping your current code almost unchanged:
public AccountController(MyDBContext context)
: this(new UserManager<User>(new UserStore<User>(context)))
{
this.DBContext = context;
}
See Tutorial (for Microsoft Unity).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With