正确生成唯一的发票ID

时间:2014-08-04 16:48:37

标签: c# asp.net-mvc multithreading entity-framework

我被要求清理其他人的控制器代码,这会产生发票,而且我遇到了一些我不知道如何修复的问题。有问题的代码如下(这是使用EF 6:代码优先):

var invid = db.TransportJobInvoice.Where(c => c.CompanyId == CompanyId)
                .Max(i => i.InvoiceId);
var invoiceId = invid == null ? 1 : (int)invid + 1;

代码应该根据为其创建发票的公司生成invoiceId。所以这个小表可能如下所示:

------------------------------
| Id | CompanyId | InvoiceId |
------------------------------
|  1 |         1 |         1 |
------------------------------
|  2 |         1 |         2 |
------------------------------
|  3 |         1 |         3 |
------------------------------
|  4 |         2 |         1 |
------------------------------
|  5 |         2 |         2 |
------------------------------

如您所见,invoiceId将根据相关公司的当前发票数生成。但是,我认为在评估此行之前建议两个线程可以执行查询是合理的:

var invoiceId = invid == null ? 1 : (int)invid + 1;

会导致为两张不同的发票生成相同的invoiceId

是否有一个简单的解决方案,可能利用Entity Framework自动执行此操作?

5 个答案:

答案 0 :(得分:3)

我建议使用主键的标识,非常重要!

然后我会为" CustomerInvoiceID"添加一列。并在CustomerID和CustomerInvoiceID"上放置复合唯一键。

然后,创建一个存储过程,在插入后填充CustomerInvoiceID字段,这里有一些伪代码:

CREATE PROCEDURE usp_PopulateCustomerInvoiceID 
    @PrimaryKey INT, --this is your primary key identity column
    @CustomerID INT
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @cnt INT;

    SELECT @CNT = COUNT(1)
    FROM TBL 
    WHERE CustomerID = @CustomerID
        AND PrimaryKeyColumn <= @PrimaryKey

    UPDATE tbl
    SET CustomerInvoiceID = @cnt + 1
    WHERE PrimaryKeyColumn = @PrimaryKey
END

答案 1 :(得分:2)

两种可能性:

服务器端:不要计算客户端上的最大(ID)+1。相反,作为INSERT语句的一部分,通过INSERT..SELECT语句计算max(ID)+1。

客户端:代替增量int,在客户端上生成GUID,并将其用作InvoiceID。

答案 2 :(得分:1)

如果在SQL Server中使用Identity字段,则会自动处理。

答案 3 :(得分:1)

我不知道你是否可以自动生成发票ID,除非它被作为外键威胁(我认为不是)。

使用lock语句可以解决多个线程的问题。

lock (myLock)
{
     var invid = db.TransportJobInvoice.Where(c => c.CompanyId == CompanyId)
            .Max(i => i.InvoiceId);
     var invoiceId = invid == null ? 1 : (int)invid + 1;
}

这将保证只有线程正在执行这些语句。

但要小心,当这些语句并行执行时,这可能会导致性能问题,并且查询需要花费一些时间来执行。

答案 4 :(得分:1)

一种相当不同的方法是为每个NextId创建一个包含CustomerId的单独表格。添加新客户后,您将在此表中添加一个新行。它的优点是,即使您允许删除发票,分配给发票的号码也可以保持唯一。

create procedure GetInvoiceIdForCustomer
  @CustomerId as Int,
  @InvoiceId as Int Output
as
begin
  set nocount on

  begin transaction

  update CustomerInvoiceNumbers 
    set @InvoiceId = NextId, NextId += 1
    where CustomerId = @CustomerId

  if @@RowCount = 0
    begin
    set @InvoiceId = 1
    insert into CustomerInvoiceNumbers ( CustomerId, NextId ) values ( @CustomerId, @InvoiceId + 1 )
    end

  commit transaction
end       

end