实体框架6和Asp.Net Identity UserManager:多个DbContext

dim*_*ima 5 c# entity-framework-6 asp.net-mvc-5 asp.net-identity-2

这是MVC 5/EF6.所以我有以下课程:

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; }
}
Run Code Online (Sandbox Code Playgroud)

和

public class Level
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    public virtual ICollection<User> Users { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

除了常规的MVC5成员资格表之外,它还创建了2个:Levels和UserLevels(使用User_Id和Level_Id列).Levelstable有一个静态数据(即1 - Excellent,2 - Good等)并且是一种库,我不想在这个表中插入.

我正在尝试做的是当用户在网站上注册并选择它将继续并从数据库中检索它的级别,以便该UserLevels表填充新的UserID和选定的LevelID.这是我的代码:

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);
Run Code Online (Sandbox Code Playgroud)

它会在此行引发异常: An entity object cannot be referenced by multiple instances of IEntityChangeTracker..

var result = await UserManager.CreateAsync(user, model.Password);

我猜它与它试图在Levels表中插入已经存在的级别有关吗?当然可能还有别的......有什么建议吗?提前致谢!

hai*_*770 15

如果你正在使用UserManager'它开箱即用'的方式,那么它可能实例化如下:

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; }
Run Code Online (Sandbox Code Playgroud)

这意味着如果您没有为构造函数提供UserManager实例AccountController(这可能就是这种情况),MyDbContext则会为UserManager商店创建一个新实例.

然而,你有另一个实例MyDbContext,因为我可以从你的代码中的这一行推断:

Level level = DBContext.Levels.Where(s => s.Name == model.Level.Name).SingleOrDefault();
Run Code Online (Sandbox Code Playgroud)

这意味着你必须UserManager使用相同的上下文:

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;
Run Code Online (Sandbox Code Playgroud)

这样,您首先创建(唯一)MyDbContext实例,然后将其传递给UserManager构造函数(作为IUserStore<User>).

此外,您绝对可以在此处充分利用依赖注入,并MyDbContext通过DI容器为您注入实例并保持当前代码几乎不变:

public AccountController(MyDBContext context)
: this(new UserManager<User>(new UserStore<User>(context)))
{
    this.DBContext = context;
}
Run Code Online (Sandbox Code Playgroud)

请参阅教程(适用于Microsoft Unity).