MVC5 - ManytoMany使用ApplicationUser添加新行而不是使用Existing Ones / Or Throws Error

时间:2017-12-31 15:54:41

标签: c# entity-framework asp.net-mvc-5 asp.net-identity

我构建了一个MVC5 Web App。我使用过Identity Framework。我试图让它成为每个用户都有一个他与之关联的军队列表。这些将通过复选框在页面上更新。

我已使用此附加属性扩展了默认的ApplicationUser类。

    public virtual ICollection<Army> Armies { get; set; }

    public ApplicationUser()
    {
        Armies = new HashSet<Army>();
    }

我的军队也有这些领域。军队的桌子上有大约10个条目,详细说明了不同的军队。

public class Army
{
    public int Id { get; set; }
    public String Name { get; set; }
    public String IconLocation { get; set; }
    public virtual ICollection<ApplicationUser> Users { get; set; }

    public Army()
    {
        Users = new HashSet<ApplicationUser>();
    }
}

然后我在DbContext

中设置了关系
          protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder
            .Entity<ApplicationUser>()
            .HasMany(u => u.Armies)
            .WithMany(t => t.Users)
            .Map(m =>
            {
                m.ToTable("UserArmies");
                m.MapLeftKey("UserId");
                m.MapRightKey("ArmyId");
            });

}

用户可以在“管理用户页面”上编辑这些内容,这些内容显示为复选框项目。

public class CheckBoxItem
{
    public int Id { get; set; }
    public String Name { get; set; }
    public bool IsChecked { get; set; }
}

控制器调用PopulateArmyCheckBoxList()方法将军队变成复选框列表。 (这最初是使用Lists完成的,但下面用字典实现,而我正试图解决我的问题。)这部分程序按预期工作,我正确显示我的复选框,并勾选相关的方框。

   private List<CheckBoxItem> PopulateArmyCheckBoxList()
    {
        var userId = User.Identity.GetUserId();
        var userArmies = _context.Users.SingleOrDefault(u => u.Id == userId).Armies.ToDictionary(t=>t.Id, t=>t.Name);

        var allArmies = _context.Armies.ToDictionary(t=>t.Id, t => t.Name);
        var armyCheckBoxList = new List<CheckBoxItem>();


        foreach (var army in allArmies)
        {
            armyCheckBoxList.Add(new CheckBoxItem()
            {
                Id = army.Key,
                Name = army.Value,
                IsChecked = false
            });
        }

        foreach(var army in userArmies)
        {
            var cb = armyCheckBoxList.Find(c => c.Id == army.Key);
            if(cb != null)
                cb.IsChecked = true;
        }

        return armyCheckBoxList;
    }

viewModel包含一个User和一个名为Armies的CheckBoxItem列表。如果是第一次访问,则User == null并且viewModel将填充数据。否则它会获取数据并更新用户。

    public async Task<ActionResult> Index(IndexViewModel viewModel)
    {
        var userId = User.Identity.GetUserId();

        if (viewModel.User == null)
        {
            var model = new IndexViewModel
            {
                User = UserManager.FindById(userId),
                Armies = PopulateArmyCheckBoxList()
            };
            return View(model);
        }

        var user = UserManager.FindById(userId);
        user.UserName = viewModel.User.UserName;
        user.Email = viewModel.User.Email;
        user.ProfilePictureLocation = SaveProfileImage(user, viewModel.ProfilePicture);
        UserManager.Update(user);

        ViewBag.StatusMessage = "Your Profile Has Been Updated.";
        viewModel.User = UserManager.FindById(User.Identity.GetUserId());
        return View(viewModel);
    }

最后阶段是一切可怕的错误。我需要更新数据库上的UserArmies表。我尝试通过在调用UserManager.Update(user);

之前添加以下代码行来完成此操作
        user.Armies = GetUserArmies(user, viewModel.Armies);

调用以下方法。首先,我得到一个Id的列表,并称之为选择敌人。然后我从数据库中获取军队列表并将其放入allArmies。 userArmies是要填充和返回的军队的空白列表。

    private List<Army> GetUserArmies(ApplicationUser user, List<CheckBoxItem> armies)
    {
        var selectedArmies = armies.Where(a => a.IsChecked).Select(a => a.Id).ToList();
        var allArmies = _context.Armies.ToList();

        var userArmies = new List<Army>();

        foreach (var army in selectedArmies)
            userArmies.Add(allArmies.Find(t => t.Id == army));

        return userArmies;
    }

我有2个不需要的结果。首先是我目前的情况。 发生错误说 无法定义两个对象之间的关系,因为它们附加到不同的ObjectContext对象

当我从陆军类中移除虚拟关键字时,我得到以下结果,其中军队表首先复制新Id下的军队行。然后将新的UserArmy保存到新创建的Exsiting Army副本下的数据库中。这会导致我的复选框列表加倍。

我哪里错了?任何帮助将不胜感激。

其他信息 我也有User Article类使用相同的armies表。这使用非常类似的代码并且完美地工作而不会产生重复。这让我觉得它与SingManager和_Context

有关

2 个答案:

答案 0 :(得分:0)

您需要创建自己的DbContext并覆盖OnModelCreating方法。

public class MyDbContext : DbContext 
{ 
    public MyDbContext () : base("YourDefaultConnectionString")
    {           
        // Database Initializer 
    }
     protected override void OnModelCreating(DbModelBuilder modelBuilder)
     {
       // Write your own model creation code;  
     }
}

希望这会给你一个想法。

答案 1 :(得分:0)

我认为已找到答案

我使用UserManager和_context(我自己的DbContext)来访问数据,由于某种原因它无法将数据合并。通过交换我的方法来通过我的DBContext而不是UserManager调用用户我得到了错误并设法解决了问题。