Skip to content

Commit 534ef0e

Browse files
committed
Fix issues with UpdateBulk method set.
* Error in condition to detect objects to update. Added additional condition so that non Dapper contrib proxies are also processed * Fixed issue with bulk transfer not transfering all columns to the database and thus not being able to update records properly * Added test for UpdateBulk
1 parent b0112db commit 534ef0e

File tree

6 files changed

+167
-33
lines changed

6 files changed

+167
-33
lines changed

Simpleverse.Dapper.Test/Simpleverse.Dapper.Test.csproj

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Dapper.Contrib" Version="2.0.35" />
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
12-
<PackageReference Include="xunit" Version="2.4.0" />
13-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
14-
<PackageReference Include="coverlet.collector" Version="1.2.0" />
10+
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
12+
<PackageReference Include="xunit" Version="2.4.1" />
13+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
</PackageReference>
17+
<PackageReference Include="coverlet.collector" Version="3.0.3">
18+
<PrivateAssets>all</PrivateAssets>
19+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20+
</PackageReference>
1521
</ItemGroup>
1622

1723
<ItemGroup>

Simpleverse.Dapper.Test/SqlServer/DatabaseFixture.cs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,15 @@ public class DatabaseFixture : IDisposable
1515

1616
public void Dispose()
1717
{
18-
using (var connection = new SqlConnection(ConnectionString))
19-
{
20-
connection.Open();
21-
connection.Execute($@"{DropTable("[Identity]")}");
22-
connection.Execute($@"{DropTable("[ExplicitKey]")}");
23-
connection.Execute($@"{DropTable("[IdentityAndExplict]")}");
24-
connection.Execute($@"{DropTable("[Computed]")}");
25-
connection.Execute($@"{DropTable("[Write]")}");
26-
connection.Execute($@"{DropTable("[DataType]")}");
27-
connection.Execute($@"{DropTable("[10_Escape]")}");
28-
29-
var schemaName = "test";
30-
31-
connection.Execute($@"{DropTable($@"{schemaName}.[10_Escape]")}");
32-
connection.Execute($@"{DropSchema(schemaName)}");
33-
}
18+
TearDownDb();
3419
}
3520

3621
public DatabaseFixture()
22+
{
23+
SetupDb();
24+
}
25+
26+
public void SetupDb()
3727
{
3828
using (var connection = new SqlConnection(ConnectionString))
3929
{
@@ -117,5 +107,25 @@ [NoId] INT NOT NULL
117107
);");
118108
}
119109
}
110+
111+
public void TearDownDb()
112+
{
113+
using (var connection = new SqlConnection(ConnectionString))
114+
{
115+
connection.Open();
116+
connection.Execute($@"{DropTable("[Identity]")}");
117+
connection.Execute($@"{DropTable("[ExplicitKey]")}");
118+
connection.Execute($@"{DropTable("[IdentityAndExplict]")}");
119+
connection.Execute($@"{DropTable("[Computed]")}");
120+
connection.Execute($@"{DropTable("[Write]")}");
121+
connection.Execute($@"{DropTable("[DataType]")}");
122+
connection.Execute($@"{DropTable("[10_Escape]")}");
123+
124+
var schemaName = "test";
125+
126+
connection.Execute($@"{DropTable($@"{schemaName}.[10_Escape]")}");
127+
connection.Execute($@"{DropSchema(schemaName)}");
128+
}
129+
}
120130
}
121131
}

Simpleverse.Dapper.Test/SqlServer/InsertTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public static object[] ExplicitKeyTest(int count) =>
121121
count
122122
);
123123

124-
public object[] IdentityAndExplictTest(int count) =>
124+
public static object[] IdentityAndExplictTest(int count) =>
125125
Generate(
126126
nameof(IdentityAndExplictTest),
127127
TestData.IdentityAndExplictData,
@@ -132,7 +132,7 @@ public object[] IdentityAndExplictTest(int count) =>
132132
count
133133
);
134134

135-
public object[] ComputedTest(int count) =>
135+
public static object[] ComputedTest(int count) =>
136136
Generate(
137137
nameof(ComputedTest),
138138
TestData.ComputedData,
@@ -143,7 +143,7 @@ public object[] ComputedTest(int count) =>
143143
count
144144
);
145145

146-
public object[] WriteTest(int count) =>
146+
public static object[] WriteTest(int count) =>
147147
Generate(
148148
nameof(WriteTest),
149149
TestData.WriteData,
@@ -155,7 +155,7 @@ public object[] WriteTest(int count) =>
155155
count
156156
);
157157

158-
public object[] DataTypeTest(int count) =>
158+
public static object[] DataTypeTest(int count) =>
159159
Generate(
160160
nameof(DataTypeTest),
161161
TestData.DataTypeData,
@@ -168,7 +168,7 @@ public object[] DataTypeTest(int count) =>
168168
count
169169
);
170170

171-
public object[] DataTypeNullableTest(int count) =>
171+
public static object[] DataTypeNullableTest(int count) =>
172172
Generate(
173173
nameof(DataTypeNullableTest),
174174
TestData.DataTypeNullableData,
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Xunit;
4+
using Dapper.Contrib.Extensions;
5+
using System.Linq;
6+
using System.Data;
7+
using System.Collections;
8+
using Simpleverse.Dapper.SqlServer;
9+
using Dapper;
10+
11+
namespace Simpleverse.Dapper.Test.SqlServer
12+
{
13+
[Collection("SqlServerCollection")]
14+
public class UpdateTests : IClassFixture<DatabaseFixture>
15+
{
16+
DatabaseFixture fixture;
17+
18+
public UpdateTests(DatabaseFixture fixture)
19+
{
20+
this.fixture = fixture;
21+
}
22+
23+
[Theory]
24+
[ClassData(typeof(UpdateTestData))]
25+
public void UpdateTest<T>(string testName, IEnumerable<T> records, Action<T, T> check, int expected) where T : Identity
26+
{
27+
using (var connection = fixture.GetConnection())
28+
{
29+
Arange<T>(connection);
30+
31+
var inserted = connection.Insert(records);
32+
records = records.Skip(1);
33+
foreach (var record in records)
34+
record.Name = (record.Id + 2).ToString();
35+
36+
// act
37+
var updated = connection.UpdateBulkAsync(records).Result;
38+
39+
// assert
40+
Assert(connection, records, check, records.Count(), updated);
41+
}
42+
}
43+
44+
[Theory]
45+
[ClassData(typeof(UpdateTestData))]
46+
public void UpdateTransactionAsyncTest<T>(string testName, IEnumerable<T> records, Action<T, T> check, int expected) where T : Identity
47+
{
48+
using (var connection = fixture.GetConnection())
49+
{
50+
Arange<T>(connection);
51+
52+
var inserted = connection.Insert(records);
53+
records = records.Skip(1);
54+
foreach (var record in records)
55+
record.Name = (record.Id + 2).ToString();
56+
57+
// act
58+
int updated = 0;
59+
using (var transaction = connection.BeginTransaction())
60+
{
61+
updated = connection.UpdateBulkAsync(records, transaction: transaction).Result;
62+
transaction.Commit();
63+
}
64+
65+
Assert<T>(connection, records, check, records.Count(), updated);
66+
}
67+
}
68+
69+
private void Arange<T>(IDbConnection connection) where T : class
70+
{
71+
connection.Open();
72+
fixture.TearDownDb();
73+
fixture.SetupDb();
74+
}
75+
76+
private void Assert<T>(IDbConnection connection, IEnumerable<T> records, Action<T, T> check, int expected, int updated) where T : Identity
77+
{
78+
IEnumerable<T> updatedRecords = null;
79+
80+
// Workaround for Dapper.Contrib not supporting composite keys
81+
if (typeof(T).Equals(typeof(IdentityAndExplict)))
82+
updatedRecords = connection.Query<T>("SELECT * FROM [IdentityAndExplict]");
83+
else
84+
updatedRecords = connection.GetAll<T>();
85+
86+
Xunit.Assert.Equal(expected, updated);
87+
Xunit.Assert.Equal("1", updatedRecords.First(x => x.Id == 1).Name);
88+
for (int i = 0; i < records.Count(); i++)
89+
{
90+
var record = records.ElementAt(i);
91+
var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
92+
93+
check(record, updatedRecord);
94+
}
95+
}
96+
}
97+
98+
public class UpdateTestData : IEnumerable<object[]>
99+
{
100+
public IEnumerator<object[]> GetEnumerator()
101+
{
102+
yield return InsertTestData.IdentityTest(2);
103+
yield return InsertTestData.IdentityTest(10);
104+
yield return InsertTestData.IdentityAndExplictTest(2);
105+
yield return InsertTestData.IdentityAndExplictTest(10);
106+
yield return InsertTestData.ComputedTest(2);
107+
yield return InsertTestData.ComputedTest(10);
108+
yield return InsertTestData.WriteTest(2);
109+
yield return InsertTestData.WriteTest(10);
110+
yield return InsertTestData.DataTypeTest(2);
111+
yield return InsertTestData.DataTypeTest(10);
112+
yield return InsertTestData.DataTypeNullableTest(2);
113+
yield return InsertTestData.DataTypeNullableTest(10);
114+
}
115+
116+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
117+
}
118+
}

Simpleverse.Dapper/Simpleverse.Dapper.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</PropertyGroup>
2121

2222
<ItemGroup>
23-
<PackageReference Include="Dapper.Contrib" Version="2.0.35" />
23+
<PackageReference Include="Dapper.Contrib" Version="2.0.78" />
2424
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" />
2525
</ItemGroup>
2626

Simpleverse.Dapper/SqlServer/BulkExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public static async Task<string> TransferBulkAsync<T>(
2525
return await connection.TransferBulkAsync(
2626
entitiesToInsert,
2727
meta.TableName,
28-
meta.PropertiesExceptKeyAndComputed,
28+
meta.Properties,
2929
transaction: transaction,
3030
sqlBulkCopy: sqlBulkCopy
3131
);
@@ -43,7 +43,7 @@ public static async Task<string> TransferBulkAsync<T>(
4343
if (!columnsToCopy.Any())
4444
return string.Empty;
4545

46-
var insertedTableName = $"#Tbl_{Guid.NewGuid().ToString().Replace("-", string.Empty)}";
46+
var insertedTableName = $"tbl_{Guid.NewGuid().ToString().Replace("-", string.Empty)}";
4747

4848
connection.Execute(
4949
$@"SELECT TOP 0 {columnsToCopy.ColumnList()} INTO {insertedTableName} FROM {tableName} WITH(NOLOCK)
@@ -248,17 +248,17 @@ public async static Task<int> InsertBulkAsync<T>(
248248
/// <param name="transaction">The transaction to run under, null (the default) if none</param>
249249
/// <param name="commandTimeout">Number of seconds before command execution timeout</param>
250250
/// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
251-
public async static Task<bool> UpdateBulkAsync<T>(
251+
public async static Task<int> UpdateBulkAsync<T>(
252252
this SqlConnection connection,
253253
IEnumerable<T> entitiesToUpdate,
254254
SqlTransaction transaction = null,
255255
int? commandTimeout = null,
256256
Action<SqlBulkCopy> sqlBulkCopy = null
257257
) where T : class
258258
{
259-
entitiesToUpdate = entitiesToUpdate.Where(x => x is SqlMapperExtensions.IProxy proxy && !proxy.IsDirty);
259+
entitiesToUpdate = entitiesToUpdate.Where(x => (x is SqlMapperExtensions.IProxy proxy && !proxy.IsDirty) || !(x is SqlMapperExtensions.IProxy));
260260
if (!entitiesToUpdate.Any())
261-
return false;
261+
return 0;
262262

263263
var typeMeta = TypeMeta.Get<T>();
264264
if (typeMeta.PropertiesKey.Count == 0 && typeMeta.PropertiesExplicit.Count == 0)
@@ -283,7 +283,7 @@ INNER JOIN { typeMeta.TableName } AS Target
283283

284284
if (wasClosed) connection.Close();
285285

286-
return updated > 0;
286+
return updated;
287287
}
288288

289289
/// <summary>

0 commit comments

Comments
 (0)