1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
4
+ using NUnit . Framework ;
5
+ using ServiceStack . Data ;
6
+ using ServiceStack . DataAnnotations ;
7
+ using ServiceStack . OrmLite . Dapper ;
8
+
9
+ namespace ServiceStack . OrmLite . SqlServerTests
10
+ {
11
+ [ TestFixture ]
12
+ public class RowVersionTests : OrmLiteTestBase
13
+ {
14
+ public class ByteChildTbl
15
+ {
16
+ [ PrimaryKey ]
17
+ public Guid Id { get ; set ; }
18
+
19
+ [ References ( typeof ( ByteTbl ) ) ]
20
+ public Guid ParentId { get ; set ; }
21
+
22
+ [ RowVersion ]
23
+ public byte [ ] RowVersion { get ; set ; }
24
+
25
+
26
+ }
27
+
28
+ public class ByteTbl
29
+ {
30
+ [ PrimaryKey ]
31
+ public Guid Id { get ; set ; }
32
+
33
+ public string Text { get ; set ; }
34
+
35
+ [ RowVersion ]
36
+ public byte [ ] RowVersion { get ; set ; }
37
+
38
+ [ Reference ]
39
+ public List < ByteChildTbl > Children { get ; set ; } = new List < ByteChildTbl > ( ) ;
40
+ }
41
+
42
+ public class UlongChildTbl
43
+ {
44
+ [ PrimaryKey ]
45
+ public Guid Id { get ; set ; }
46
+
47
+ [ References ( typeof ( UlongTbl ) ) ]
48
+ public Guid ParentId { get ; set ; }
49
+
50
+ [ RowVersion ]
51
+ public ulong RowVersion { get ; set ; }
52
+ }
53
+
54
+ public class UlongTbl
55
+ {
56
+ [ PrimaryKey ]
57
+ public Guid Id { get ; set ; }
58
+
59
+ public string Text { get ; set ; }
60
+
61
+ // Use of ulong makes embedded Dapper functionality unavailable
62
+ [ RowVersion ]
63
+ public ulong RowVersion { get ; set ; }
64
+
65
+ [ Reference ]
66
+ public List < UlongChildTbl > Children { get ; set ; } = new List < UlongChildTbl > ( ) ;
67
+ }
68
+
69
+ [ Test ]
70
+ public void Read_and_write_to_tables_with_rowversions ( )
71
+ {
72
+ using ( var db = OpenDbConnection ( ) )
73
+ {
74
+ // Show that we can drop and create tables with rowversions of both .NET types and both get created as ROWVERSION in MSSQL
75
+ db . DropTable < ByteChildTbl > ( ) ;
76
+ db . DropTable < ByteTbl > ( ) ;
77
+ db . DropTable < UlongChildTbl > ( ) ;
78
+ db . DropTable < UlongTbl > ( ) ;
79
+ db . CreateTable < ByteTbl > ( ) ;
80
+ db . CreateTable < ByteChildTbl > ( ) ;
81
+ db . CreateTable < UlongTbl > ( ) ;
82
+ db . CreateTable < UlongChildTbl > ( ) ;
83
+
84
+
85
+
86
+
87
+ {
88
+ // Confirm off new Ormlite CRUD functionality with byte[] rowversion
89
+
90
+ var byteId = Guid . NewGuid ( ) ;
91
+ db . Insert ( new ByteTbl ( ) { Id = byteId } ) ;
92
+
93
+ var getByteRecord = db . SingleById < ByteTbl > ( byteId ) ;
94
+ getByteRecord . Text += " Updated" ;
95
+ db . Update ( getByteRecord ) ; //success!
96
+
97
+ getByteRecord . Text += "Attempting to update stale record" ;
98
+
99
+ //Can't update stale record
100
+ Assert . Throws < OptimisticConcurrencyException > ( ( ) => db . Update ( getByteRecord ) ) ;
101
+
102
+ //Can update latest version
103
+ var updatedRow = db . SingleById < ByteTbl > ( byteId ) ; // fresh version
104
+ updatedRow . Text += "Update Success!" ;
105
+ db . Update ( updatedRow ) ;
106
+
107
+ updatedRow = db . SingleById < ByteTbl > ( byteId ) ;
108
+ db . Delete ( updatedRow ) ; // can delete fresh version
109
+ }
110
+
111
+
112
+ {
113
+ // confirm original Ormlite CRUD functionality based on ulong rowversion
114
+
115
+ var ulongId = Guid . NewGuid ( ) ;
116
+ db . Insert ( new UlongTbl { Id = ulongId } ) ;
117
+
118
+ var getUlongRecord = db . SingleById < UlongTbl > ( ulongId ) ;
119
+ getUlongRecord . Text += " Updated" ;
120
+ db . Update ( getUlongRecord ) ; //success!
121
+
122
+ getUlongRecord . Text += "Attempting to update stale record" ;
123
+
124
+ //Can't update stale record
125
+ Assert . Throws < OptimisticConcurrencyException > ( ( ) => db . Update ( getUlongRecord ) ) ;
126
+
127
+ // Can update latest version
128
+ var updatedUlongRow = db . SingleById < UlongTbl > ( ulongId ) ; // fresh version
129
+ updatedUlongRow . Text += "Update Success!" ;
130
+ db . Update ( updatedUlongRow ) ;
131
+
132
+ updatedUlongRow = db . SingleById < UlongTbl > ( ulongId ) ;
133
+ db . Delete ( updatedUlongRow ) ; // can delete fresh version
134
+ }
135
+
136
+
137
+ {
138
+ // Confirm that original ulong rowversion unfortunately fails with Dapper custom sql queries
139
+
140
+ var ulongId = Guid . NewGuid ( ) ;
141
+ db . Insert ( new UlongTbl { Id = ulongId } ) ;
142
+
143
+ // As a further example, using Dapper but without rowversion column WILL work (but rowversion will NOT be set of course)
144
+ var thisDapperQryWorks = db . Query < UlongTbl > ( "select Id, Text from [UlongTbl]" ) . ToList ( ) ;
145
+ Assert . True ( thisDapperQryWorks . Count == 1 ) ;
146
+
147
+ // But any time RowVersion as ulong is include... no joy.. the map from db rowversion to ulong in dapper fails
148
+ Assert . Throws < System . Data . DataException > ( ( ) => db . Query < UlongTbl > ( "select Id, Text, Rowversion from [UlongTbl]" ) ) ;
149
+ }
150
+
151
+
152
+ {
153
+ // Now test out use of new byte[] rowversion with Dapper custom sql queries
154
+
155
+ var byteId = Guid . NewGuid ( ) ;
156
+ db . Insert ( new ByteTbl { Id = byteId } ) ;
157
+
158
+ // As a further example, using Dapper but without rowversion column WILL work, BUT the rowversion WONT BE SET (of course)
159
+ var thisDapperQryWorks = db . Query < ByteTbl > ( "select Id, Text from [ByteTbl]" ) . ToList ( ) ;
160
+ Assert . True ( thisDapperQryWorks . Count == 1 ) ;
161
+ Assert . True ( thisDapperQryWorks . First ( ) . RowVersion == default ( byte [ ] ) ) ;
162
+
163
+ // But any time RowVersion as ulong is include... no joy.. the map from db rowversion to ulong in dapper fails
164
+ var thisDapperQryWithRowVersionAlsoWorks = db . Query < ByteTbl > ( "select Id, Text, Rowversion from [ByteTbl]" ) . ToList ( ) ;
165
+ Assert . True ( thisDapperQryWithRowVersionAlsoWorks . Count == 1 ) ;
166
+ Assert . True ( thisDapperQryWithRowVersionAlsoWorks . First ( ) . RowVersion != default ( byte [ ] ) ) ;
167
+ }
168
+
169
+
170
+ {
171
+ // test original ulong version with child operations
172
+ var ulongId1 = Guid . NewGuid ( ) ;
173
+ db . Save ( new UlongTbl
174
+ {
175
+ Id = ulongId1 ,
176
+ Children = new List < UlongChildTbl > ( )
177
+ {
178
+ new UlongChildTbl { Id = Guid . NewGuid ( ) } ,
179
+ new UlongChildTbl { Id = Guid . NewGuid ( ) }
180
+ }
181
+ } , references : true ) ;
182
+ var ulongObj1 = db . LoadSelect < UlongTbl > ( x => x . Id == ulongId1 , x => x . Children ) . First ( ) ;
183
+ Assert . AreNotEqual ( default ( byte [ ] ) , ulongObj1 . RowVersion ) ;
184
+ Assert . AreNotEqual ( default ( byte [ ] ) , ulongObj1 . Children [ 0 ] . RowVersion ) ;
185
+ Assert . AreNotEqual ( default ( byte [ ] ) , ulongObj1 . Children [ 1 ] . RowVersion ) ;
186
+
187
+ var ulongId2 = Guid . NewGuid ( ) ;
188
+ db . Save ( new UlongTbl
189
+ {
190
+ Id = ulongId2 ,
191
+ Children = new List < UlongChildTbl > ( )
192
+ {
193
+ new UlongChildTbl { Id = Guid . NewGuid ( ) } ,
194
+ new UlongChildTbl { Id = Guid . NewGuid ( ) }
195
+ }
196
+ } , references : true ) ;
197
+ var ulongObj2 = db . LoadSelect < UlongTbl > ( x => x . Id == ulongId2 , x => x . Children ) . First ( ) ;
198
+ Assert . AreNotEqual ( default ( byte [ ] ) , ulongObj2 . RowVersion ) ;
199
+ Assert . AreNotEqual ( default ( byte [ ] ) , ulongObj2 . Children [ 0 ] . RowVersion ) ;
200
+ Assert . AreNotEqual ( default ( byte [ ] ) , ulongObj2 . Children [ 1 ] . RowVersion ) ;
201
+
202
+ // COnfirm multip select logic works
203
+ var q = db . From < UlongTbl > ( )
204
+ . Join < UlongTbl , UlongChildTbl > ( )
205
+ ;
206
+ var results = db . SelectMulti < UlongTbl , UlongChildTbl > ( q ) ;
207
+ foreach ( var tuple in results )
208
+ {
209
+ UlongTbl parent = tuple . Item1 ;
210
+ UlongChildTbl child = tuple . Item2 ;
211
+ Assert . AreNotEqual ( default ( byte [ ] ) , parent . RowVersion ) ;
212
+ Assert . AreNotEqual ( default ( byte [ ] ) , child . RowVersion ) ;
213
+ }
214
+ }
215
+
216
+ {
217
+ // test byte[] version with child operations
218
+ var byteid1 = Guid . NewGuid ( ) ;
219
+ db . Save ( new ByteTbl
220
+ {
221
+ Id = byteid1 ,
222
+ Children = new List < ByteChildTbl > ( )
223
+ {
224
+ new ByteChildTbl ( ) { Id = Guid . NewGuid ( ) , ParentId = byteid1 } ,
225
+ new ByteChildTbl { Id = Guid . NewGuid ( ) , ParentId = byteid1 }
226
+ }
227
+ } , references : true ) ;
228
+ var byteObj1 = db . LoadSelect < ByteTbl > ( x => x . Id == byteid1 , x => x . Children ) . First ( ) ;
229
+ Assert . AreNotEqual ( default ( byte [ ] ) , byteObj1 . RowVersion ) ;
230
+ Assert . AreNotEqual ( default ( byte [ ] ) , byteObj1 . Children [ 0 ] . RowVersion ) ;
231
+ Assert . AreNotEqual ( default ( byte [ ] ) , byteObj1 . Children [ 1 ] . RowVersion ) ;
232
+
233
+ var byteid2 = Guid . NewGuid ( ) ;
234
+ db . Save ( new ByteTbl
235
+ {
236
+ Id = byteid2 ,
237
+ Children = new List < ByteChildTbl > ( )
238
+ {
239
+ new ByteChildTbl { Id = Guid . NewGuid ( ) , ParentId = byteid2 } ,
240
+ new ByteChildTbl { Id = Guid . NewGuid ( ) , ParentId = byteid2 }
241
+ }
242
+ } , references : true ) ;
243
+ var byteObj2 = db . LoadSelect < ByteTbl > ( x => x . Id == byteid2 , x => x . Children ) . First ( ) ;
244
+ Assert . AreNotEqual ( default ( byte [ ] ) , byteObj2 . RowVersion ) ;
245
+ Assert . AreNotEqual ( default ( byte [ ] ) , byteObj2 . Children [ 0 ] . RowVersion ) ;
246
+ Assert . AreNotEqual ( default ( byte [ ] ) , byteObj2 . Children [ 1 ] . RowVersion ) ;
247
+
248
+ // COnfirm multip select logic works
249
+ var q = db . From < ByteTbl > ( )
250
+ . Join < ByteTbl , ByteChildTbl > ( )
251
+ ;
252
+ var results = db . SelectMulti < ByteTbl , ByteChildTbl > ( q ) ;
253
+ foreach ( var tuple in results )
254
+ {
255
+ ByteTbl parent = tuple . Item1 ;
256
+ ByteChildTbl child = tuple . Item2 ;
257
+ Assert . AreNotEqual ( default ( byte [ ] ) , parent . RowVersion ) ;
258
+ Assert . AreNotEqual ( default ( byte [ ] ) , child . RowVersion ) ;
259
+ }
260
+ }
261
+
262
+
263
+ {
264
+ // test the multi select using dapper
265
+
266
+ var byteid2 = Guid . NewGuid ( ) ;
267
+ db . Save ( new ByteTbl
268
+ {
269
+ Id = byteid2 ,
270
+ Children = new List < ByteChildTbl > ( )
271
+ {
272
+ new ByteChildTbl { Id = Guid . NewGuid ( ) , ParentId = byteid2 } ,
273
+ new ByteChildTbl { Id = Guid . NewGuid ( ) , ParentId = byteid2 }
274
+ }
275
+ } , references : true ) ;
276
+
277
+ var q = db . From < ByteTbl > ( )
278
+ . Join < ByteTbl , ByteChildTbl > ( )
279
+ . Select ( "*" ) ;
280
+
281
+ using ( var multi = db . QueryMultiple ( q . ToSelectStatement ( ) ) )
282
+ {
283
+ var results = multi . Read < ByteTbl , ByteChildTbl ,
284
+ Tuple < ByteTbl , ByteChildTbl > > ( Tuple . Create ) . ToList ( ) ;
285
+
286
+ foreach ( var tuple in results )
287
+ {
288
+ ByteTbl parent = tuple . Item1 ;
289
+ ByteChildTbl child = tuple . Item2 ;
290
+ Assert . AreNotEqual ( default ( byte [ ] ) , parent . RowVersion ) ;
291
+ Assert . AreNotEqual ( default ( byte [ ] ) , child . RowVersion ) ;
292
+ }
293
+ Assert . True ( results . Count > 0 ) ;
294
+ }
295
+ }
296
+ //Console.WriteLine("hit a key to end test");
297
+ //Console.ReadLine();
298
+
299
+
300
+ }
301
+ }
302
+ }
303
+ }
0 commit comments