Skip to content

Commit 7ababa8

Browse files
committed
#29 branch is unstable still have some asynchronous code to stabilize
1 parent d22ca23 commit 7ababa8

27 files changed

+641
-90
lines changed

SubSonic.Extensions.Test/Data/Procedures/DeleteRealEstateProperty.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace SubSonic.Extensions.Test
88
{
9-
[DbStoredProcedure(nameof(DeleteRealEstateProperty))]
9+
[DbStoredProcedure(nameof(DeleteRealEstateProperty), IsNonQuery = true)]
1010
public class DeleteRealEstateProperty
1111
: DbSubSonicCommandQueryProcedure<Models.RealEstateProperty>
1212
{

SubSonic.Extensions.Test/Extensions/UnitTestingExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public static DataTable ToDataTable<TEntity>(this IEnumerable<TEntity> source)
177177
}
178178
}
179179

180-
public static void AddCommandBehavior<TResult>(this DbProviderFactory factory, string command, Func<DbCommand, TResult> result)
180+
public static void AddCommandBehavior(this DbProviderFactory factory, string command, Func<DbCommand, object> result)
181181
{
182182
if (factory is null)
183183
{

SubSonic.Extensions.Test/MockDbClient/IMockCommandExecution.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
57

68
namespace SubSonic.Extensions.Test.MockDbClient
79
{
810
using Syntax;
11+
912
interface IMockCommandExecution
1013
{
1114
int ExecuteNonQuery(MockDbCommand cmd);
1215
object ExecuteScalar(MockDbCommand cmd);
1316
MockDbDataReaderCollection ExecuteDataReader(MockDbCommand cmd);
17+
Task<MockDbDataReaderCollection> ExecuteDataReaderAsync(MockDbCommand cmd, CancellationToken cancellationToken);
1418
}
1519
}

SubSonic.Extensions.Test/MockDbClient/MockDBErrors.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SubSonic.Extensions.Test/MockDbClient/MockDBErrors.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,7 @@
126126
<data name="MissingDbParameter" xml:space="preserve">
127127
<value>Missing expected db parameter.</value>
128128
</data>
129+
<data name="ResultSetIsNotDataTable" xml:space="preserve">
130+
<value>The return is not of type DataTable.</value>
131+
</data>
129132
</root>

SubSonic.Extensions.Test/MockDbClient/MockDbClientFactory.cs

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.Text;
55
using System.Data;
66
using System.Data.Common;
7+
using System.Threading;
8+
using System.Threading.Tasks;
79

810
namespace SubSonic.Extensions.Test.MockDbClient
911
{
@@ -91,21 +93,88 @@ private MockCommandBehavior FindBehavior(MockDbCommand cmd)
9193

9294
int IMockCommandExecution.ExecuteNonQuery(MockDbCommand cmd)
9395
{
94-
return GetReturnValue<int>(cmd);
96+
if (GetReturnValue<int>(cmd) is int @return)
97+
{
98+
return @return;
99+
}
100+
101+
throw Error.InvalidOperation();
95102
}
96103

97104
object IMockCommandExecution.ExecuteScalar(MockDbCommand cmd)
98105
{
99106
return GetReturnValue<object>(cmd);
100107
}
101108

109+
async Task<MockDbDataReaderCollection> IMockCommandExecution.ExecuteDataReaderAsync(MockDbCommand cmd, CancellationToken cancellationToken)
110+
{
111+
string[] commands = cmd.CommandText.Split(';');
112+
113+
if (commands.Length == 1)
114+
{ // command contains one select command
115+
object value = GetReturnValue<DataTable>(cmd);
116+
117+
if (value is DataTable result)
118+
{
119+
return new MockDbDataReaderCollection(result.CreateDataReader());
120+
}
121+
else if (value is null)
122+
{
123+
return new MockDbDataReaderCollection();
124+
}
125+
else
126+
{
127+
throw Error.InvalidOperation(MockDBErrors.ResultSetIsNotDataTable);
128+
}
129+
}
130+
else
131+
{
132+
using (DataSet data = new DataSet())
133+
{
134+
foreach (string sql in commands)
135+
{
136+
cancellationToken.ThrowIfCancellationRequested();
137+
138+
#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
139+
#pragma warning disable CA2000 // Dispose objects before losing scope
140+
if (GetReturnValue<DataTable>(new MockDbCommand(this, cmd.Parameters) { CommandText = sql.Trim("\r\n".ToCharArray()) }) is DataTable result)
141+
{
142+
data.Tables.Add(result);
143+
}
144+
#pragma warning restore CA2000 // Dispose objects before losing scope
145+
#pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
146+
}
147+
148+
if (data.Tables.Count == 0)
149+
{
150+
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Could not find behavior for command '{0}'", cmd.CommandText));
151+
}
152+
153+
return new MockDbDataReaderCollection(data.CreateDataReader());
154+
}
155+
}
156+
}
157+
102158
MockDbDataReaderCollection IMockCommandExecution.ExecuteDataReader(MockDbCommand cmd)
103159
{
104160
string[] commands = cmd.CommandText.Split(';');
105161

106162
if (commands.Length == 1)
107163
{ // command contains one select command
108-
return new MockDbDataReaderCollection(GetReturnValue<DataTable>(cmd).IsNotNull(x => x.CreateDataReader()));
164+
object value = GetReturnValue<DataTable>(cmd);
165+
166+
if (value is DataTable result)
167+
{
168+
return new MockDbDataReaderCollection(result.CreateDataReader());
169+
}
170+
else if (value is null)
171+
{
172+
return new MockDbDataReaderCollection();
173+
}
174+
else
175+
{
176+
throw Error.InvalidOperation(MockDBErrors.ResultSetIsNotDataTable);
177+
}
109178
}
110179
else
111180
{
@@ -115,8 +184,7 @@ MockDbDataReaderCollection IMockCommandExecution.ExecuteDataReader(MockDbCommand
115184
{
116185
#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
117186
#pragma warning disable CA2000 // Dispose objects before losing scope
118-
DataTable result = GetReturnValue<DataTable>(new MockDbCommand(this, cmd.Parameters) { CommandText = sql.Trim("\r\n".ToCharArray()) });
119-
if (!(result is null))
187+
if (GetReturnValue<DataTable>(new MockDbCommand(this, cmd.Parameters) { CommandText = sql.Trim("\r\n".ToCharArray()) }) is DataTable result)
120188
{
121189
data.Tables.Add(result);
122190
}
@@ -134,7 +202,7 @@ MockDbDataReaderCollection IMockCommandExecution.ExecuteDataReader(MockDbCommand
134202
}
135203
}
136204

137-
public TReturn GetReturnValue<TReturn>(MockDbCommand cmd)
205+
public object GetReturnValue<TReturn>(MockDbCommand cmd)
138206
{
139207
if (cmd is null)
140208
{
@@ -150,7 +218,7 @@ public TReturn GetReturnValue<TReturn>(MockDbCommand cmd)
150218

151219
object value = behavior.ReturnValue;
152220

153-
if (value is Func<DbCommand, TReturn> func)
221+
if (value is Func<DbCommand, object> func)
154222
{
155223
return func(cmd);
156224
}

SubSonic.Extensions.Test/MockDbClient/MockDbCommand.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,24 @@ protected override DbDataReader ExecuteDbDataReader(System.Data.CommandBehavior
122122

123123
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
124124
{
125-
return ExecuteDbDataReader(behavior);
125+
try
126+
{
127+
if (this.Connection.State == ConnectionState.Open)
128+
{
129+
Prepare();
130+
131+
return await _exec.ExecuteDataReaderAsync(this, cancellationToken)
132+
.ConfigureAwait(false);
133+
}
134+
else
135+
{
136+
throw new InvalidOperationException(MockDBErrors.ConnectionStateNotOpen);
137+
}
138+
}
139+
catch (Exception ex)
140+
{
141+
throw new MockDBException(ex.Message, ex);
142+
}
126143
}
127144

128145
public override int ExecuteNonQuery()

SubSonic.Extensions.Test/MockDbClient/MockDbConnection.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace SubSonic.Extensions.Test.MockDbClient
99
{
1010
public class MockDbConnection : DbConnection
1111
{
12-
IMockCommandExecution exec;
12+
readonly IMockCommandExecution exec;
1313
ConnectionState state;
1414
internal MockDbConnection(IMockCommandExecution exec)
1515
{
@@ -38,8 +38,10 @@ public override string ConnectionString
3838

3939
protected override DbCommand CreateDbCommand()
4040
{
41-
var cmd = new MockDbCommand(exec);
42-
cmd.Connection = this;
41+
var cmd = new MockDbCommand(exec)
42+
{
43+
Connection = this
44+
};
4345
return cmd;
4446
}
4547

SubSonic.Extensions.Test/Models/Person.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ public class Person
2828
public string FullName { get; set; }
2929

3030
public virtual ICollection<Renter> Renters { get; set; }
31+
32+
public override string ToString() => FullName;
3133
}
3234
}

SubSonic.Tests/DAL/Asynchronous/AsynchronousTests.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public override void SetupTestFixture()
2424
base.SetupTestFixture();
2525

2626
string
27+
people_all = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
28+
FROM [dbo].[Person] AS [T1]",
2729
people_equal = @"SELECT [T1].[ID], [T1].[FirstName], [T1].[MiddleInitial], [T1].[FamilyName], [T1].[FullName]
2830
FROM [dbo].[Person] AS [T1]
2931
WHERE ([T1].[ID] = @id_1)",
@@ -34,6 +36,7 @@ FROM [dbo].[Person] AS [T1]
3436
FROM [dbo].[Person] AS [T1]
3537
WHERE ([T1].[ID] < @id_1)";
3638

39+
Context.Database.Instance.AddCommandBehavior(people_all, cmd => People.ToDataTable());
3740
Context.Database.Instance.AddCommandBehavior(people_greater_than, cmd => People.Where(x => x.ID > cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
3841
Context.Database.Instance.AddCommandBehavior(people_equal, cmd => People.Where(x => x.ID == cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
3942
Context.Database.Instance.AddCommandBehavior(people_less_than, cmd => People.Where(x => x.ID < cmd.Parameters["@id_1"].GetValue<int>()).ToDataTable());
@@ -133,7 +136,7 @@ public void ShouldBeAbleToNotThrowFirstOrDefaultAsyncOnNull()
133136
{
134137
Person person = await Context.People.Where(x => x.ID < 1)
135138
.AsAsyncSubSonicQueryable()
136-
.SingleAsync();
139+
.FirstOrDefaultAsync();
137140

138141
person.Should().BeNull();
139142

@@ -145,18 +148,29 @@ public void ShouldBeAbleToLoadResultSet()
145148
{
146149
FluentActions.Invoking(async () =>
147150
{
148-
ISubSonicCollection<Person> people = await Context.People
151+
var cts = new CancellationTokenSource();
152+
153+
var people = Context.People
149154
.AsAsyncSubSonicQueryable()
150-
.LoadAsync();
155+
.LoadAsync(cts.Token);
156+
157+
int cnt = 0;
151158

152-
await foreach(Person person in people)
159+
await foreach(Person person in people.Result
160+
.WithCancellation(cts.Token)
161+
.ConfigureAwait(true))
153162
{
154163
person.FullName.Should().Be(String.Format("{0}, {1}{2}",
155164
person.FamilyName, person.FirstName,
156165
string.IsNullOrEmpty(person.MiddleInitial?.Trim()) ? "" : $" {person.MiddleInitial}."));
166+
167+
cnt++;
157168
}
158169

170+
cnt.Should().Be(Context.People.Count);
171+
159172
}).Should().NotThrow();
173+
160174
}
161175
}
162176
}

0 commit comments

Comments
 (0)