附加类型' X'的实体失败,因为同一类型的另一个实体已具有相同的主键值。错误

时间:2018-05-03 02:50:46

标签: asp.net asp.net-mvc entity-framework-6

你好我对MVC编程很新,并且遇到了一个相当奇怪的错误,我似乎无法解决这个问题。我正在创建一个MVC 5应用程序,当我尝试使用来自我的控制器的编辑选项时,父实体和子实体似乎都正确加载,但是当我尝试将更改保存回来时,我收到错误"附加类型' X.Models.ApplicationUser'的实体。失败,因为同一类型的另一个实体已经具有相同的主键值"。我有以下ViewModel / Models:

    public class EditMatchesViewModel
{
    public int ID { get; set; }
    [Required]
    public DateTime Date { get; set; }
    [Required]
    public string Division { get; set; }
    [Required]
    public Team HomeTeam { get; set; }
    [Required]
    public Team AwayTeam { get; set; }
    public List<Game> Games { get; set; }
    public MatchStatus Status { get; set; }

}

    public class Game
{
    public int ID { get; set; }
    public GameType GameType { get; set; }
    public virtual ApplicationUser AwayPlayer1 { get; set; }
    public virtual ApplicationUser AwayPlayer2 { get; set; }
    public virtual ApplicationUser HomePlayer1 { get; set; }
    public virtual ApplicationUser HomePlayer2 { get; set; } 
    public int AwayScore1 { get; set; }
    public int AwayScore2 { get; set; }
    public int HomeScore1 { get; set; }
    public int HomeScore2 { get; set; }

}

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
    public string UserRole { get; set; }
    public bool Active { get; set; }
    public Team Team { get; set; }
}

    public class Team
{
    public int ID { get; set; }
    public string TeamName { get; set; }
    public bool Active { get; set; }
    public virtual ICollection<ApplicationUser> Players { get; set; }
}

以下控制器:

public async Task<ActionResult> Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Match matchData = await db.Matches.
            Where(m => m.ID == id).
            Include(t => t.AwayTeam).
            Include(t2 => t2.HomeTeam).
            Include(x => x.Games.Select(g => g.GameType)).               
            FirstOrDefaultAsync();

        EditMatchesViewModel model = new EditMatchesViewModel
        {
            Date = matchData.Date,
            Division = matchData.Division,
            AwayTeam = matchData.AwayTeam,
            HomeTeam = matchData.HomeTeam,
            ID = matchData.ID,
            Status = matchData.Status,
            Games = matchData.Games.ToList()
        };

        ViewBag.teams = new SelectList(db.Teams.Where(a => a.Active == true).ToList(), "ID", "TeamName");
        ViewBag.players = db.Users.AsNoTracking().Where(a => a.Active == true).ToList();
        ViewBag.gametypelist = db.GameType.Where(a => a.Active == true).AsNoTracking().ToList();
        if (model == null)
        {
            return HttpNotFound();
        }
        return View(model);
    }

    // POST: Matches/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Edit([Bind(Include = "ID,Date,Division,HomeTeam,AwayTeam,Games")] Match match)
    {
        if (ModelState.IsValid)
        {

            db.Entry(match).State = EntityState.Modified;
            //db.Set<Match>().AddOrUpdate(match);
            await db.SaveChangesAsync();
            return RedirectToAction("Index");
        }
        return View(match);
    }

最后是以下观点:

@model TennisClub.Models.EditMatchesViewModel

@{
ViewBag.Title = "Update match and game information.";
}

<h2>Edit</h2>


@using (Html.BeginForm())
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>Match</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.ID)

    <div class="form-group">
        @Html.LabelFor(model => model.Date, htmlAttributes: new { @class = 
"control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Date, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Date, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Division, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Division, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Division, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.HomeTeam, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.HomeTeam.ID, (IEnumerable<SelectListItem>)ViewBag.Teams, "Please Select Home Team", new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.HomeTeam, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.AwayTeam, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.AwayTeam.ID, (IEnumerable<SelectListItem>)ViewBag.Teams, "Please Select Away Team", new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.AwayTeam, "", new { @class = "text-danger" })
        </div>
    </div>


    <table class="table table-striped">
        <thead>
            <tr>
                <th>Game Type</th>
                <th>Away Team</th>
                <th>Home Team</th>
                <th>Away Score</th>
                <th>Home Score</th>
            </tr>
        </thead>
        <tbody>
            @for (var i = 0; i < Model.Games.Count; i++)
            {

                <tr>
                    @Html.HiddenFor(model => Model.Games[i].ID)
                    <td>@Html.LabelFor(model => 
Model.Games[i].GameType.Name, htmlAttributes: new { @class = "control-label 
col-md-2" })<br />@Html.DropDownListFor(model => Model.Games[i].GameType.ID, 
new SelectList(ViewBag.gametypelist, "ID", "Name", 
Model.Games[i].GameType.ID), null, new { @class = "form-control" })</td>
                    <td>
                        @Html.LabelFor(model => 
Model.Games[i].AwayPlayer1.UserName, htmlAttributes: new { @class = 
"control-label col-md-2" })<br />@Html.DropDownListFor(model => 
Model.Games[i].AwayPlayer1.Id,
Model.Games[i].AwayPlayer1 != null ? new SelectList(ViewBag.players, "Id", 
"UserName", Model.Games[i].AwayPlayer1.Id) :
new SelectList(ViewBag.players, "Id", "UserName"), "Please select away 
player 1", new { @class = "form-control" })
                        <br />
                        @Html.LabelFor(model => 
Model.Games[i].AwayPlayer2.UserName, htmlAttributes: new { @class = 
"control-label col-md-2" })<br />@Html.DropDownListFor(model => 
Model.Games[i].AwayPlayer2.Id,
Model.Games[i].AwayPlayer2 != null ? new SelectList(ViewBag.players, "Id", 
"UserName", Model.Games[i].AwayPlayer2.Id) :
new SelectList(ViewBag.players, "Id", "UserName"), "Please select away 
player 2", new { @class = "form-control" })
                    </td>
                    <td>
                        @Html.LabelFor(model => 
Model.Games[i].HomePlayer1.UserName, htmlAttributes: new { @class = 
"control-label col-md-2" })<br />@Html.DropDownListFor(model => 
Model.Games[i].HomePlayer1.Id,
Model.Games[i].HomePlayer1 != null ? new SelectList(ViewBag.players, "Id", 
"UserName", Model.Games[i].HomePlayer1.Id) :
new SelectList(ViewBag.players, "Id", "UserName"), "Please select home 
player 1", new { @class = "form-control" })
                        <br />
                        @Html.LabelFor(model => 
Model.Games[i].HomePlayer2.UserName, htmlAttributes: new { @class = 
"control-label col-md-2" })<br />@Html.DropDownListFor(model => 
Model.Games[i].HomePlayer2.Id,
Model.Games[i].HomePlayer2 != null ? new SelectList(ViewBag.players, "Id", 
"UserName", Model.Games[i].HomePlayer2.Id) :
new SelectList(ViewBag.players, "Id", "UserName"), "Please select home 
player 2", new { @class = "form-control" })
                    </td>
                    <td>
                        @Html.LabelFor(model => Model.Games[i].AwayScore1, 
htmlAttributes: new { @class = "control-label col-md-2" })<br 
/>@Html.EditorFor(model => Model.Games[i].AwayScore1, new { htmlAttributes = 
new { @class = "form-control" } })<br />
                        @Html.LabelFor(model => Model.Games[i].AwayScore2, 
htmlAttributes: new { @class = "control-label col-md-2" })<br />@Html.EditorFor(model => Model.Games[i].AwayScore2, new { htmlAttributes = new { @class = "form-control" } })
                    </td>
                    <td>
                        @Html.LabelFor(model => Model.Games[i].HomeScore1, htmlAttributes: new { @class = "control-label col-md-2" })<br />@Html.EditorFor(model => Model.Games[i].HomeScore1, new { htmlAttributes = new { @class = "form-control" } })<br />
                        @Html.LabelFor(model => Model.Games[i].HomeScore2, htmlAttributes: new { @class = "control-label col-md-2" })<br />@Html.EditorFor(model => Model.Games[i].HomeScore2, new { htmlAttributes = new { @class = "form-control" } })
                    </td>
                </tr>
            }
        </tbody>
    </table>


    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>
</div>
}

<div>
@Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

我从其他一些帖子中尝试了一些没有运气的东西,似乎无论我是否说要包括它,ApplicationUser总是被加载。它似乎总是包括团队ID,然后再次包括玩家和不断。任何帮助或方向将不胜感激。

2 个答案:

答案 0 :(得分:0)

问题发生在这一行,因为另一个Match实体集仍然在内存中的某个地方加载了与POST方法中的Match viewmodel完全相同的主键,并且你不能超过保存更改时,内存中具有相同主键的一个实体:

db.Entry(match).State = EntityState.Modified;

在执行Match之前,请尝试使用Attach()方法,而不是直接设置SaveChangesAsync()实体状态:

db.Matches.Attach(match);
await db.SaveChangesAsync();

或者使用AsNoTracking()在刚刚用于检索数据的GET操作方法中禁用Match的实体跟踪:

Match matchData = await db.Matches.AsNoTracking()
      .Where(m => m.ID == id)
      .Include(t => t.AwayTeam)
      .Include(t2 => t2.HomeTeam)
      .Include(x => x.Games.Select(g => g.GameType))           
      .FirstOrDefaultAsync();

如果上述两种可能的解决方案都不起作用,请在检索到GET操作方法的结果后将Match实体状态设置为EntityState.Detached

if (matchData != null)
{
    context.Entry(matchData).State = EntityState.Detached;
}

作为旁注,最好通过在POST操作方法(使用Match方法)中使用其主键来加载Find()实体,或者根据在内存中加载的现有实体更新属性值,而不是分离和重新连接它。

类似问题:

ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value

Error attaching entity because of same primary key when trying to save an update

答案 1 :(得分:0)

前三个解决方案无效。我最终被迫做出的解决方案是跟随你的旁注(至少我认为这就是你所得到的)。之所以出现这个问题,是因为GameType和AppplicationUsers的包含试图创建各自对象的新实体,而不是在数据库中查找信息并将其设置为已修改。下面是更新后的控制器代码:

AttributeError