c# - 如何按ID自动重启列增量?

标签 c# sql-server entity-framework

我们正在开发供小型企业使用的 Multi-Tenancy 系统,需要为其客户生成发票。出于显而易见的原因,基本上每个租户都希望自己的发票编号序列彼此独立生成。所以第一个租户可以有发票 1、2、3,第二个租户可以有相同的发票,因为他们是独立的企业并且彼此一无所知。

我们使用 Entity Framework 7 作为 ORM,使用 SQL 2014 作为数据库。我们需要一种方法来生成这些发票编号,而不会在高并发负载下为同一租户意外重复。最初在 EF 中,我们只是取发票列的最大值,其中租户 = 当前租户 ID,然后将其加 1,但在压力测试之后,它为该租户创建了很多重复的发票编号。触发器对此更好吗?序列?我不确定下一步该去哪里。

这是描述我们情况的简化表格布局。请注意最后一个表格,每个租户的发票编号需要如何重新启动。这就是我们要实现的目标

+----------+-------------+
| TenantID | TenantName  |
+----------+-------------+
|        5 | ABC Company |
|        6 | XYZ Corp    |
+----------+-------------+
+----------+------------+----------------+
| TenantID | CustomerID |  CustomerName  |
+----------+------------+----------------+
|        5 |          2 | Alpa Customer  |
|        5 |          5 | Beta Customer  |
|        6 |          3 | Delta Customer |
|        6 |          4 | Omega Customer |
+----------+------------+----------------+

    +----------+------------+-----------+-------------------------------------------------------+
| TenantID | CustomerID | InvoiceID | InvoiceNumber(this one needs to restart per tenant) |
+----------+------------+-----------+-------------------------------------------------------+
|        5 |          2 |         1 |                                                     1 |
|        5 |          2 |         7 |                                                     2 |
|        5 |          5 |         2 |                                                     3 |
|        5 |          5 |         4 |                                                     4 |
|        5 |          5 |         5 |                                                     5 |
|        6 |          3 |         8 |                                                     1 |
|        6 |          4 |         3 |                                                     2 |
|        6 |          4 |         6 |                                                     3 |
+----------+------------+-----------+-------------------------------------------------------+

基于@ERIKE 的回答 我最终在 TenantID 和 InvoiceNumber 周围添加了一个唯一约束,然后我取发票号的最大值并将其加一并尝试插入。这是围绕在 C# 中的 do while 循环。每当出现唯一约束错误时,它都会重试

bool retryInsert;
do
{
    try
    {
        retryInsert = false;
        var invNo = (db.tbl_Invoice
            .Where(t => t.TenantID == invoice.TenantID)
            .Max(t => t == null ? 0 : t.InvoiceNumber)
            ) + 1;

        invoice.InvoiceNumber = invNo;
        db.tbl_Invoice.Add(invoice);
        db.SaveChanges();
    }

    catch (DbUpdateException ex)
    {
        retryInsert = false;
        var sqlexception = ex.InnerException as SqlException;
        if (sqlexception != null)
        {
            if (sqlexception.Errors.OfType<SqlError>()
                .Any(se => se.Number == 2627))
            {
                retryInsert = true;
            }

            else throw ex;
        }
    }
} while (retryInsert);

return invoice;

最佳答案

为了支持每秒最高的交易,我相信你最好的答案是在发票表中的(租户ID,发票ID)上放置一个唯一索引并进行并发插入,然后在唯一键的情况下违规,重试。

我基于几年前读到的一篇关于在这种情况下实现最高吞吐量的文章。

无论如何,我强烈建议不要为每个租户提供一张 table 。您可能会研究序列,但我不确定每个租户有一个序列是否合理。

每个租户一行的“最后一张发票”表可以工作:

UPDATE dbo.TenantLastInvoice
SET
   LastInvoiceID = LastInvoiceID + 1,
   @InvoiceID = InvoiceID
WHERE TenantID = 123;

我不是 100% 确定您是否需要锁定提示,您可以考虑 ROWLOCKUPDATELOCK,如果所有其他方法都失败,READPAST(如果没有发生更新则重试)。

不要指望能够完美地避免偶尔跳过 ID。一个人要么容忍碰撞的可能性(完全 Not Acceptable ),要么容忍在某些边缘情况下跳过 ID(烦人,但不是世界末日),这似乎是一个难以接受的事实。

关于c# - 如何按ID自动重启列增量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34258820/

相关文章:

sql - 获取每个部分缺失的数字

c# - ASP.NET MVC 5项目中VS2017连接MySQL

c# - ASP.NET Web Forms 4.5 模型绑定(bind),其中模型包含集合

c# - 在另一个正则表达式匹配后匹配所有字符的正则表达式

mysql - SQl 选择出现在一组或多组中的数据

c# - Entity Framework Core 和包括子集合

entity-framework - 使用 Entity Framework 导航属性而不创建大量查询(避免 N+1)

C# - 解析 XSD 架构 - 将所有元素获取到组合框

c# - 如何在 zip 存档中找到某些文件的压缩位以进行进一步处理?

sql-server - SQLServer object_id 用于索引?