例如,考虑http://starcounter.io/tutorials/1-introduction-to-part-1/ InvoiceDemo
上的示例应用程序声明以下数据库对象
using Starcounter;
[Database]
public class Invoice {
public int InvoiceNo;
public string Name;
public decimal Total {
get {
return Db.SQL<decimal>(@"SELECT sum(r.Total)
FROM InvoiceRow r
WHERE r.Invoice = ?",
this).First;
}
}
public QueryResultRows<InvoiceRow> Items {
get {
return Db.SQL<InvoiceRow>(@"SELECT r
FROM InvoiceRow r
WHERE r.Invoice = ?",
this);
}
}
public Invoice() {
new InvoiceRow() {
Invoice = this
};
}
}
[Database]
public class InvoiceRow {
public Invoice Invoice;
public string Description;
public int Quantity;
public decimal Price;
public decimal Total {
get {
return Quantity * Price;
}
}
public InvoiceRow() {
Quantity = 1;
}
}
如果我想确定我知道已添加的Invoice Rows的顺序,我会在标准SQL DB中使用自动增量ID。 Starcounter的最佳做法是什么?
答案 0 :(得分:3)
Starcounter不提供自动递增的用户ID,我怀疑它会因不同的原因而出现。例如:
一个例子,说明什么顺序对申请意味着什么和期望是不明显的:
我需要确保我可以按创建顺序对行进行排序
创作时间可能意味着:
由于可以在不同的交易中同时添加记录,因此它们的顺序在选项之间可能会有很大差异。只有应用程序开发人员知道它的含义以及它将如何影响用户体验(UX)。
Starcounter分配基于计数器的object identity,因此它类似于自动增量ID,但适用于数据库中的所有对象/记录。但是,重要的是要记住:
由于最后一点使用Starcounter的对象标识来按创建时间排序记录并不是一个好主意。
我猜其他数据库中自动递增的ID也有类似的问题。因此,如果应该保留用户体验或者将来可能会遇到重大变化(通常无法预见),使用它们并不是那么安全。
我的意见是订单是特定于应用程序的,应该在应用程序逻辑中实现。我看到了几种方式:
__sync_bool_compare_and_swap
或Interlocked.Increment
,这是原子操作并且不需要锁定,但它对缓存不友好。请注意,在可重新调整的事务范围之外访问它很重要,即使用快照隔离。但是,在此类交易中使用它仍然有效,但每次重试时都会增加计数器。SELECT MAX(InvoiceRowNo) FROM InvoiceRow
,并将其递增。通过creating unique index添加唯一约束,以避免在并发情况下使用相同的ID。在并发增量的情况下,将发生事务之间的冲突,并且将重试其中一个事务,而另一个事务将成功。请注意,在高负载时,冲突可能经常发生,并且一些不幸的请求将被大大延迟或永远不会通过。答案 1 :(得分:1)
如果它仅用于排序,而不是真实的(例如带有规则的收据#)seqno,我建议你在对象中添加DateTime进行过滤/排序。如果那不够好,创建一个单例类(可能)在启动时从数据库中选择最高知名的SeqNo,然后使用这个单例来获取/递增计数器(或者如果你是懒惰的,每次选择它) 。如果你想100%确定它们是唯一的,即使你删除了项目,也要让seqno配置保持不变,这样即使你删除了最新的条目,也总是保存最后使用的SeqNo。
不确定如何在SC中管理ObjectID,但我认为将它们用于除了唯一标识符之外的任何东西都可能是一件坏事。 可能你会得到OID(n-1)&lt; OID(n),但你不应该依赖它......