C# EF核心:更新现有子级而不是添加新子级
我正在使用ASP.NET核心(MVC)创建一个Web应用程序。数据库是SQL Server。代码优先设计。我是EF的新手 我已经定义了C# EF核心:更新现有子级而不是添加新子级,c#,sql-server,asp.net-mvc,entity-framework,one-to-one,C#,Sql Server,Asp.net Mvc,Entity Framework,One To One,我正在使用ASP.NET核心(MVC)创建一个Web应用程序。数据库是SQL Server。代码优先设计。我是EF的新手 我已经定义了Visitor模型、Checkin和Checkout模型,其中Visitor/Checkin和Visitor/Checkout的关系为1:1。该应用程序的理念是,当创建一个新访客时,会创建一个新的入住和退房记录,并创建访客记录的子项 我的模型如下(我使用ForeignKey数据注释来定义访客和签入以及访客和签出之间的一对一关系): 我的控制器中有一个POST方法,
Visitor
模型、Checkin
和Checkout
模型,其中Visitor/Checkin和Visitor/Checkout的关系为1:1。该应用程序的理念是,当创建一个新访客时,会创建一个新的入住和退房记录,并创建访客记录的子项
我的模型如下(我使用ForeignKey
数据注释来定义访客和签入以及访客和签出之间的一对一关系):
我的控制器中有一个POST方法,它应该在数据库中添加一个新的访问者,以及一个新的签入和签出记录作为访问者记录的子项
但是,我遇到了签入记录未添加到数据库的问题:而是更新数据库中的现有签入记录,使其主键为新访问者记录的键(ID)。新的访客记录创建正常。此外,新的签出记录也被正确创建,并作为新访客记录的子记录。以下是我的控制器中POST方法的代码(删除了一些与此问题无关的代码)(还使用viewmodel将数据从视图传递给控制器):
[HttpPost]
[ValidateAntiForgeryToken]
公共异步任务确认签名(SignVM obj)
{
if(ModelState.IsValid)
{
var visitor=新访问者();
var checkin=新签入();
var checkout=新签出();
签入=对象签入;
checkout=obj.checkout;
访客=对象访客;
visitor.CheckIn=签入;
visitor.CheckOut=结帐;
_添加(访问者);
_添加(签入);
_添加(签出);
wait_context.SaveChangesAsync();
返回重定向操作(nameof(Index),“Home”,新的{LocationId=obj.Visitor.LocationId});
}
返回视图(obj);
}
在数据库中成功创建访问者:
也会在数据库中成功创建签出:
您会注意到签出记录的ID与父记录访问者的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);
}
[HttpGet]
公共IActionResult ConfirmSignin()
{
SignVM SignVM=new();
if(TempData[“NewSignIn”]是字符串s)
{
signVM=JsonConvert.DeserializeObject;
}
ViewData[“LocationName”]=新的选择列表(\u context.Locations,“Id”,“Name”,signVM.Visitor.LocationId);
返回视图(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);
}
[HttpPost]
[ValidateAntiForgeryToken]
公共异步任务确认签名(SignVM obj)
{
if(ModelState.IsValid)
{
var-escort=新的escort();
护送=等待_上下文护送
.包括(c=>c.签入)
.FirstOrDefaultAsync(m=>m.EmployeeId==obj.Escort.EmployeeId);
如果(护送==null)
{
护送=目标护送;
_添加(护送);
wait_context.SaveChangesAsync();
}
var visitor=对象visitor;
var checkin=obj.checkin;
var checkout=obj.checkout;
visitor.CheckIn=签入;
visitor.CheckOut=结帐;
escort.CheckIn=新列表();
escort.CheckOut=新列表();
护送。签入。添加(签入);
护送。签出。添加(签出);
_添加(访问者);
wait_context.SaveChangesAsync();
返回重定向操作(nameof(Index),“Home”,新的{LocationId=obj.Visitor.LocationId});
}
返回视图(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");}
}
@model VisitorLog.Models.ViewModels.SignVM
@{
ViewData[“Title”]=“登录”;
}
确认登录
返回
@节脚本{
@{wait Html.RenderPartialAsync(“_validationScript”);}
}
您必须修复您的类:
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();
.....
但是我想你不需要两张桌子——签到和结账。只要用IsCheckOut bool标志签一次就足够了
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);
致:
非常感谢你的建议,谢尔盖。不幸的是,我仍然看到相同的行为(儿童签入不是
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; }
}
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);