更新现有 child 而不是添加新的
Updating existing child instead of adding new one
我正在使用 ASP.NET Core (MVC) 创建 Web 应用程序。数据库是 SQL 服务器。代码优先设计。我是 EF 的新手。
我定义了 Visitor
模型,Checkin
和 Checkout
模型,visitor/checkin 和 1:1 的关系为 1:1 visitor/checkout。该应用程序的想法是,当创建新访客时,将创建新的 check-in 和新的 check-out 记录,并 children 访客记录。
我的模型如下(我使用ForeignKey
数据注解来定义访客和签到以及访客和签出之间的one-to-one关系):
public class Visitor
{
[Key]
public int Id { get; set; }
[DisplayName("Visitor First Name")]
[Required]
public string FirstName { get; set; }
[DisplayName("Visitor Last Name")]
[Required]
public string LastName { get; set; }
public CheckIn CheckIn { get; set; }
public CheckOut CheckOut { get; set; }
}
public class CheckIn
{
[ForeignKey("Visitor")]
public int Id { get; set; }
[DisplayName("Time In")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime Time { get; set; }
public Visitor Visitor { get; set; }
}
public class CheckOut
{
[ForeignKey("Visitor")]
public int Id { get; set; }
[DisplayName("Time Out")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime? Time { get; set; }
public Visitor Visitor { get; set; }
}
我的控制器中有一个 POST 方法,它应该在数据库中添加一个新的访客以及一个新的签入和签出记录作为访客记录的 children。
但是,我 运行 遇到签入记录未添加到数据库的问题:而是更新了数据库中的现有签入记录,因此其主键是新记录的键 (ID)访客记录。新访客记录创建成功。此外,新的结帐记录也已正确创建并作为新访客记录的 child。这是我的控制器中 POST 方法的代码(删除了一些与此问题无关的代码)(也使用视图模型将数据从视图传递到控制器):
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ConfirmSignin(SignVM obj)
{
if (ModelState.IsValid)
{
var visitor = new Visitor();
var checkin = new CheckIn();
var checkout = new CheckOut();
checkin = obj.CheckIn;
checkout = obj.CheckOut;
visitor = obj.Visitor;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
_context.Add(visitor);
_context.Add(checkin);
_context.Add(checkout);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index),"Home",new { LocationId = obj.Visitor.LocationId });
}
return View(obj);
}
在数据库中成功创建访客:
结帐也已在数据库中成功创建:
您会注意到结帐记录的 ID 与 parent 记录访客的 ID 匹配 - 所以这部分似乎工作正常。
但是在数据库中添加新访客时没有添加签到记录:
当我添加访问者 ID=6 时,应该在数据库中添加新的签入记录 - 而 EF 更新了现有的签入记录并将其主键从 5(先前的访问者)更新为 6(新的访问者)。
我假设我在控制器代码中做错了什么。有什么建议吗?
更新 - 2021 年 4 月 28 日:
下面是对应GET方法的代码:
[HttpGet]
public IActionResult ConfirmSignin()
{
SignVM signVM = new();
if (TempData["NewSignIn"] is string s)
{
signVM = JsonConvert.DeserializeObject<SignVM>(s);
}
ViewData["LocationName"] = new SelectList(_context.Locations, "Id", "Name", signVM.Visitor.LocationId);
return View(signVM);
}
下面是 POST 方法更新后的完整代码:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ConfirmSignin(SignVM obj)
{
if (ModelState.IsValid)
{
var escort = new Escort();
escort = await _context.Escorts
.Include(c => c.CheckIn)
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
if (escort == null)
{
escort = obj.Escort;
_context.Add(escort);
await _context.SaveChangesAsync();
}
var visitor = obj.Visitor;
var checkin = obj.CheckIn;
var checkout = obj.CheckOut;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
escort.CheckIn = new List<CheckIn>();
escort.CheckOut = new List<CheckOut>();
escort.CheckIn.Add(checkin);
escort.CheckOut.Add(checkout);
_context.Add(visitor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index),"Home",new { LocationId = obj.Visitor.LocationId });
}
return View(obj);
}
这是对应视图的代码:
@model VisitorLog.Models.ViewModels.SignVM
@{
ViewData["Title"] = "Sign In";
}
<div class="row justify-content-center mt-3">
<div class="col-md-4">
<div class="h4 text-center">Confirm Sign In</div>
<hr />
<form asp-action="ConfirmSignin">
<div class="form-group">
<label asp-for="Visitor.LocationId" class="control-label"></label>
<select asp-for="Visitor.LocationId" class="form-control" disabled asp-items="ViewBag.LocationName"></select>
</div>
<div class="form-group">
<label asp-for="Visitor.FullName" class="control-label"></label>
<input asp-for="Visitor.FullName" class="form-control" readonly value="@Model.Visitor.FullName" />
</div>
<div class="form-group">
<label asp-for="Visitor.Company" class="control-label"></label>
<input asp-for="Visitor.Company" class="form-control" readonly value="@Model.Visitor.Company" />
</div>
<div class="form-group">
<label asp-for="Escort.EmployeeId" class="control-label"></label>
<input asp-for="Escort.EmployeeId" class="form-control" readonly value="@Model.Escort.EmployeeId" />
</div>
<div class="form-group">
<label asp-for="Escort.FullName" class="control-label"></label>
<input asp-for="Escort.FullName" class="form-control" readonly value="@Model.Escort.FullName" />
</div>
<div class="form-group">
<label asp-for="CheckIn.Time" class="control-label"></label>
<input asp-for="CheckIn.Time" class="form-control" readonly type="text" value="@Model.CheckIn.Time.ToString("g")" />
</div>
<div class="form-group">
<label asp-for="CheckOut.ExpectedTime" class="control-label"></label>
<input asp-for="CheckOut.ExpectedTime" class="form-control" readonly type="text" value="@Model.CheckOut.ExpectedTime.ToString("g")" />
</div>
<div class="form-group">
<input asp-for="Visitor.LocationId" class="form-control" hidden value="@Model.Visitor.LocationId" />
</div>
<div class="form-group">
<input asp-for="Visitor.FirstName" class="form-control" hidden value="@Model.Visitor.FirstName" />
</div>
<div class="form-group">
<input asp-for="Visitor.LastName" class="form-control" hidden value="@Model.Visitor.LastName" />
</div>
<div class="form-group">
<input asp-for="Visitor.SignedIn" class="form-control" hidden value="@Model.Visitor.SignedIn" />
</div>
<div class="form-group">
<input asp-for="Escort.FirstName" class="form-control" hidden value="@Model.Escort.FirstName" />
</div>
<div class="form-group">
<input asp-for="Escort.LastName" class="form-control" hidden value="@Model.Escort.LastName" />
</div>
<div class="form-group">
<input asp-for="Escort.EmailAddress" class="form-control" hidden value="@Model.Escort.EmailAddress" />
</div>
<div class="form-group">
<input asp-for="Escort.Phone" class="form-control" hidden value="@Model.Escort.Phone" />
</div>
<div class="form-group text-center mt-4">
<input type="submit" class="btn btn-primary col-5" value="Confirm" />
<a class="btn btn-danger col-5 offset-1" asp-controller="Visitors" asp-action="SignIn" asp-route-LocationId=@Context.Request.Query["LocationId"]>Back</a>
</div>
</form>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
你必须修复你的 类:
public class CheckIn
{
[Key]
public int Id { get; set; }
[DisplayName("Time In")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime Time { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public virtual Visitor Visitor { get; set; }
}
public class CheckOut
{
[Key]
public int Id { get; set; }
[DisplayName("Time Out")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime? Time { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public virtual Visitor Visitor { get; set; }
}
并修复您的代码
.....
var visitor = obj.Visitor;
var checkin = obj.CheckIn;
var checkout = obj.CheckOut;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
_context.Add(visitor);
await _context.SaveChangesAsync();
.....
但是恕我直言,您不需要 2 张桌子 - 入住和退房。只需一个带有 IsCheckOut bool flag
的 CheckInOut 就足够了
public class CheckInOut
{
[Key]
public int Id { get; set; }
public bool IsCheckOut { get; set; }
[DisplayName("Time")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime Time { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public virtual Visitor Visitor { get; set; }
}
我通过如下更新 POST 方法解决了这个问题:
已更改:
escort = await _context.Escorts
.Include(c => c.CheckIn)
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
收件人:
escort = await _context.Escorts
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
我正在使用 ASP.NET Core (MVC) 创建 Web 应用程序。数据库是 SQL 服务器。代码优先设计。我是 EF 的新手。
我定义了 Visitor
模型,Checkin
和 Checkout
模型,visitor/checkin 和 1:1 的关系为 1:1 visitor/checkout。该应用程序的想法是,当创建新访客时,将创建新的 check-in 和新的 check-out 记录,并 children 访客记录。
我的模型如下(我使用ForeignKey
数据注解来定义访客和签到以及访客和签出之间的one-to-one关系):
public class Visitor
{
[Key]
public int Id { get; set; }
[DisplayName("Visitor First Name")]
[Required]
public string FirstName { get; set; }
[DisplayName("Visitor Last Name")]
[Required]
public string LastName { get; set; }
public CheckIn CheckIn { get; set; }
public CheckOut CheckOut { get; set; }
}
public class CheckIn
{
[ForeignKey("Visitor")]
public int Id { get; set; }
[DisplayName("Time In")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime Time { get; set; }
public Visitor Visitor { get; set; }
}
public class CheckOut
{
[ForeignKey("Visitor")]
public int Id { get; set; }
[DisplayName("Time Out")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime? Time { get; set; }
public Visitor Visitor { get; set; }
}
我的控制器中有一个 POST 方法,它应该在数据库中添加一个新的访客以及一个新的签入和签出记录作为访客记录的 children。
但是,我 运行 遇到签入记录未添加到数据库的问题:而是更新了数据库中的现有签入记录,因此其主键是新记录的键 (ID)访客记录。新访客记录创建成功。此外,新的结帐记录也已正确创建并作为新访客记录的 child。这是我的控制器中 POST 方法的代码(删除了一些与此问题无关的代码)(也使用视图模型将数据从视图传递到控制器):
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ConfirmSignin(SignVM obj)
{
if (ModelState.IsValid)
{
var visitor = new Visitor();
var checkin = new CheckIn();
var checkout = new CheckOut();
checkin = obj.CheckIn;
checkout = obj.CheckOut;
visitor = obj.Visitor;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
_context.Add(visitor);
_context.Add(checkin);
_context.Add(checkout);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index),"Home",new { LocationId = obj.Visitor.LocationId });
}
return View(obj);
}
在数据库中成功创建访客:
结帐也已在数据库中成功创建:
您会注意到结帐记录的 ID 与 parent 记录访客的 ID 匹配 - 所以这部分似乎工作正常。
但是在数据库中添加新访客时没有添加签到记录:
当我添加访问者 ID=6 时,应该在数据库中添加新的签入记录 - 而 EF 更新了现有的签入记录并将其主键从 5(先前的访问者)更新为 6(新的访问者)。
我假设我在控制器代码中做错了什么。有什么建议吗?
更新 - 2021 年 4 月 28 日: 下面是对应GET方法的代码:
[HttpGet]
public IActionResult ConfirmSignin()
{
SignVM signVM = new();
if (TempData["NewSignIn"] is string s)
{
signVM = JsonConvert.DeserializeObject<SignVM>(s);
}
ViewData["LocationName"] = new SelectList(_context.Locations, "Id", "Name", signVM.Visitor.LocationId);
return View(signVM);
}
下面是 POST 方法更新后的完整代码:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ConfirmSignin(SignVM obj)
{
if (ModelState.IsValid)
{
var escort = new Escort();
escort = await _context.Escorts
.Include(c => c.CheckIn)
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
if (escort == null)
{
escort = obj.Escort;
_context.Add(escort);
await _context.SaveChangesAsync();
}
var visitor = obj.Visitor;
var checkin = obj.CheckIn;
var checkout = obj.CheckOut;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
escort.CheckIn = new List<CheckIn>();
escort.CheckOut = new List<CheckOut>();
escort.CheckIn.Add(checkin);
escort.CheckOut.Add(checkout);
_context.Add(visitor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index),"Home",new { LocationId = obj.Visitor.LocationId });
}
return View(obj);
}
这是对应视图的代码:
@model VisitorLog.Models.ViewModels.SignVM
@{
ViewData["Title"] = "Sign In";
}
<div class="row justify-content-center mt-3">
<div class="col-md-4">
<div class="h4 text-center">Confirm Sign In</div>
<hr />
<form asp-action="ConfirmSignin">
<div class="form-group">
<label asp-for="Visitor.LocationId" class="control-label"></label>
<select asp-for="Visitor.LocationId" class="form-control" disabled asp-items="ViewBag.LocationName"></select>
</div>
<div class="form-group">
<label asp-for="Visitor.FullName" class="control-label"></label>
<input asp-for="Visitor.FullName" class="form-control" readonly value="@Model.Visitor.FullName" />
</div>
<div class="form-group">
<label asp-for="Visitor.Company" class="control-label"></label>
<input asp-for="Visitor.Company" class="form-control" readonly value="@Model.Visitor.Company" />
</div>
<div class="form-group">
<label asp-for="Escort.EmployeeId" class="control-label"></label>
<input asp-for="Escort.EmployeeId" class="form-control" readonly value="@Model.Escort.EmployeeId" />
</div>
<div class="form-group">
<label asp-for="Escort.FullName" class="control-label"></label>
<input asp-for="Escort.FullName" class="form-control" readonly value="@Model.Escort.FullName" />
</div>
<div class="form-group">
<label asp-for="CheckIn.Time" class="control-label"></label>
<input asp-for="CheckIn.Time" class="form-control" readonly type="text" value="@Model.CheckIn.Time.ToString("g")" />
</div>
<div class="form-group">
<label asp-for="CheckOut.ExpectedTime" class="control-label"></label>
<input asp-for="CheckOut.ExpectedTime" class="form-control" readonly type="text" value="@Model.CheckOut.ExpectedTime.ToString("g")" />
</div>
<div class="form-group">
<input asp-for="Visitor.LocationId" class="form-control" hidden value="@Model.Visitor.LocationId" />
</div>
<div class="form-group">
<input asp-for="Visitor.FirstName" class="form-control" hidden value="@Model.Visitor.FirstName" />
</div>
<div class="form-group">
<input asp-for="Visitor.LastName" class="form-control" hidden value="@Model.Visitor.LastName" />
</div>
<div class="form-group">
<input asp-for="Visitor.SignedIn" class="form-control" hidden value="@Model.Visitor.SignedIn" />
</div>
<div class="form-group">
<input asp-for="Escort.FirstName" class="form-control" hidden value="@Model.Escort.FirstName" />
</div>
<div class="form-group">
<input asp-for="Escort.LastName" class="form-control" hidden value="@Model.Escort.LastName" />
</div>
<div class="form-group">
<input asp-for="Escort.EmailAddress" class="form-control" hidden value="@Model.Escort.EmailAddress" />
</div>
<div class="form-group">
<input asp-for="Escort.Phone" class="form-control" hidden value="@Model.Escort.Phone" />
</div>
<div class="form-group text-center mt-4">
<input type="submit" class="btn btn-primary col-5" value="Confirm" />
<a class="btn btn-danger col-5 offset-1" asp-controller="Visitors" asp-action="SignIn" asp-route-LocationId=@Context.Request.Query["LocationId"]>Back</a>
</div>
</form>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
你必须修复你的 类:
public class CheckIn
{
[Key]
public int Id { get; set; }
[DisplayName("Time In")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime Time { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public virtual Visitor Visitor { get; set; }
}
public class CheckOut
{
[Key]
public int Id { get; set; }
[DisplayName("Time Out")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime? Time { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public virtual Visitor Visitor { get; set; }
}
并修复您的代码
.....
var visitor = obj.Visitor;
var checkin = obj.CheckIn;
var checkout = obj.CheckOut;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
_context.Add(visitor);
await _context.SaveChangesAsync();
.....
但是恕我直言,您不需要 2 张桌子 - 入住和退房。只需一个带有 IsCheckOut bool flag
的 CheckInOut 就足够了public class CheckInOut
{
[Key]
public int Id { get; set; }
public bool IsCheckOut { get; set; }
[DisplayName("Time")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:g}")]
public DateTime Time { get; set; }
public int VisitorId { get; set; }
[ForeignKey("VisitorId")]
public virtual Visitor Visitor { get; set; }
}
我通过如下更新 POST 方法解决了这个问题:
已更改:
escort = await _context.Escorts
.Include(c => c.CheckIn)
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
收件人:
escort = await _context.Escorts
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);