如何在EF Core+PostgreSQL项目中实现连续cID编号
实现跨批次连续的cID(EF Core + PostgreSQL)
需求说明
每月定时将PostgreSQL数据库中的Institution数据序列化发送到消息队列,要求每次发送的记录拥有跨批次连续的cID:比如第一次发100条用1-100,下月再发这些记录时cID从101开始到200,以此类推。当前代码仅能从1开始编号,需要基于PostgreSQL序列实现连续编号。
现有模型与代码
实体模型
[Table("institutions")] public partial class Institution { public Institution() { InstitutionResources = new HashSet<InstitutionResource>(); } [Key] [Column("id")] public int Id { get; set; } public int AddressId { get; set; } [Column("phone")] [StringLength(20)] public string Phone { get; set; } [Column("email_address")] [StringLength(255)] public string EmailAddress { get; set; } [ForeignKey(nameof(AddressId))] [InverseProperty("Institutions")] public virtual Address Address { get; set; } [InverseProperty(nameof(InstitutionResource.Institution))] public virtual ICollection<InstitutionResource> InstitutionResources { get; set; } }
当前生成cID的代码
public List<InstitutionDataResult> FormInstitutionDataResult() { long cID = 1; // 这里需要改为跨批次连续的起始值 var institutions = _dbContext.Institutions .Select(s => new InstitutionDataResult { Id = s.Id, NameKk = s.InstitutionResources.Where(l => l.LanguageId == 1).SingleOrDefault().Name, NameRu = s.InstitutionResources.Where(l => l.LanguageId == 2).SingleOrDefault().Name, Geoposition = s.Address.Geoposition.ToString(), Phone = s.Phone, Email = s.EmailAddress, Date = DateTime.Now.ToString("dd.MM.yyyy") }).ToList(); foreach (var institution in institutions) { institution.CID = cID; cID++; } return institutions; // 原代码中返回crosswalks为笔误,修正为institutions }
解决方案:使用PostgreSQL序列实现连续编号
步骤1:在PostgreSQL中创建专用序列
执行以下SQL语句,创建一个持久化的序列用于记录cID的当前计数:
-- 创建序列,起始值1,每次递增1 CREATE SEQUENCE cid_sequence START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
步骤2:修改C#代码实现连续编号逻辑
核心逻辑:每次发送前从数据库获取序列当前值作为起始cID;分配完所有记录的cID后,将序列值更新为「起始cID + 本次发送记录数」,确保下次发送的起始值连续。
修改后的代码:
public List<InstitutionDataResult> FormInstitutionDataResult() { // 1. 获取序列当前值作为起始cID,处理序列刚创建的特殊情况 long startCid; try { startCid = _dbContext.Database.SqlQuery<long>("SELECT currval('cid_sequence');").FirstOrDefault(); } catch { // 序列刚创建时currval会报错,先获取nextval再回退到初始值1 startCid = _dbContext.Database.SqlQuery<long>("SELECT nextval('cid_sequence');").FirstOrDefault(); _dbContext.Database.ExecuteSqlRaw("SELECT setval('cid_sequence', {0});", startCid - 1); } var institutions = _dbContext.Institutions .Select(s => new InstitutionDataResult { Id = s.Id, NameKk = s.InstitutionResources.Where(l => l.LanguageId == 1).SingleOrDefault()?.Name, NameRu = s.InstitutionResources.Where(l => l.LanguageId == 2).SingleOrDefault()?.Name, Geoposition = s.Address.Geoposition.ToString(), Phone = s.Phone, Email = s.EmailAddress, Date = DateTime.Now.ToString("dd.MM.yyyy") }).ToList(); // 2. 为每条记录分配连续cID long currentCid = startCid; foreach (var institution in institutions) { institution.CID = currentCid; currentCid++; } // 3. 更新序列值,保证下次起始值连续 _dbContext.Database.ExecuteSqlRaw("SELECT setval('cid_sequence', {0});", currentCid); return institutions; }
关键补充
- 原子性保障:PostgreSQL序列本身是原子性操作,多并发请求也不会出现重复或跳号问题。
- 异常回滚:如果发送过程中出现异常,需要用事务包裹整个流程,回滚序列值避免计数断层:
using var transaction = _dbContext.Database.BeginTransaction(); try { // 上述获取起始cID、分配cID、更新序列的代码 transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); // 处理异常逻辑 throw; }
- 持久化特性:序列值存储在数据库中,服务或数据库重启后不会丢失计数,确保跨批次的连续性。
内容的提问来源于stack exchange,提问作者Мади Маликов
相关产品推荐
相关产品推荐

