数据库中的一条记录,除了应有自己的唯一切不包含任何逻辑意义的主键外,常常也需要一些暴露给最终用户的,User-Friendly的唯一编号。
例如用户、订单之类的对象,除了自己有一个可能是GUID类型的主键以外,常常还会有一个Int型的编号“用户编号”,“订单号码”。
如果直接操作数据库,例如SQL SERVER,这是很容易的,只需要将字段设成Identity即可获得一个自增量。
但是在XPO中,只有作为KEY(主键)的字段才有AutoGenerate属性,其他非KEY的字段都没有。同时,XPO又限定一个对象只能有一个KEY字段。(实际上System.Data.SqlClient也是不支持的)
那如何获取一个自增量?只能靠自己动手了。
DevExpress的Support Center中提供了一段Sample:
http://www.devexpress.com/Support/Center/p/CQ22273.aspx
例子中的代码稍显复杂,偶将其简化如下:
XpoSequencer
1 public sealed class XpoSequencer : XPBaseObject
2 {
3 [Key(true)]
4 public Guid Oid;
5 [Size(254), Indexed(Unique = true)]
6 public string SequenceId;
7 public int Counter;
8 public XpoSequencer(Session session) : base(session) { }
9 public const int MaxIdGenerationAttempts = 7;
10 public const int MinConflictDelay = 50;
11 public const int MaxConflictDelay = 500;
12
13 public static int GetNextValue(IDataLayer dataLayer, string sequenceId)
14 {
15 if (dataLayer == null)
16 throw new ArgumentNullException("dataLayer");
17 if (sequenceId == null)
18 sequenceId = string.Empty;
19 for (int attempt = 1; ; ++attempt)
20 {
21 try
22 {
23 using (Session generatorSession = new Session(dataLayer))
24 {
25 XpoSequencer generator = generatorSession.FindObject<XpoSequencer>(new OperandProperty("SequenceId") == sequenceId);
26 if (generator == null)
27 {
28 generator = new XpoSequencer(generatorSession);
29 generator.SequenceId = sequenceId;
30 }
31 generator.Counter++;
32 generator.Save();
33 return generator.Counter;
34 }
35 }
36 catch (LockingException)
37 {
38 if (attempt >= MaxIdGenerationAttempts)
39 throw;
40 }
41 if (attempt > MaxIdGenerationAttempts / 2)
42 Thread.Sleep(new Random().Next(MinConflictDelay, MaxConflictDelay));
43 }
44 }
45 }
2 {
3 [Key(true)]
4 public Guid Oid;
5 [Size(254), Indexed(Unique = true)]
6 public string SequenceId;
7 public int Counter;
8 public XpoSequencer(Session session) : base(session) { }
9 public const int MaxIdGenerationAttempts = 7;
10 public const int MinConflictDelay = 50;
11 public const int MaxConflictDelay = 500;
12
13 public static int GetNextValue(IDataLayer dataLayer, string sequenceId)
14 {
15 if (dataLayer == null)
16 throw new ArgumentNullException("dataLayer");
17 if (sequenceId == null)
18 sequenceId = string.Empty;
19 for (int attempt = 1; ; ++attempt)
20 {
21 try
22 {
23 using (Session generatorSession = new Session(dataLayer))
24 {
25 XpoSequencer generator = generatorSession.FindObject<XpoSequencer>(new OperandProperty("SequenceId") == sequenceId);
26 if (generator == null)
27 {
28 generator = new XpoSequencer(generatorSession);
29 generator.SequenceId = sequenceId;
30 }
31 generator.Counter++;
32 generator.Save();
33 return generator.Counter;
34 }
35 }
36 catch (LockingException)
37 {
38 if (attempt >= MaxIdGenerationAttempts)
39 throw;
40 }
41 if (attempt > MaxIdGenerationAttempts / 2)
42 Thread.Sleep(new Random().Next(MinConflictDelay, MaxConflictDelay));
43 }
44 }
45 }
只需要上面这个XpoSequencer类即可,例子中的XpoSiteId类偶没用。
XpoSequencer 类的使用方法也很简单,例如在Order类中需要获取一个自增订单号:
static readonly string typeName = typeof(XpoOrder).FullName;
int GetMaxID()
{
return (int)XpoSequencer.GetNextValue(Session.DataLayer, typeName);
}
int GetMaxID()
{
return (int)XpoSequencer.GetNextValue(Session.DataLayer, typeName);
}
注意上面的typeName字符串,它代表了要获取自增量对象的唯一标识。在XpoSequencer中会对每一个唯一标识添加一条记录,自增量是按该记录中的值来算的。在一个项目中就可以用XpoSequencer为多个对象产生各自独立的自增量。例如换到User要用时,传入typeof(XpoUser).FullName即可。也可以用其他的任意字符串,只要能唯一的标识出不同的自增量就可以。