【问题标题】:Auto increment for different prefixes - EF不同前缀的自动递增 - EF
【发布时间】:2018-02-27 09:10:05
【问题描述】:

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

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

因此,虽然在 A 国,最新的发票编号可能是 2018002345,但在 B 国,有人应该能够创建 ID 为 201800001 以及之后为 201800002 的发票。

我认为默认的[Key] 列当然不适合这个?

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

所以我正在寻找一种正确的方法来处理这种情况。它是一个现有的生产应用程序,因此无法突然更改 Key 列,如果更改它们,则需要迁移。

我想避免我首先必须调用数据库以检索最后一个键,然后插入一个递增的键的情况,这会产生问题并且无法扩展。

数据库是使用 Code-First 迁移生成的。我更喜欢可以使用代码优先注释的解决方案。但是,还有第二个项目使用了没有实体框架的数据库,它使用的是 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; }
}

【问题讨论】:

  • 发票没有空白是否关键?例如,201800001 后面可以跟201800013 而不是201800002我问,如果是这样,问题就变得方式容易了。
  • 根据您的描述,我假设两个国家/地区可能有相同编号的发票?如果是这样,您如何在不更改键列的情况下处理此问题,据我所知,这是唯一的?
  • @Evk 谈到了这里的主要问题。您不想更改主键列,但也希望有重复的键。要做到这一点,至少需要将表更改为具有一个组合键,该键将新的非唯一发票编号与前缀值组合在一起,如果不是对表结构进行更大的重新设计
  • 让我澄清一下,是的,2 个国家/地区可能有相同的发票编号,这就是为什么我认为主键列不是选项(除非使用前缀完成的操作可以处理此问题)。 @mjwills 理想的解决方案是没有,没有差距,但是如果您有一个肯定不会重复的可行解决方案,那么这可能会奏效。
  • @iamdave 我想指出更改主键列涉及迁移,这没关系,但这只是在发布解决方案之前需要考虑的事情吗? :)

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


【解决方案1】:

作为服务器端解决方案,您可以在发票表上使用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;

【讨论】:

  • 有趣!你有触发器的例子吗?
  • 我在 aswer 中添加了一个示例
  • 有趣的是,与此同时我也在做同样的事情。现在没有对此进行测试,但看起来我们在同一页面上,谢谢!
猜你喜欢
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 2016-06-26
相关资源
最近更新 更多