API控制器中数据库中的重复实体

时间:2019-06-17 15:15:57

标签: c# sql-server .net-core asp.net-core-webapi ef-core-2.2

我的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; }
    }
}

CandidateIdJobId是主键。

两个实体还具有一个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);
        }        
    }
}

0 个答案:

没有答案