Skip to content

Commit bd43454

Browse files
Add SqlServerDatabase.InsertIntoAsync() method.
1 parent 4e94659 commit bd43454

3 files changed

Lines changed: 117 additions & 33 deletions

File tree

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Add SqlServer.CreateEmptyDatabaseAsync() method.
1616
- Add SqlServer.DeleteDatabaseAsync() method.
1717
- Add SqlServer.ExecuteScriptAsync() method.
18+
- Add SqlServer.InsertIntoAsync() method.
1819

1920
2.1.0
2021
- PosInformatique.Testing.Databases.SqlServer target the .NET Standard 2.0 platform.

src/Testing.Databases.SqlServer/SqlServerDatabaseExtensions.cs

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -55,43 +55,43 @@ public static int InsertInto<T>(this SqlServerDatabase database, string tableNam
5555
/// <returns>The number of the rows inserted.</returns>
5656
public static int InsertInto<T>(this SqlServerDatabase database, string tableName, bool disableIdentityInsert, params T[] objects)
5757
{
58-
var builder = new SqlInsertStatementBuilder(tableName);
59-
var properties = typeof(T).GetProperties();
60-
61-
foreach (var property in properties)
62-
{
63-
builder.AddColumn(property.Name);
64-
}
65-
66-
foreach (var @object in objects)
67-
{
68-
foreach (var property in properties)
69-
{
70-
_ = property.PropertyType switch
71-
{
72-
_ when property.GetValue(@object) == null => builder.AddValue("NULL"),
73-
_ when property.PropertyType == typeof(bool) => builder.AddValue(Convert.ToString(Convert.ToInt32((bool)property.GetValue(@object)!), CultureInfo.InvariantCulture)!),
74-
_ when property.PropertyType == typeof(bool?) => builder.AddValue(Convert.ToString(Convert.ToInt32(((bool?)property.GetValue(@object)!).Value), CultureInfo.InvariantCulture)!),
75-
_ when property.PropertyType == typeof(byte[]) => builder.AddValue((byte[])property.GetValue(@object)!),
76-
Type t when Array.Exists(AuthorizedNonStringTypes, at => at == t) => builder.AddValue(Convert.ToString(property.GetValue(@object), CultureInfo.InvariantCulture)!),
77-
_ => builder.AddValueWithQuotes((string)property.GetValue(@object)!),
78-
};
79-
}
58+
var statement = BuildInsertStatement(tableName, disableIdentityInsert, objects);
8059

81-
if (!@object!.Equals(objects[objects.Length - 1]))
82-
{
83-
builder.NewRecord();
84-
}
85-
}
60+
return database.ExecuteNonQuery(statement);
61+
}
8662

87-
var statement = builder.ToString();
63+
/// <summary>
64+
/// Insert data into the table asynchronously specified by the <paramref name="tableName"/> argument. The row
65+
/// to insert are represents by objects (or anonymous objects) which the property name must match the
66+
/// the column name.
67+
/// </summary>
68+
/// <typeparam name="T">Type of the object which contains the data to insert in the table.</typeparam>
69+
/// <param name="database">SQL Server database which contains the table where the data will be inserted.</param>
70+
/// <param name="tableName">SQL table where the data will be inserted.</param>
71+
/// <param name="objects">Set of object which represents the row to insert. Each object must have property which are mapped to the column to insert.</param>
72+
/// <returns>A <see cref="Task"/> which represents the asynchronous operation and contains the number of the rows inserted.</returns>
73+
public static async Task<int> InsertIntoAsync<T>(this SqlServerDatabase database, string tableName, params T[] objects)
74+
{
75+
return await InsertIntoAsync(database, tableName, false, objects);
76+
}
8877

89-
if (disableIdentityInsert)
90-
{
91-
statement = $"SET IDENTITY_INSERT [{tableName}] ON;" + statement + $"SET IDENTITY_INSERT [{tableName}] OFF;";
92-
}
78+
/// <summary>
79+
/// Insert data into the table asynchronously specified by the <paramref name="tableName"/> argument. The row
80+
/// to insert are represents by objects (or anonymous objects) which the property name must match the
81+
/// the column name.
82+
/// </summary>
83+
/// <typeparam name="T">Type of the object which contains the data to insert in the table.</typeparam>
84+
/// <param name="database">SQL Server database which contains the table where the data will be inserted.</param>
85+
/// <param name="tableName">SQL table where the data will be inserted.</param>
86+
/// <param name="disableIdentityInsert"><see langword="true"/> to disable auto incrementation of the <c>IDENTITY</c> column. In this case, the object must contains explicitely the value of the <c>IDENTITY</c>
87+
/// column to insert.</param>
88+
/// <param name="objects">Set of object which represents the row to insert. Each object must have property which are mapped to the column to insert.</param>
89+
/// <returns>A <see cref="Task"/> which represents the asynchronous operation and contains the number of the rows inserted.</returns>
90+
public static Task<int> InsertIntoAsync<T>(this SqlServerDatabase database, string tableName, bool disableIdentityInsert, params T[] objects)
91+
{
92+
var statement = BuildInsertStatement(tableName, disableIdentityInsert, objects);
9393

94-
return database.ExecuteNonQuery(statement);
94+
return database.ExecuteNonQueryAsync(statement);
9595
}
9696

9797
/// <summary>
@@ -202,6 +202,47 @@ public static async Task ExecuteScriptAsync(this SqlServerDatabase database, Str
202202
}
203203
}
204204

205+
private static string BuildInsertStatement<T>(string tableName, bool disableIdentityInsert, params T[] objects)
206+
{
207+
var builder = new SqlInsertStatementBuilder(tableName);
208+
var properties = typeof(T).GetProperties();
209+
210+
foreach (var property in properties)
211+
{
212+
builder.AddColumn(property.Name);
213+
}
214+
215+
foreach (var @object in objects)
216+
{
217+
foreach (var property in properties)
218+
{
219+
_ = property.PropertyType switch
220+
{
221+
_ when property.GetValue(@object) == null => builder.AddValue("NULL"),
222+
_ when property.PropertyType == typeof(bool) => builder.AddValue(Convert.ToString(Convert.ToInt32((bool)property.GetValue(@object)!), CultureInfo.InvariantCulture)!),
223+
_ when property.PropertyType == typeof(bool?) => builder.AddValue(Convert.ToString(Convert.ToInt32(((bool?)property.GetValue(@object)!).Value), CultureInfo.InvariantCulture)!),
224+
_ when property.PropertyType == typeof(byte[]) => builder.AddValue((byte[])property.GetValue(@object)!),
225+
Type t when Array.Exists(AuthorizedNonStringTypes, at => at == t) => builder.AddValue(Convert.ToString(property.GetValue(@object), CultureInfo.InvariantCulture)!),
226+
_ => builder.AddValueWithQuotes((string)property.GetValue(@object)!),
227+
};
228+
}
229+
230+
if (!@object!.Equals(objects[objects.Length - 1]))
231+
{
232+
builder.NewRecord();
233+
}
234+
}
235+
236+
var statement = builder.ToString();
237+
238+
if (disableIdentityInsert)
239+
{
240+
statement = $"SET IDENTITY_INSERT [{tableName}] ON;" + statement + $"SET IDENTITY_INSERT [{tableName}] OFF;";
241+
}
242+
243+
return statement;
244+
}
245+
205246
private sealed class SqlInsertStatementBuilder
206247
{
207248
private readonly string tableName;

tests/Testing.Databases.SqlServer.Tests/SqlServerDatabaseInitializerTest.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,47 @@ public void Test2()
6666
// Insert a row which should not be use in other tests.
6767
this.database.InsertInto("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
6868
}
69+
70+
[Fact]
71+
public async Task Test1Async()
72+
{
73+
var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()");
74+
currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
75+
76+
// Check the constructor has been called
77+
var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable");
78+
79+
table.Rows.Should().HaveCount(2);
80+
81+
table.Rows[0]["Id"].Should().Be(1);
82+
table.Rows[0]["Name"].Should().Be("Name 1");
83+
84+
table.Rows[1]["Id"].Should().Be(2);
85+
table.Rows[1]["Name"].Should().Be("Name 2");
86+
87+
// Insert a row which should not be use in other tests.
88+
await this.database.InsertIntoAsync("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
89+
}
90+
91+
[Fact]
92+
public async Task Test2Async()
93+
{
94+
var currentUser = await this.database.ExecuteQueryAsync("SELECT SUSER_NAME()");
95+
currentUser.Rows[0][0].Should().Be($"{Environment.UserDomainName}\\{Environment.UserName}");
96+
97+
// Check the constructor has been called
98+
var table = await this.database.ExecuteQueryAsync("SELECT * FROM MyTable");
99+
100+
table.Rows.Should().HaveCount(2);
101+
102+
table.Rows[0]["Id"].Should().Be(1);
103+
table.Rows[0]["Name"].Should().Be("Name 1");
104+
105+
table.Rows[1]["Id"].Should().Be(2);
106+
table.Rows[1]["Name"].Should().Be("Name 2");
107+
108+
// Insert a row which should not be use in other tests.
109+
await this.database.InsertIntoAsync("MyTable", new { Id = 99, Name = "Should not be here for the next test" });
110+
}
69111
}
70112
}

0 commit comments

Comments
 (0)