字典实现客户ID自动生成时MySQL主键重复异常排查
问题原因分析
咱们先拆解下你遇到的问题:抛出的Duplicate entry '0' for key 'customer.PRIMARY'异常已经很明确——你插入的Id_Cus值是0,而这个主键值已经在数据库里存在了。具体问题出在这两处:
- 新客户的
Id_Cus未被赋值:你注释掉了从customerDataContract获取Id_Cus的代码,但也没通过其他逻辑给新创建的cust对象的Id_Cus设置唯一值,所以它默认是0。第一次插入时数据库里还没有0这个主键,所以成功了;第二次插入时Id_Cus还是0,直接触发主键重复的冲突。 - 你写的字典逻辑完全没发挥作用:你查询了已有邮箱对应的
Id_Cus并存入字典,但后续根本没用到这个字典——既没检查新客户的邮箱是否已存在(复用已有Id),也没根据邮箱生成新的唯一Id,这段代码属于冗余代码。
解决方案
根据你“借助字典根据输入邮箱自动生成Id_Cus”的需求,分两种常见场景给你解决思路:
场景1:邮箱已存在则复用Id,不存在则生成新唯一Id
如果你的业务是同一个邮箱对应同一个客户,那可以先查询邮箱是否存在,再决定是复用还是新建:
public void InsertCustomer(customer customerDataContract) { // 先查询当前邮箱是否已有对应的客户 var existingCustomer = dc.customers.FirstOrDefault(x => x.Email_Cus == customerDataContract.Email_Cus); customer cust; if (existingCustomer != null) { // 邮箱已存在,复用已有Id,可选更新客户其他信息 cust = existingCustomer; cust.FirstName_Cus = customerDataContract.FirstName_Cus; cust.LastName_Cus = customerDataContract.LastName_Cus; cust.PhoneNum_Cus = Convert.ToInt32(customerDataContract.PhoneNum_Cus); } else { // 邮箱不存在,创建新客户并生成唯一Id cust = new customer(); cust.FirstName_Cus = customerDataContract.FirstName_Cus; cust.LastName_Cus = customerDataContract.LastName_Cus; cust.PhoneNum_Cus = Convert.ToInt32(customerDataContract.PhoneNum_Cus); cust.Email_Cus = customerDataContract.Email_Cus; // 生成唯一Id:取现有最大Id加1(如果是并发场景建议用数据库自增,这里是简单示例) int maxExistingId = dc.customers.Any() ? dc.customers.Max(x => x.Id_Cus) : 0; cust.Id_Cus = maxExistingId + 1; } // 新客户才需要添加到上下文,现有客户EF会自动跟踪变更 if (existingCustomer == null) { dc.customers.Add(cust); } dc.SaveChanges(); // 后续查询逻辑保留 customer custFromDb = dc.customers.Include(c => c.customerpreference) .Include(c => c.orders) .Include(c => c.dishesrankings) .First(n => n.Id_Cus == cust.Id_Cus); }
场景2:把Id_Cus设为自增主键(更推荐的生产级做法)
如果你的业务允许Id_Cus是无意义的唯一标识,那直接把数据库的Id_Cus字段设为AUTO_INCREMENT(MySQL),同时在EF模型里标记为自增,这样根本不需要手动赋值:
- 数据库端:修改
customer表的Id_Cus列,设置为PRIMARY KEY AUTO_INCREMENT - EF模型端:给
Id_Cus字段添加特性(或者用Fluent API配置):[DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id_Cus { get; set; } - 简化后的插入代码:
public void InsertCustomer(customer customerDataContract) { customer cust = new customer(); cust.FirstName_Cus = customerDataContract.FirstName_Cus; cust.LastName_Cus = customerDataContract.LastName_Cus; cust.PhoneNum_Cus = Convert.ToInt32(customerDataContract.PhoneNum_Cus); cust.Email_Cus = customerDataContract.Email_Cus; dc.customers.Add(cust); dc.SaveChanges(); // 保存后,EF会自动把数据库生成的自增Id赋值给cust.Id_Cus customer custFromDb = dc.customers.Include(c => c.customerpreference) .Include(c => c.orders) .Include(c => c.dishesrankings) .First(n => n.Id_Cus == cust.Id_Cus); }
这种方式能避免手动生成Id时的并发冲突,可靠性更高。
额外提示
- 原代码里的字典逻辑完全没被使用,可以直接删除。
- 用
Convert.ToInt32处理电话号码要谨慎:如果输入的号码包含非数字字符(比如+86、-)会抛出转换异常,建议改用字符串类型存储电话号码(更合理,因为电话号码一般不需要数值运算)。
内容的提问来源于stack exchange,提问作者Кирилл Филободченко
相关产品推荐
相关产品推荐

