你好我对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,然后再次包括玩家和不断。任何帮助或方向将不胜感激。
答案 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()
实体,或者根据在内存中加载的现有实体更新属性值,而不是分离和重新连接它。
类似问题:
Error attaching entity because of same primary key when trying to save an update
答案 1 :(得分:0)
前三个解决方案无效。我最终被迫做出的解决方案是跟随你的旁注(至少我认为这就是你所得到的)。之所以出现这个问题,是因为GameType和AppplicationUsers的包含试图创建各自对象的新实体,而不是在数据库中查找信息并将其设置为已修改。下面是更新后的控制器代码:
AttributeError