C# 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方法,

我正在使用ASP.NET核心(MVC)创建一个Web应用程序。数据库是SQL Server。代码优先设计。我是EF的新手

我已经定义了
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);