You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在EF Core中为电子发票系统实现按供应商维度的自增整数列?

解决方案:实现供应商维度的发票自增编号

这是一个非常合理的需求——分维度的自增编号确实能大幅提升特定场景下的检索效率,同时优化打印等用户体验。下面是针对你的EF Core配置和需求的完整实现方案:

1. 修改Invoice实体结构

首先需要在Invoice类中新增两个关键属性:显式的供应商外键(方便后续逻辑处理)和供应商维度的自增编号:

public class Invoice
{
    public Guid Id { get; set; } // 全局GUID主键(保留)
    public string Title { get; set; }
    public string Description { get; set; }
    public DateTime Date { get; set; }
    public decimal TotalValue { get; set; }
    public decimal TaxValue { get; set; }
    
    // 显式供应商外键(建议添加,简化关联逻辑)
    public Guid ProviderId { get; set; }
    public Provider Provider { get; set; }
    
    public Taker Taker { get; set; }
    public ServiceType ServiceType { get; set; }
    
    // 新增:供应商维度的自增发票编号
    public int ProviderInvoiceNumber { get; set; }
}

2. 更新EF Core实体配置

接下来调整你的EntityTypeBuilder配置,添加组合唯一索引(确保同一供应商的编号不会重复),并优化导航属性关联:

public void Configure(EntityTypeBuilder<Invoice> builder)
{
    builder.HasKey(x => x.Id); // 保留GUID主键
    
    // 原有必填属性配置不变
    builder.Property(x => x.Title)
        .IsRequired()
        .HasMaxLength(100);
    builder.Property(x => x.Description)
        .IsRequired();
    builder.Property(x => x.Date)
        .IsRequired();
    builder.Property(x => x.TotalValue)
        .IsRequired();
    builder.Property(x => x.TaxValue)
        .IsRequired();
    
    // 显式配置供应商关联(假设Provider类有对应的Invoices集合)
    builder.HasOne(x => x.Provider)
        .WithMany(p => p.Invoices)
        .HasForeignKey(x => x.ProviderId)
        .OnDelete(DeleteBehavior.Restrict); // 根据业务需求调整删除规则
    
    builder.HasOne(x => x.Taker);
    builder.HasOne(x => x.ServiceType);
    
    // 关键:添加ProviderId + ProviderInvoiceNumber的组合唯一索引
    // 确保同一供应商下的发票编号唯一,同时提升查询效率
    builder.HasIndex(x => new { x.ProviderId, x.ProviderInvoiceNumber })
        .IsUnique();
}

3. 实现分维度自增逻辑

EF Core没有内置的“按分组自增”功能,所以需要选择以下两种方案之一:

方案A:数据库触发器(推荐高并发场景)

通过数据库触发器在插入时自动计算当前供应商的最大编号并加1,以SQL Server为例:

CREATE TRIGGER trg_Invoice_GenerateProviderInvoiceNumber
ON Invoices
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;
    
    INSERT INTO Invoices (Id, Title, Description, Date, TotalValue, TaxValue, ProviderId, TakerId, ServiceTypeId, ProviderInvoiceNumber)
    SELECT 
        i.Id,
        i.Title,
        i.Description,
        i.Date,
        i.TotalValue,
        i.TaxValue,
        i.ProviderId,
        i.TakerId,
        i.ServiceTypeId,
        -- 取当前供应商的最大编号,无则从1开始
        COALESCE((SELECT MAX(ProviderInvoiceNumber) FROM Invoices WHERE ProviderId = i.ProviderId), 0) + 1
    FROM inserted i;
END

优点:逻辑在数据库层面,无需应用层额外处理,高并发下冲突概率低;缺点:调试和跨数据库迁移需要适配不同语法。

方案B:应用层事务处理(适合低并发场景)

在插入发票前,通过事务查询当前供应商的最大编号并赋值,避免并发冲突:

public async Task<Invoice> CreateInvoiceAsync(Invoice newInvoice)
{
    if (newInvoice.ProviderId == Guid.Empty)
        throw new ArgumentException("必须指定供应商ID");
    
    // 使用事务包裹查询和插入操作,避免并发导致的编号重复
    using (var transaction = await _dbContext.Database.BeginTransactionAsync())
    {
        try
        {
            // 获取当前供应商的最大发票编号,无则返回0
            var maxNumber = await _dbContext.Invoices
                .Where(i => i.ProviderId == newInvoice.ProviderId)
                .MaxAsync(i => (int?)i.ProviderInvoiceNumber) ?? 0;
            
            newInvoice.ProviderInvoiceNumber = maxNumber + 1;
            
            _dbContext.Invoices.Add(newInvoice);
            await _dbContext.SaveChangesAsync();
            
            await transaction.CommitAsync();
            return newInvoice;
        }
        catch (Exception)
        {
            await transaction.RollbackAsync();
            throw;
        }
    }
}

优点:逻辑直观,易于维护;缺点:高并发下可能出现冲突,需要额外添加重试机制。

4. 优化检索逻辑

有了组合索引后,你可以快速通过“供应商ID + 发票编号”查询目标发票,效率远高于GUID检索:

public async Task<Invoice> GetInvoiceByProviderNumberAsync(Guid providerId, int invoiceNumber)
{
    return await _dbContext.Invoices
        .FirstOrDefaultAsync(i => i.ProviderId == providerId && i.ProviderInvoiceNumber == invoiceNumber);
}

这样用户只需输入简洁的供应商发票编号,就能快速定位到目标记录,打印时也可以直接使用这个编号替代冗长的GUID。

内容的提问来源于stack exchange,提问作者Gabriel Francisco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 11:57:28