我的EF Core模型定义为:
namespace TestApp.DataAccess.Models {
public class Candidate
{
public int CandidateId { get; set; }
public Guid UniqueKey { get; set; }
public string Name { get; set; }
public virtual List<Job> Jobs { get; set; }
}
public class Job
{
public int JobId { get; set; }
public Guid UniqueKey { get; set; }
public int CandidateId { get; set; }
public virtual Candidate Candidate { get; set; }
public string Title { get; set; }
}
}
CandidateId
和JobId
是主键。
两个实体还具有一个UniqueKey
属性,它是Guid。这是由我们的客户生成的,并已发布到请求正文中的API。我们永远不应以相同的Job
属性获得多个UniqueKey
。
理想情况下,数据库应该使用唯一的约束来强制执行此操作,但是目前尚不存在。
相反,在我们的控制器中,我们检查UniqueKey
是否已经存在。如果没有,我们将创建一个新的Job
。如果是这样,那么我们将更新现有记录:
foreach (var jobModel in model.Jobs) {
//Check if the job already exists for the entity
var jobEntity = candidate.Jobs.FirstOrDefault(x => x.UniqueKey == jobModel.UniqueKey);
//If not, then create it
if (jobEntity == null) {
jobEntity = new Job { UniqueKey = jobModel.UniqueKey };
candidate.Jobs.Add(jobEntity);
}
jobEntity.Title = jobModel.Title;
//...
}
最近,我开始看到重复的Jobs
:
JobId CandidateId Title UniqueKey
201 100 Teacher 4177b6da-7a4c-4032-b13d-8e3e2d2aeaca
202 100 Teacher 4177b6da-7a4c-4032-b13d-8e3e2d2aeaca
我认为以上代码无法实现。在将唯一性约束应用于基础SQL数据库之前,我试图了解这种情况是如何发生的,以确保我不会只是简单地处理更大的问题。
什么会导致创建这两个记录?可能与重复的请求同时到达我们的API端点有关吗?
这是完整的代码示例:
namespace TestApp.DataAccess.Models {
public class Candidate
{
public int CandidateId { get; set; }
public Guid UniqueKey { get; set; }
public string Name { get; set; }
public virtual List<Job> Jobs { get; set; }
}
public class Job
{
public int JobId { get; set; }
public Guid UniqueKey { get; set; }
public int CandidateId { get; set; }
public virtual Candidate Candidate { get; set; }
public string Title { get; set; }
}
}
namespace TestApp.Api.Controllers {
[Route("api/[controller]")]
public class CandidateController : BaseController {
public ServerApplicationContext _context { get; set; }
public CandidateController(ServerApplicationContext context) {
_context = context;
}
public async Task<Candidate> findEntityOrDefault(Guid key) {
if(entity == null) {
return null;
}
}
[HttpPost]
public async Task<IActionResult> Post([FromBody]CandidateViewModel model) {
//Load the existing entity
var candidate = await _context.Candidates.FirstOrDefaultAsync(x => x.UniqueKey == key);
//If we don't find a candidate then create one
if(entity == null) {
//...
//candidate = new Candidate { ... }
//...
} else {
//Else load in child properties for the existing candidate
candidate.Jobs = await _context.Jobs.Where(x => x.CandidateId == entity.CandidateId).ToListAsync();
return entity;
}
//Add jobs from the model to our entity
foreach (var jobModel in model.Jobs) {
//Check if the job already exists for the entity
var jobEntity = candidate.Jobs.FirstOrDefault(x => x.UniqueKey == jobModel.UniqueKey);
//If not, then create it
if (jobEntity == null) {
jobEntity = new Job { UniqueKey = jobModel.UniqueKey };
candidate.Jobs.Add(jobEntity);
}
jobEntity.Title = jobModel.Title;
//...
}
//Add or update the candidate to the DB
if (candidate.CandidateId == 0)
_context.Add(candidate);
else
_context.Update(candidate);
//Commit changes
await _context.SaveChangesAsync();
var viewModel = Mapper.Map<CandidateViewModel>(candidate);
return new ObjectResult(viewModel);
}
}
}