У меня есть модель 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);
}
}
}