创建了以下Linq to SQL事务,尝试创建无间隙的发票编号。 假设2个表:
表1:InvoiceNumbers。 - 列ID,序列号,增量 - 示例:1,10001,1
表2:发票。 - 列:ID,InvoiceNumber,名称 - 示例:1,10001,“Bob Smith”
Dim db As New Invoices.InvoicesDataContext
Dim lastInvoiceNumber = (From n In db.InvoiceNumbers Order By n.LastSerialNumber Descending
Select n.LastSerialNumber, n.Increment).First
Dim nextInvoiceNumber As Integer = lastInvoiceNumber.LastSerialNumber + lastInvoiceNumber.Increment
Dim newInvoiceNumber = New Invoices.InvoiceNumber With {.LastSerialNumber = nextInvoiceNumber, .Increment = lastInvoiceNumber.Increment}
Dim newInvoice = New Invoices.Invoice With {.InvoiceNumber = nextInvoiceNumber, .Name = "Test" + nextInvoiceNumber.ToString}
db.InvoiceNumbers.InsertOnSubmit(newInvoiceNumber)
db.Invoices.InsertOnSubmit(newInvoice)
db.SubmitChanges()
一切正常但可以使用这种方法,如果2个用户同时点击交易时可能会收到相同的发票号码吗? 如果是这样,是否有更好的方法使用Linq到Sql?
答案 0 :(得分:0)
在处理交易数据库时,序列中的差距不可避免。
首先,您不能使用SELECT max(id)+1
,因为它可能会为同时执行的2个事务提供相同的id
。这意味着您必须使用数据库本机自动增量列(MySQL,SQLite)或数据库序列(PostgreSQL,MSSQL,Oracle)来获取下一个可用的id
。
但即使使用自动增量序列也无法解决此问题。
想象一下,您有2个数据库连接,几乎同时启动了2个并行事务。第一个从自动增量序列中获得了一些id
,它变成了之前使用的值+1。一纳秒之后,第二次交易获得了下一个id
,现在是+2。现在假设第一个事务由于某种原因回滚(遇到错误,你的代码决定中止它,程序崩溃 - 你说出来)。之后,使用id
+2提交第二笔交易,在id
编号中创建了一个空白。
但是,如果此类并行交易的数量超过2,该怎么办?您无法预测,也无法告诉当前正在运行的事务重用已被放弃的id
。
理论上可以重复使用废弃的ID。但是,在实践中,它在数据库上非常昂贵,并且当多个会话尝试执行相同的操作时会产生更多问题。
TDLR:停止对抗它,使用过的ID中的空隙完全正常。
答案 1 :(得分:0)
您始终可以使用lock()确保事务未在多个线程中同时运行:
private static object myLockObject = new object();
....
....
public class MyClass
{
...
public void TransactionAndStuff()
{
lock(myLockObject)
{
// your linq query
}
}