首页 > 解决方案 > 来自 API 控制器的数据库中的重复实体

问题描述

我有一个 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; }
    }
}

WhereCandidateIdJobId是主键。

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

标签: c#sql-server.net-coreasp.net-core-webapief-core-2.2

解决方案


推荐阅读