Skip to content

Commit 49fb2e9

Browse files
author
Jason Valdez
committed
Refactored logic to allow for testing the batch size feature. As well, maybe, change out the bulkcopy utility.
1 parent 2699988 commit 49fb2e9

File tree

7 files changed

+200
-38
lines changed

7 files changed

+200
-38
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System.Collections.Generic;
2+
using System.Data.SqlClient;
3+
using System.Linq;
4+
using FluentAssertions;
5+
using ivaldez.Sql.IntegrationTests.Data;
6+
using ivaldez.Sql.SqlBulkLoader;
7+
using Xunit;
8+
9+
namespace ivaldez.Sql.IntegrationTests.BulkLoading
10+
{
11+
public class BulkLoaderForBatchSizeTests
12+
{
13+
public class SqlBulkCopyUtilitySpy: BulkLoader.ISqlBulkCopyUtility
14+
{
15+
public void BulkCopy<T>(string tableName,
16+
SqlConnection conn,
17+
SqlBulkCopyOptions options,
18+
BulkLoader.TargetProperty[] targetProperties,
19+
IEnumerable<T> toInsert)
20+
{
21+
BulkCopyCalled++;
22+
23+
new BulkLoader.SqlBulkCopyUtility()
24+
.BulkCopy(tableName,
25+
conn,
26+
options,
27+
targetProperties,
28+
toInsert);
29+
}
30+
31+
public int BulkCopyCalled { get; set; }
32+
}
33+
34+
35+
[Fact]
36+
public void ShouldBatchBulkCopyCommandsByOptionValue()
37+
{
38+
var testingDatabaseService = new TestingDatabaseService();
39+
testingDatabaseService.CreateTestDatabase();
40+
41+
var dataGateway = new TestingDataGateway(testingDatabaseService);
42+
43+
dataGateway.DropTable();
44+
dataGateway.CreateSingleSurrogateKeyTable();
45+
46+
var dtos = new[]
47+
{
48+
new SampleSurrogateKey
49+
{
50+
Pk = 100,
51+
TextValue = "JJ",
52+
IntValue = 100,
53+
DecimalValue = 100.99m
54+
},
55+
new SampleSurrogateKey
56+
{
57+
Pk = 200,
58+
TextValue = "ZZ",
59+
IntValue = 999,
60+
DecimalValue = 123.45m
61+
}
62+
};
63+
64+
var sqlBulkCopyUtilitySpy = new SqlBulkCopyUtilitySpy();
65+
66+
dataGateway.ExecuteWithConnection(conn =>
67+
{
68+
new BulkLoader(sqlBulkCopyUtilitySpy)
69+
.InsertWithOptions("Sample", conn, true, dtos)
70+
.SetBatchSize(1)
71+
.Execute();
72+
});
73+
74+
sqlBulkCopyUtilitySpy.BulkCopyCalled.Should().Be(2);
75+
76+
var databaseDtos = dataGateway.GetAllSampleSurrogateKey().ToArray();
77+
78+
var firstDto = databaseDtos.First(x => x.TextValue == "JJ");
79+
firstDto.IntValue.Should().Be(100);
80+
firstDto.DecimalValue.Should().Be(100.99m);
81+
82+
var secondDto = databaseDtos.First(x => x.TextValue == "ZZ");
83+
secondDto.IntValue.Should().Be(999);
84+
secondDto.DecimalValue.Should().Be(123.45m);
85+
}
86+
}
87+
}

src/IntegrationTests/BulkLoading/BulkLoaderForFieldOptionsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void ShouldRespectRenamedFields()
4040

4141
dataGateway.ExecuteWithConnection(conn =>
4242
{
43-
new BulkLoader()
43+
BulkLoaderFactory.Create()
4444
.InsertWithOptions("Sample", conn, true, dtos)
4545
.With(c => c.TextValueExtra, "TextValue")
4646
.With(c => c.IntValueExtra, "IntValue")
@@ -92,7 +92,7 @@ public void ShouldRespectTheWithoutOption()
9292

9393
dataGateway.ExecuteWithConnection(conn =>
9494
{
95-
new BulkLoader()
95+
BulkLoaderFactory.Create()
9696
.InsertWithOptions("Sample", conn, true, dtos)
9797
.Without("DecimalValue")
9898
.Without(t => t.IntValue)
@@ -141,7 +141,7 @@ public void ShouldHaveAccessToRenameRules()
141141
}
142142
};
143143

144-
var bulkLoader = new BulkLoader()
144+
var bulkLoader = BulkLoaderFactory.Create()
145145
.InsertWithOptions("Sample", null, true, dtos)
146146
.With(c => c.TextValueExtra, "TextValue")
147147
.With(c => c.IntValueExtra, "IntValue")

src/IntegrationTests/BulkLoading/BulkLoaderForPrimaryKeyTests.cs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace ivaldez.Sql.IntegrationTests.BulkLoading
1313
public class BulkLoaderForPrimaryKeyTests
1414
{
1515
[Fact]
16-
public void ShouldBulkLoadIntoPrimaryKey()
16+
public void ShouldInsertPrimaryKeyWhenKeepIdentityOptionIsTrue()
1717
{
1818
var testingDatabaseService = new TestingDatabaseService();
1919
testingDatabaseService.CreateTestDatabase();
@@ -43,7 +43,7 @@ public void ShouldBulkLoadIntoPrimaryKey()
4343

4444
dataGateway.ExecuteWithConnection(conn =>
4545
{
46-
new BulkLoader()
46+
BulkLoaderFactory.Create()
4747
.InsertWithOptions("Sample", conn, true, dtos)
4848
.Execute();
4949
});
@@ -60,5 +60,54 @@ public void ShouldBulkLoadIntoPrimaryKey()
6060
secondDto.IntValue.Should().Be(999);
6161
secondDto.DecimalValue.Should().Be(123.45m);
6262
}
63+
64+
[Fact]
65+
public void ShouldNotInsertPrimaryKeyWhenKeepIdentityOptionIsFalse()
66+
{
67+
var testingDatabaseService = new TestingDatabaseService();
68+
testingDatabaseService.CreateTestDatabase();
69+
70+
var dataGateway = new TestingDataGateway(testingDatabaseService);
71+
72+
dataGateway.DropTable();
73+
dataGateway.CreateSingleSurrogateKeyTable();
74+
75+
var dtos = new[]
76+
{
77+
new SampleSurrogateKey
78+
{
79+
Pk = 100,
80+
TextValue = "JJ",
81+
IntValue = 100,
82+
DecimalValue = 100.99m
83+
},
84+
new SampleSurrogateKey
85+
{
86+
Pk = 200,
87+
TextValue = "ZZ",
88+
IntValue = 999,
89+
DecimalValue = 123.45m
90+
}
91+
};
92+
93+
dataGateway.ExecuteWithConnection(conn =>
94+
{
95+
BulkLoaderFactory.Create()
96+
.InsertWithOptions("Sample", conn, false, dtos)
97+
.Execute();
98+
});
99+
100+
var databaseDtos = dataGateway.GetAllSampleSurrogateKey().ToArray();
101+
102+
var firstDto = databaseDtos.First(x => x.TextValue == "JJ");
103+
firstDto.Pk.Should().NotBe(100);
104+
firstDto.IntValue.Should().Be(100);
105+
firstDto.DecimalValue.Should().Be(100.99m);
106+
107+
var secondDto = databaseDtos.First(x => x.TextValue == "ZZ");
108+
secondDto.Pk.Should().NotBe(200);
109+
secondDto.IntValue.Should().Be(999);
110+
secondDto.DecimalValue.Should().Be(123.45m);
111+
}
63112
}
64113
}

src/IntegrationTests/BulkLoading/BulkLoaderGeneralTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void ShouldBulkLoad()
3939

4040
dataGateway.ExecuteWithConnection(conn =>
4141
{
42-
new BulkLoader()
42+
BulkLoaderFactory.Create()
4343
.InsertWithOptions("Sample", conn, true, dtos)
4444
.Without(c => c.Pk)
4545
.Execute();

src/IntegrationTests/IntegrationTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
</Reference>
6969
</ItemGroup>
7070
<ItemGroup>
71+
<Compile Include="BulkLoading\BulkLoaderForBatchSizeTests.cs" />
7172
<Compile Include="BulkLoading\BulkLoaderForFieldOptionsTests.cs" />
7273
<Compile Include="BulkLoading\BulkLoaderForPrimaryKeyTests.cs" />
7374
<Compile Include="BulkLoading\BulkLoaderGeneralTests.cs" />

src/ivaldez.SqlBulkLoader/BulkLoader.cs

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ namespace ivaldez.Sql.SqlBulkLoader
1313
/// </summary>
1414
public class BulkLoader : IBulkLoader
1515
{
16+
private readonly ISqlBulkCopyUtility _sqlBulkCopyUtility;
17+
18+
public BulkLoader(ISqlBulkCopyUtility sqlBulkCopyUtility)
19+
{
20+
_sqlBulkCopyUtility = sqlBulkCopyUtility;
21+
}
22+
1623
/// <summary>
1724
/// Bulk load with customizations.
1825
/// </summary>
@@ -103,42 +110,58 @@ public void Insert<T>(
103110

104111
if (batch.Count >= batchSize)
105112
{
106-
BulkCopy(tableName, conn, options, targetProperties, batch);
113+
_sqlBulkCopyUtility.BulkCopy(tableName, conn, options, targetProperties, batch);
107114
batch.Clear();
108115
}
109116
}
110117

111118
if (batch.Any())
112119
{
113-
BulkCopy(tableName, conn, options, targetProperties, batch);
120+
_sqlBulkCopyUtility.BulkCopy(tableName, conn, options, targetProperties, batch);
114121
batch.Clear();
115122
}
116123
}
117-
118-
private static void BulkCopy<T>(string tableName, SqlConnection conn, SqlBulkCopyOptions options,
119-
TargetProperty[] targetProperties, IEnumerable<T> toInsert)
124+
125+
public class SqlBulkCopyUtility: ISqlBulkCopyUtility
120126
{
121-
using (var bulkCopy = new SqlBulkCopy(conn, options, null))
127+
public void BulkCopy<T>(string tableName, SqlConnection conn, SqlBulkCopyOptions options,
128+
TargetProperty[] targetProperties, IEnumerable<T> toInsert)
122129
{
123-
var parameters = targetProperties.Select(x => x.OriginalName).ToArray();
124-
125-
126-
using (var reader = ObjectReader.Create(toInsert, parameters))
130+
using (var bulkCopy = new SqlBulkCopy(conn, options, null))
127131
{
128-
foreach (var property in targetProperties)
132+
var parameters = targetProperties.Select(x => x.OriginalName).ToArray();
133+
134+
using (var reader = ObjectReader.Create(toInsert, parameters))
129135
{
130-
bulkCopy.ColumnMappings.Add(property.OriginalName, property.Name);
131-
}
136+
foreach (var property in targetProperties)
137+
{
138+
bulkCopy.ColumnMappings.Add(property.OriginalName, property.Name);
139+
}
132140

133-
bulkCopy.BulkCopyTimeout = 900;
134-
bulkCopy.DestinationTableName = tableName;
135-
bulkCopy.WriteToServer(reader);
141+
bulkCopy.BulkCopyTimeout = 900;
142+
bulkCopy.DestinationTableName = tableName;
143+
bulkCopy.WriteToServer(reader);
136144

137-
bulkCopy.Close();
145+
bulkCopy.Close();
146+
}
138147
}
139148
}
140149
}
141150

151+
public interface ISqlBulkCopyUtility
152+
{
153+
void BulkCopy<T>(string tableName, SqlConnection conn, SqlBulkCopyOptions options,
154+
TargetProperty[] targetProperties, IEnumerable<T> toInsert);
155+
}
156+
157+
public class TargetProperty
158+
{
159+
public string Name { get; set; }
160+
public Type Type { get; set; }
161+
public PropertyInfo PropertyInfo { get; set; }
162+
public string OriginalName { get; set; }
163+
}
164+
142165
private static TargetProperty[] GetTargetProperties<T>(List<string> propertiesToIgnore,
143166
Dictionary<string, string> renameFields)
144167
{
@@ -169,11 +192,11 @@ private static TargetProperty[] GetTargetProperties<T>(List<string> propertiesTo
169192
}
170193
}
171194

172-
internal class TargetProperty
195+
public class BulkLoaderFactory
173196
{
174-
public string Name { get; set; }
175-
public Type Type { get; set; }
176-
public PropertyInfo PropertyInfo { get; set; }
177-
public string OriginalName { get; set; }
197+
public static IBulkLoader Create()
198+
{
199+
return new BulkLoader(new BulkLoader.SqlBulkCopyUtility());
200+
}
178201
}
179202
}

src/ivaldez.SqlBulkLoader/BulkLoaderContext.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ public BulkLoaderContext<T> Without(string name)
6060
return this;
6161
}
6262

63+
public IReadOnlyDictionary<string, string> GetRenameRules()
64+
{
65+
return _renameFields;
66+
}
67+
68+
public BulkLoaderContext<T> SetBatchSize(int value)
69+
{
70+
_batchSize = value;
71+
72+
return this;
73+
}
74+
6375
public void Execute()
6476
{
6577
_bulkLoader.Insert(
@@ -72,16 +84,6 @@ public void Execute()
7284
_batchSize);
7385
}
7486

75-
public IReadOnlyDictionary<string, string> GetRenameRules()
76-
{
77-
return _renameFields;
78-
}
79-
80-
public void SetBatchSize(int value)
81-
{
82-
_batchSize = value;
83-
}
84-
8587
private string GetName(Expression<Func<T, object>> expression)
8688
{
8789
var body = expression.Body as MemberExpression;

0 commit comments

Comments
 (0)