【问题标题】:Generate Invoice Number for Multi user environment in C#在 C# 中为多用户环境生成发票编号
【发布时间】:2018-04-06 20:11:45
【问题描述】:

我正在使用 Max 函数生成发票编号,并将最后一个发票编号加 1。非常简单。但我将此应用到多用户环境中遇到问题。因为两个用户同时打开一个发票窗口都可以获得相同的 ID,所以发票号应该是第一个出现而不是最后一个,所以我不能使用身份(自动生成 ID)作为发票号。我想在 C# 中为多用户环境窗口窗体应用程序生成发票编号...

另一个问题是两个用户同时访问和更新同一记录会发生什么。

我希望你能理解这个问题。我阅读了有关乐观与悲观锁定的信息,但我需要一个解决方案。所以有人可以回复我吗

【问题讨论】:

  • 只需使用 IDENTITY 列并更改您的 UI,使其仅在保存记录后显示。如果尚未保存记录,为什么要显示此信息?记录完成后应保存,因此如果用户取消发票创建,您将不会留下空记录。
  • 是的,使用身份列 - 下一次审查确保您被解雇。这是一种反模式,因为发票编号具有标识列无法满足的法律要求。这里可能出错的原因太多了,包括由于 sql server 重新启动行为导致的间隙。

标签: c# sql-server


【解决方案1】:

有一个不同的数据库表来存储最大发票编号。 当用户打开发票窗口时,运行一个存储过程来:

  • 锁定表格
  • 获取当前号码
  • 存储当前+1号
  • 解锁表
  • 返回当前+1号

这将确保即使有同时存在的请求,您也将始终获得唯一的发票编号。

另一方面: 1. 这个存储过程不能同时为多个用户运行,所以在高流量的情况下会成为瓶颈。 2.发票号码会有空洞——发票被取消的空洞。

如果您对这种方法不满意,则必须在保存时生成发票编号,这将是一个 IDENTITY 列,但您提到用户希望在开始工作时看到发票编号发票。

更新:

我发现一篇优秀的文章使用sp_getapplock 详细说明了上述方法。文章链接是HERE。我建议使用这种方法。

【讨论】:

  • Flip Side 1 是一个小问题——经过适当编程,您每秒可以处理数千个请求。不是真正的问题。 2 是架构师问题...
【解决方案2】:

通常问题是您的应用程序提出了不好的要求并且方法不合适。

  • 发票编号不应在创建发票进行编辑时分配,而是在将其放入系统进行交易时分配。这样中止不会留下间隙。

  • 收集发票详细信息,然后通过存储过程创建插入,该存储过程放置适当的锁。这很简单 - 如果您知道如何在相关表上加锁,或为此 SP 使用应用锁。

通常这是存储过程有意义的少数几个地方之一。生成包含 0 个行项目的未记帐发票,以便您的应用程序可以添加详细信息。瞧,问题解决了。如果用户中止,请将发票标记为“已取消”并完成。

【讨论】:

  • 感谢您的想法,但问题是我的客户希望在表单加载时显示发票号码,因此我无法使用身份,它是一个多用户环境系统。请帮助我....
  • 那么您的客户需要进行现实检查。这将导致很多被取消的发票。无论如何,方法是相同的 - 使用存储过程分配发票编号。
  • 我能知道一般POS系统发票号码如何生成。当我使用标识列作为发票编号并插入一个值并使用 IDENT_CURRENT 检索最后插入的值之后,我遇到了另一个问题,但是在多用户环境中,两个用户同时进行交易会发生什么......
  • 任何价值超过一美分的 POS 都会在保存时生成数字,而不是在显示表单时生成。剩下的就是鲁莽的编程——没有正当理由的要求,违反了适当的会计原则。
  • 感谢您的回复.. 抱歉再次询问我无法理解您的最后评论。你能详细说明答案吗..我应该为我的问题做些什么给我一个好的解决方案..给出一些示例代码等...再次感谢@TomTom
【解决方案3】:

自动增量列是最佳解决方案。这就是我们在 SQL Server 中所说的 Identity 列。

如果在您的情况下您无法使用它,您可以在服务器端代码中创建一个线程安全的方法,以基于静态属性生成发票 ID。 确保您在单实例模式下使用服务。

static long invoiceId = getMaxFunction();
...
...
public long GenerateInvoiceId()
{
    lock(this)
    {
        return invoiceId++;
    }
}

【讨论】:

  • 这就是大多数人所说的“你被解雇”的反模式。 ID 存在严重问题,仅适用于最微不足道的情况(例如,在某些公司中,我们为不同的子组提供多个系列的发票编号)。除非您设置了一个标志,否则您也可以在服务器重启时出现间隙,并且您通常会在事务失败的情况下打开间隙。非常糟糕 - ID 就其本身而言是好的,它不是生成业务标识符的序列。
  • 这在技术上甚至是错误的。即使在 Web 应用程序中,锁也不能保证有效。读过文档吗?锁是每个进程。
  • @TomTom 他想要一种在插入数据库之前为发票生成 Id 的方法。我已经注意到最好的方法是数据库中的列标识。对于第二种解决方案,请考虑我正在谈论为所有客户端提供服务的单实例服务。
  • @hatem87 你是对的,我想在插入数据库之前生成 id,但你的想法很好,我可以在数据库中获取列标识,但在保存(插入)值之后,我想检索最后一个标识使用 IDENT_CURRENT 的值,但在多用户环境中存在问题 两个或多个用户同时保存数据会发生什么......如何在发票中打印准确的发票号码......请帮助我。
  • 如果您确实需要在插入发票之前显示 id。因此,在进行保存时,插入 id 并检查这是否与您已经在 gui 中正确显示的不同,并提醒用户。 (我不相信您在保存数据之前显示 id)。否则请参阅@TomTom 在他的解决方案中所说的关于“已取消”状态的内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多