Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit c46a221

Browse files
Add tests that confirm bug
1 parent 9cce4b1 commit c46a221

File tree

3 files changed

+300
-0
lines changed

3 files changed

+300
-0
lines changed

src/ServiceStack.OrmLite.Oracle.Tests/ServiceStack.OrmLite.Oracle.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@
156156
<Compile Include="..\..\tests\ServiceStack.OrmLite.Tests\MockAllApiTests.cs">
157157
<Link>MockAllApiTests.cs</Link>
158158
</Compile>
159+
<Compile Include="..\..\tests\ServiceStack.OrmLite.Tests\MultipleConnectionIdTests.cs">
160+
<Link>MultipleConnectionIdTests.cs</Link>
161+
</Compile>
159162
<Compile Include="..\..\tests\ServiceStack.OrmLite.Tests\OrmLiteBasicPersistenceProviderTests.cs">
160163
<Link>OrmLiteBasicPersistenceProviderTests.cs</Link>
161164
</Compile>
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using NUnit.Framework;
7+
using ServiceStack.DataAnnotations;
8+
9+
namespace ServiceStack.OrmLite.Tests
10+
{
11+
[TestFixture]
12+
public class MultipleConnectionIdTests : OrmLiteTestBase
13+
{
14+
private int _waitingThreadCount;
15+
private int _waitingThreadsReleasedCounter;
16+
17+
[SetUp]
18+
public void SetUp()
19+
{
20+
using (var db = OpenDbConnection())
21+
{
22+
db.DropAndCreateTable<MultipleConnection>();
23+
}
24+
}
25+
26+
[Test]
27+
public void TwoSimultaneousInsertsGetDifferentIds()
28+
{
29+
var dataArray = new[]
30+
{
31+
new MultipleConnection {Data = "one"},
32+
new MultipleConnection {Data = "two"}
33+
};
34+
35+
var originalExecFilter = OrmLiteConfig.ExecFilter;
36+
try
37+
{
38+
OrmLiteConfig.ExecFilter = new PostExecuteActionExecFilter(originalExecFilter, cmd => PauseForOtherThreadsAfterInserts(cmd, 2));
39+
40+
Parallel.ForEach(dataArray, data =>
41+
{
42+
using (var db = OpenDbConnection())
43+
{
44+
data.Id = db.Insert(new MultipleConnection {Data = data.Data}, selectIdentity: true);
45+
46+
Assert.That(data.Id, Is.Not.EqualTo(0));
47+
}
48+
});
49+
}
50+
finally
51+
{
52+
OrmLiteConfig.ExecFilter = originalExecFilter;
53+
}
54+
55+
Assert.That(dataArray[1].Id, Is.Not.EqualTo(dataArray[0].Id));
56+
}
57+
58+
private void PauseForOtherThreadsAfterInserts(IDbCommand cmd, int numberOfThreads)
59+
{
60+
if (!cmd.CommandText.StartsWith("INSERT ", StringComparison.InvariantCultureIgnoreCase))
61+
return;
62+
63+
var initialReleasedCounter = _waitingThreadsReleasedCounter;
64+
Interlocked.Increment(ref _waitingThreadCount);
65+
try
66+
{
67+
var waitUntil = DateTime.UtcNow.AddSeconds(2);
68+
while ((_waitingThreadCount < numberOfThreads) && (initialReleasedCounter == _waitingThreadsReleasedCounter))
69+
{
70+
if (DateTime.UtcNow >= waitUntil)
71+
throw new Exception("There were not enough waiting threads after timeout");
72+
Thread.Sleep(1);
73+
}
74+
75+
Interlocked.Increment(ref _waitingThreadsReleasedCounter);
76+
}
77+
finally
78+
{
79+
Interlocked.Decrement(ref _waitingThreadCount);
80+
}
81+
}
82+
83+
[Test]
84+
public void TwoSimultaneousSavesGetDifferentIds()
85+
{
86+
var dataArray = new[]
87+
{
88+
new MultipleConnection {Data = "one"},
89+
new MultipleConnection {Data = "two"}
90+
};
91+
92+
var originalExecFilter = OrmLiteConfig.ExecFilter;
93+
try
94+
{
95+
OrmLiteConfig.ExecFilter = new PostExecuteActionExecFilter(originalExecFilter, cmd => PauseForOtherThreadsAfterInserts(cmd, 2));
96+
97+
Parallel.ForEach(dataArray, data =>
98+
{
99+
using (var db = OpenDbConnection())
100+
{
101+
db.Save(data);
102+
103+
Assert.That(data.Id, Is.Not.EqualTo(0));
104+
}
105+
});
106+
}
107+
finally
108+
{
109+
OrmLiteConfig.ExecFilter = originalExecFilter;
110+
}
111+
112+
Assert.That(dataArray[1].Id, Is.Not.EqualTo(dataArray[0].Id));
113+
}
114+
115+
private class PostExecuteActionExecFilter : IOrmLiteExecFilter
116+
{
117+
private readonly IOrmLiteExecFilter _inner;
118+
private readonly Action<IDbCommand> _postExecuteAction;
119+
120+
public PostExecuteActionExecFilter(IOrmLiteExecFilter inner, Action<IDbCommand> postExecuteAction)
121+
{
122+
_inner = inner;
123+
_postExecuteAction = postExecuteAction;
124+
}
125+
126+
public SqlExpression<T> SqlExpression<T>(IDbConnection dbConn)
127+
{
128+
return _inner.SqlExpression<T>(dbConn);
129+
}
130+
131+
public IDbCommand CreateCommand(IDbConnection dbConn)
132+
{
133+
var innerCommand = _inner.CreateCommand(dbConn);
134+
return new PostExcuteActionCommand(innerCommand, _postExecuteAction);
135+
}
136+
137+
public void DisposeCommand(IDbCommand dbCmd)
138+
{
139+
_inner.DisposeCommand(dbCmd);
140+
}
141+
142+
public T Exec<T>(IDbConnection dbConn, Func<IDbCommand, T> filter)
143+
{
144+
var cmd = CreateCommand(dbConn);
145+
try
146+
{
147+
return filter(cmd);
148+
}
149+
finally
150+
{
151+
DisposeCommand(cmd);
152+
}
153+
}
154+
155+
public void Exec(IDbConnection dbConn, Action<IDbCommand> filter)
156+
{
157+
var cmd = CreateCommand(dbConn);
158+
try
159+
{
160+
filter(cmd);
161+
}
162+
finally
163+
{
164+
DisposeCommand(cmd);
165+
}
166+
}
167+
168+
public IEnumerable<T> ExecLazy<T>(IDbConnection dbConn, Func<IDbCommand, IEnumerable<T>> filter)
169+
{
170+
var cmd = CreateCommand(dbConn);
171+
try
172+
{
173+
var results = filter(cmd);
174+
175+
foreach (var item in results)
176+
{
177+
yield return item;
178+
}
179+
}
180+
finally
181+
{
182+
DisposeCommand(cmd);
183+
}
184+
}
185+
}
186+
187+
private class PostExcuteActionCommand : IDbCommand
188+
{
189+
private readonly IDbCommand _inner;
190+
private readonly Action<IDbCommand> _postExecuteAction;
191+
192+
public PostExcuteActionCommand(IDbCommand inner, Action<IDbCommand> postExecuteAction)
193+
{
194+
_inner = inner;
195+
_postExecuteAction = postExecuteAction;
196+
}
197+
198+
public void Dispose()
199+
{
200+
_inner.Dispose();
201+
}
202+
203+
public void Prepare()
204+
{
205+
_inner.Prepare();
206+
}
207+
208+
public void Cancel()
209+
{
210+
_inner.Cancel();
211+
}
212+
213+
public IDbDataParameter CreateParameter()
214+
{
215+
return _inner.CreateParameter();
216+
}
217+
218+
public int ExecuteNonQuery()
219+
{
220+
var result = _inner.ExecuteNonQuery();
221+
_postExecuteAction(this);
222+
return result;
223+
}
224+
225+
public IDataReader ExecuteReader()
226+
{
227+
var result = _inner.ExecuteReader();
228+
_postExecuteAction(this);
229+
return result;
230+
}
231+
232+
public IDataReader ExecuteReader(CommandBehavior behavior)
233+
{
234+
var result = _inner.ExecuteReader(behavior);
235+
_postExecuteAction(this);
236+
return result;
237+
}
238+
239+
public object ExecuteScalar()
240+
{
241+
var result = _inner.ExecuteScalar();
242+
_postExecuteAction(this);
243+
return result;
244+
}
245+
246+
public IDbConnection Connection
247+
{
248+
get { return _inner.Connection; }
249+
set { _inner.Connection = value; }
250+
}
251+
252+
public IDbTransaction Transaction
253+
{
254+
get { return _inner.Transaction; }
255+
set { _inner.Transaction = value; }
256+
}
257+
258+
public string CommandText
259+
{
260+
get { return _inner.CommandText; }
261+
set { _inner.CommandText = value; }
262+
}
263+
264+
public int CommandTimeout
265+
{
266+
get { return _inner.CommandTimeout; }
267+
set { _inner.CommandTimeout = value; }
268+
}
269+
270+
public CommandType CommandType
271+
{
272+
get { return _inner.CommandType; }
273+
set { _inner.CommandType = value; }
274+
}
275+
276+
public IDataParameterCollection Parameters
277+
{
278+
get { return _inner.Parameters; }
279+
}
280+
281+
public UpdateRowSource UpdatedRowSource
282+
{
283+
get { return _inner.UpdatedRowSource; }
284+
set { _inner.UpdatedRowSource = value; }
285+
}
286+
}
287+
}
288+
289+
public class MultipleConnection
290+
{
291+
[AutoIncrement]
292+
public long Id { get; set; }
293+
294+
public string Data { get; set; }
295+
}
296+
}

tests/ServiceStack.OrmLite.Tests/ServiceStack.OrmLite.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
<Compile Include="LoadReferencesJoinTests.cs" />
133133
<Compile Include="LoadReferencesTests.cs" />
134134
<Compile Include="MockAllApiTests.cs" />
135+
<Compile Include="MultipleConnectionIdTests.cs" />
135136
<Compile Include="OrmLiteExecFilterTests.cs" />
136137
<Compile Include="OrmLiteFiltersTests.cs" />
137138
<Compile Include="PerfTests.cs" />

0 commit comments

Comments
 (0)