不同前缀的自动增量 - EF

时间:2018-02-27 09:10:06

标签: c# sql-server entity-framework ef-code-first

所以我有一张发票的表格。目前,Key列用作发票号。它会自动递增,因为它是一个关键列。

现在该应用程序将被不同国家的不同公司使用。因此,如果两者都创建发票,则发票编号不正确。

因此,在国家A中,最新发票编号可能是2018002345,在国家B中,某人应该能够创建Id 201800001及其后201800002的发票。

当然,默认[Key]列不适用于此,我想?

除此之外,最好为它们添加前缀,因此前缀包含国家代码:NL20180002345和US2018000001。 (当前缀是唯一的时,也许可以使用Key列?)。 在完美的情况下,数字增加+1,但如果这非常困难,那么具有99%概率的解决方案是可接受的。

所以我正在寻找一种正确的方法来处理这种情况。它是一个现有的和生产中的应用程序,因此无法改变密钥列是不可能的,如果更改它们将需要迁移。

我想避免我首先要调用数据库来检索最后一个密钥,然后插入一个递增的密钥,这会产生问题但不会扩展。

使用代码优先迁移生成数据库。我更喜欢可以使用代码优先注释的解决方案。但是,有第二个项目使用数据库而没有实体框架,它正在使用Dapper。那么另一方面,MSSQL解决方案也可以很好用吗?

当前实体:

发票

public class Invoice
{
    public int InvoiceId { get; set; }
    public int ContactId { get; set; }
    public int BillingAddressId { get; set; }
    public int? OrderId { get; set; }
    public int? RefundInvoiceId { get; set; }
    public DateTime Date { get; set; }
    //more properties
    [Timestamp]
    public byte[] Timestamp { get; set; }
    public DateTime? PayDate { get; set; }
    public PaymentStatusEnum PaymentStatus { get; set; }
    [ForeignKey("BillingAddressId")]
    public Address BillingAddress { get; set; }
}

因此它链接到一个地址,该地址链接到一个包含国家/地区代码的国家/地区。

地址

public class Address
{
    public int addressId { get; set; }
    public int countryId { get; set; }
    //more properties
    public bool validated { get; set; }
    public AddressTypeEnum addressType { get; set; }
    public Country Country { get; set; }
}

国家

public class Country
{
    public int CountryId { get; set; }
    public string CountryCode { get; set; }
    public string CountryName { get; set; }
    public double Taxpercentage { get; set; }
    public int CurrencyId { get; set; }
    [ForeignKey("CurrencyId")]
    public Currency Currency { get; set; }
}

1 个答案:

答案 0 :(得分:2)

作为服务器端解决方案,您可以在发票表上使用insert trigger,并在触发器代码中配备字段值。您可以使用(sequence objects)生成数字,这两个国家可能有两个不同的序列可能适合您。您的发票ID字段可以是nvarchar字符串,因此您的触发器代码也可以应用国家/地区前缀。

然后,在客户端,使用DatabaseGenerated属性标记该字段,如下所示:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string InvoiceId { get; set; }

当您插入或更新该表时,它指示EF在同一个往返中获取列值(在触发器中创建)。

简化示例:

create table Invoice (
  Id int not null primary key identity(1,1),
  InvoiceId nvarchar(20),
  CountryId int not null
  -- etc.
)

CREATE SEQUENCE InvNo_US 
    START WITH 1000000  
    INCREMENT BY 1;  
GO  

CREATE TRIGGER TriInvoice_AI
ON Invoice AFTER INSERT
AS 
BEGIN
  SET NOCOUNT ON;

  declare @id int, @country_id int, @seq bigint;

  DECLARE c CURSOR LOCAL FAST_FORWARD FOR   
    SELECT i.Id, i.CountryId from inserted i
    ORDER BY i.Id;

  open c;
  fetch next from c into @id, @country_id;

  while @@FETCH_STATUS = 0
  begin
    /* TODO: At this point you may use different sequence object depending on @country_id,
       you may apply different country prefixed and maybe do other specific formatting to the InvoiceId 
       This example always just uses 'US' and doesn't take the @country_id in account at all. */
    set @seq = NEXT VALUE FOR InvNo_US;
    update Invoice set InvoiceId = 'US' + cast(@seq as nvarchar) where Id=@id;
    fetch next from c into @id, @country_id;
  end
  close c;
  deallocate c;
END

像这样测试:

insert into Invoice (CountryId) values (42);
select * from Invoice;