Не удалось подключить объект типа «X», поскольку другой объект того же типа уже имеет такое же значение первичного ключа. ошибка - PullRequest
0 голосов
/ 03 мая 2018

Здравствуйте, я новичок в программировании 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 всегда загружается независимо от того, говорю я, включать его или нет. Также кажется, что он всегда включает в себя идентификатор команды, который затем включает игроков снова и снова и снова. Любая помощь или направление будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Первые три решения не сработали. То, что я в конечном итоге был вынужден сделать, чтобы найти решение, было следовать вместе с вашей запиской (по крайней мере, я думаю, что это то, что вы получили). Эта проблема возникала из-за того, что включенные в GameType и AppplicationUsers пытались создать новые объекты своих соответствующих объектов вместо того, чтобы находить информацию в базе данных и изменять ее. Ниже приведен обновленный код контроллера, который начал работать:

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Edit(EditMatchesViewModel match)
    {

        if (ModelState.IsValid)
        {
            var updatedMatch = db.Matches.Find(match.ID);
            updatedMatch.Date = match.Date;
            updatedMatch.AwayTeam = match.AwayTeam;
            updatedMatch.HomeTeam = match.HomeTeam;
            updatedMatch.Division = match.Division;
            updatedMatch.Games = new List<Game>();

            foreach (var game in match.Games)
            {
                if (game.ID > 0)
                {
                    var updatedGame = db.Games.Find(game.ID);
                    updatedGame.GameType = db.GameType.Find(game.GameType.ID);
                    updatedGame.AwayPlayer1 = db.Users.Find(game.AwayPlayer1.Id);
                    updatedGame.AwayPlayer2 = db.Users.Find(game.AwayPlayer2.Id);
                    updatedGame.HomePlayer1 = db.Users.Find(game.HomePlayer1.Id);
                    updatedGame.HomePlayer2 = db.Users.Find(game.HomePlayer2.Id);
                    updatedGame.AwayScore1 = game.AwayScore1;
                    updatedGame.AwayScore2 = game.AwayScore2;
                    updatedGame.HomeScore1 = game.HomeScore1;
                    updatedGame.HomeScore2 = game.HomeScore2;

                    updatedMatch.Games.Add(updatedGame);
                }
            }
            db.Matches.Attach(updatedMatch);
            db.Entry(updatedMatch).State = EntityState.Modified;
            await db.SaveChangesAsync();

            return RedirectToAction("Index");
        }
        return View(match);
    }
0 голосов
/ 03 мая 2018

Проблема возникает в этой строке, потому что другой набор сущностей Match все еще загружен где-то в памяти с точно таким же первичным ключом, как у Match viewmodel в методе POST, и вы не можете иметь более одной сущности с одним и тем же первичным ключом в памяти при сохранении изменений:

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

Вместо непосредственной установки Match состояния объекта таким образом, попробуйте использовать метод Attach() перед выполнением SaveChangesAsync():

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

Или используйте AsNoTracking(), чтобы отключить отслеживание сущности для Match в методе действия GET, который просто используется для извлечения данных:

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();

Если оба вышеупомянутых возможных решения не работают, установите Match состояние объекта на EntityState.Detached после того, как полученный запрос приводит к методу действия GET:

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

В качестве примечания, лучше загружать объект Match, используя его первичный ключ в методе действия POST (с методом Find()), или обновлять значения свойств на основе существующего объекта, загруженного в память, а не отсоединять и снова подключать его.

Похожие проблемы:

ASP.NET MVC - Не удалось подключить объект типа 'MODELNAME', поскольку другой объект того же типа уже имеет такое же значение первичного ключа

Ошибка при подключении объекта из-за того же первичного ключа при попытке сохранить обновление

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...