[英]Updating existing child instead of adding new one
I am creating a Web app with ASP.NET Core (MVC).我正在使用 ASP.NET 核心 (MVC) 创建一个 Web 应用程序。 Database is SQL Server.
数据库是 SQL 服务器。 Code First design.
代码优先设计。 I am new to EF.
我是 EF 的新手。
I have defined Visitor
model, a Checkin
and a Checkout
model with a relationship of 1:1 for visitor/checkin and 1:1 for visitor/checkout.我已经定义了
Visitor
model、 Checkin
入和Checkout
model,访客/签入的关系为 1:1,访客/签出的关系为 1:1。 The idea of the app is when a new visitor is created, a new check-in and new check-out records are created and children of the visitor record.该应用程序的想法是在创建新访客时,创建新的签到和新的签出记录以及访客记录的子项。
My models are as follows (I am using the ForeignKey
data annotation to define the one-to-one relationship between visitor and checkin and between visitor and checkout):我的模型如下(我使用
ForeignKey
数据注解来定义visitor和checkin、visitor和checkout的一一对应关系):
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; }
}
I have a POST method in my controller that should add a new visitor in the database as well as a new checkin and checkout record as children of the visitor record.我的 controller 中有一个 POST 方法,它应该在数据库中添加一个新的访问者以及一个新的签入和签出记录作为访问者记录的子项。
However, I am running into issues where the checkin record is not added to the database: instead an existing checkin record in the DB is updated so its primary key is the key (ID) of the new visitor record.但是,我遇到了未将签入记录添加到数据库的问题:而是更新了数据库中现有的签入记录,因此其主键是新访问者记录的键 (ID)。 The new visitor record is created OK.
新访客记录创建成功。 Also the new checkout record is also created correctly and as a child of the new visitor record.
此外,新的结帐记录也被正确创建并作为新访客记录的子项。 Here is the code of the POST method from my controller (removed some code not relevant to this question) (also using a viewmodel to pass data from the view to the controller):
这是我的 controller 中的 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);
}
Visitors are created successfully in the database:访问者在数据库中创建成功:
Checkouts are also created successfully in the database:结帐也在数据库中成功创建:
You will notice that the ID of the checkouts records match the ID of the parent record Visitor - so this part seems to be working fine.您会注意到结帐记录的 ID 与父记录访问者的 ID 匹配 - 所以这部分似乎工作正常。
But the checkin record is not added when a new visitor is added in the database:但是在数据库中添加新访客时不会添加签到记录:
A new checkin record should have been added in the database when I added visitor ID=6 - instead EF updated an existing checkin record and updated its primary key from 5 (previous visitor) to 6 (new visitor).当我添加访问者 ID=6 时,应该在数据库中添加了一条新的签到记录 - 相反,EF 更新了现有的签到记录并将其主键从 5(上一个访问者)更新为 6(新访问者)。
I assume I am doing something wrong in the controller code.我假设我在 controller 代码中做错了什么。 Any suggestion?
有什么建议吗?
UPDATE - 4/28/2021: Below is the code for the corresponding GET method:更新 - 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);
}
Below is the updated full code of the POST method:下面是更新后的 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);
}
And this is the code of the corresponding view:这是相应视图的代码:
@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");}
}
you have to fix your classes:你必须修复你的课程:
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; }
}
and fix your code并修复你的代码
.....
var visitor = obj.Visitor;
var checkin = obj.CheckIn;
var checkout = obj.CheckOut;
visitor.CheckIn = checkin;
visitor.CheckOut = checkout;
_context.Add(visitor);
await _context.SaveChangesAsync();
.....
But IMHO you don't need 2 tables - checkin and checkout.但恕我直言,您不需要 2 张桌子 - 签到和结帐。 It would be enough just one CheckInOut with IsCheckOut bool flag
一个带有 IsCheckOut 布尔标志的 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; }
}
I resolved the issue by updating the POST method as follows:我通过更新 POST 方法解决了这个问题,如下所示:
Changed:改变:
escort = await _context.Escorts
.Include(c => c.CheckIn)
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
To:至:
escort = await _context.Escorts
.FirstOrDefaultAsync(m => m.EmployeeId == obj.Escort.EmployeeId);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.