Skip to content

Commit 0d9de38

Browse files
committed
Fixed bug with Sql Bulk Insert/Update processing with Model Properties that have mapped database names via mapping attribute (e.g. [SqlBulkColumn("")], [Map("")], [Column("")], etc.).
1 parent fdde00f commit 0d9de38

File tree

5 files changed

+84
-8
lines changed

5 files changed

+84
-8
lines changed

NetStandard.SqlBulkHelpers/NetStandard.SqlBulkHelpers.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@
88
<PackageLicenseExpression>MIT</PackageLicenseExpression>
99
<Authors>BBernard / CajunCoding</Authors>
1010
<Company>CajunCoding</Company>
11-
<Version>2.3.0</Version>
11+
<Version>2.3.1</Version>
1212
<PackageProjectUrl>https://github.com/cajuncoding/SqlBulkHelpers</PackageProjectUrl>
1313
<RepositoryUrl>https://github.com/cajuncoding/SqlBulkHelpers</RepositoryUrl>
1414
<Description>A library for easy, efficient and high performance bulk insert and update of data, into a Sql Database, from .Net applications. By leveraging the power of the SqlBulkCopy classes with added support for Identity primary key table columns this library provides a greatly simplified interface to process Identity based Entities with Bulk Performance with the wide compatibility of .NetStandard 2.0.</Description>
1515
<PackageTags>sql server database table bulk insert update identity column sqlbulkcopy orm dapper linq2sql materialization materialized data view materialized-data materialized-view sync replication replica readonly</PackageTags>
1616
<PackageReleaseNotes>
17+
- Fixed bug with Sql Bulk Insert/Update processing with Model Properties that have mapped database names via mapping attribute (e.g. [SqlBulkColumn("")], [Map("")], [Column("")], etc.).
18+
19+
Prior Relese Notes:
1720
- Changed default behaviour to no longer clone tables/schema inside a Transaction which creates a full Schema Lock -- as this greatly impacts Schema aware ORMs such as SqlBulkHelpers, RepoDb, etc.
1821
- New separate methods is now added to handle the CleanupMaterializeDataProcessAsync() but must be explicitly called as it is no longer implicitly called with FinishMaterializeDataProcessAsync().
1922
- Added new configuration value to control if Schema copying/cloning (for Loading Tables) is inside or outide the Transaction (e.g. SchemaCopyMode.InsideTransactionAllowSchemaLocks vs OutsideTransactionAvoidSchemaLocks).
2023
- Fix bug in ReSeedTableIdentityValueWithMaxIdAsync() when the Table is Empty so that it now defaults to value of 1.
21-
22-
Prior Relese Notes:
2324
- Fixed a Bug where Identity Column Value was not correctly synced after Materialization Process is completing.
2425
- Added new Helper API to quickly Sync the Identity column value with the current MAX Id value of the column (ensuring it's valid after populating additional data); This is useful if you override Identity values for a full Table refresh, but then want to later insert data into the table.
2526
- Improved namespace for SqlBulkHelpers.CustomExtensions to reduce risk of conflicts with similar existing extensions.

NetStandard.SqlBulkHelpers/SqlBulkHelper/QueryProcessing/SqlBulkHelpersDataReader.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public bool Read()
4949
return _dataEnumerator.MoveNext();
5050
}
5151

52-
public int GetOrdinal(string name)
52+
public int GetOrdinal(string dbColumnName)
5353
{
5454
//Lazy Load the Ordinal reverse lookup dictionary (ONLY if needed)
5555
if (_processingDefinitionOrdinalDictionary == null)
@@ -58,15 +58,15 @@ public int GetOrdinal(string name)
5858
int i = 0;
5959
_processingDefinitionOrdinalDictionary = new Dictionary<string, int>();
6060
foreach (var propDef in _processingFields)
61-
_processingDefinitionOrdinalDictionary[propDef.PropertyName] = i++;
61+
_processingDefinitionOrdinalDictionary[propDef.MappedDbColumnName] = i++;
6262
}
6363

64-
if (SqlBulkHelpersConstants.ROWNUMBER_COLUMN_NAME.Equals(name))
64+
if (SqlBulkHelpersConstants.ROWNUMBER_COLUMN_NAME.Equals(dbColumnName))
6565
return _rowNumberPseudoColumnOrdinal;
66-
else if (_processingDefinitionOrdinalDictionary.TryGetValue(name, out var ordinalIndex))
66+
else if (_processingDefinitionOrdinalDictionary.TryGetValue(dbColumnName, out var ordinalIndex))
6767
return ordinalIndex;
6868

69-
throw new ArgumentOutOfRangeException($"Property name [{name}] could not be found.");
69+
throw new ArgumentOutOfRangeException($"Property name [{dbColumnName}] could not be found.");
7070
}
7171

7272
public object GetValue(int i)

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,16 @@ public class TestDataService
215215
## Nuget Package
216216
To use in your project, add the [SqlBulkHelpers NuGet package](https://www.nuget.org/packages/SqlBulkHelpers/) to your project.
217217

218+
## v2.3.1 Release Notes:
219+
- Fixed bug with Sql Bulk Insert/Update processing with Model Properties that have mapped database names via mapping attribute (e.g. [SqlBulkColumn("")], [Map("")], [Column("")], etc.).
220+
221+
## v2.3 Release Notes:
222+
- Changed default behaviour to no longer clone tables/schema inside a Transaction which creates a full Schema Lock -- as this greatly impacts Schema aware ORMs such as SqlBulkHelpers, RepoDb, etc.
223+
- Note: If you are manually orchestrating your process using StartMaterializedDataProcessAsync() and FinishMaterializeDataProcessAsync() then you now need to handle this by explicitly calling CleanupMaterializeDataProcessAsync() in a Try/Finally.
224+
- Added new configuration value to control if Schema copying/cloning (for Loading Tables) is inside or outide the Transaction (e.g. SchemaCopyMode.InsideTransactionAllowSchemaLocks vs OutsideTransactionAvoidSchemaLocks).
225+
- Fix bug in ReSeedTableIdentityValueWithMaxIdAsync() when the Table is Empty so that it now defaults to value of 1.
226+
227+
218228
## v2.2.2 Release Notes:
219229
- Improved namespace for SqlBulkHelpers.CustomExtensions to reduce risk of conflicts with similar existing extensions.
220230

SqlBulkHelpers.SampleApp.Common/TestHelpers.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ public class ChildTestElement
103103
[SqlBulkTable(TestHelpers.TestTableName, uniqueMatchMergeValidationEnabled: false)]
104104
public class TestElementWithMappedNames
105105
{
106+
public TestElementWithMappedNames()
107+
{
108+
}
109+
110+
public TestElementWithMappedNames(TestElement testElement)
111+
{
112+
MyId = testElement.Id;
113+
MyKey = testElement.Key;
114+
MyValue = testElement.Value;
115+
UnMappedProperty = -1;
116+
}
117+
106118
[SqlBulkMatchQualifier]
107119
[Map("Id")]
108120
public int MyId { get; set; }

SqlBulkHelpers.Tests/IntegrationTests/SqlBulkLoadingTests/BulkInsertOrUpdateTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,58 @@ public async Task TestBulkInsertOrUpdateWithMultipleCustomMatchQualifiersAsync()
141141
}
142142
}
143143
}
144+
145+
[TestMethod]
146+
public async Task TestBulkInsertOrUpdateWithMappedPropertiesAndCustomMatchQualifiersAsync()
147+
{
148+
var testDataWithMappedProps = TestHelpers
149+
.CreateTestData(10)
150+
.Select(t =>
151+
{
152+
var r = new TestElementWithMappedNames(t);
153+
r.MyKey = $"MULTIPLE_QUALIFIER_TEST-{r.MyKey}";
154+
return r;
155+
})
156+
.ToList();
157+
158+
var sqlConnectionString = SqlConnectionHelper.GetSqlConnectionString();
159+
ISqlBulkHelpersConnectionProvider sqlConnectionProvider = new SqlBulkHelpersConnectionProvider(sqlConnectionString);
160+
161+
await using var sqlConn = await sqlConnectionProvider.NewConnectionAsync().ConfigureAwait(false);
162+
await using (var sqlTrans = (SqlTransaction)await sqlConn.BeginTransactionAsync().ConfigureAwait(false))
163+
{
164+
var results = await sqlTrans.BulkInsertOrUpdateAsync(
165+
testDataWithMappedProps,
166+
TestHelpers.TestTableName,
167+
new SqlMergeMatchQualifierExpression(
168+
nameof(TestElement.Value),
169+
nameof(TestElement.Key) //This will still result in UNIQUE entries that are being inserted (NO UPDATES)
170+
)
171+
);
172+
173+
await sqlTrans.CommitAsync().ConfigureAwait(false);
174+
175+
//ASSERT Results are Valid...
176+
Assert.IsNotNull(results);
177+
178+
//We Sort the Results by Identity Id to ensure that the inserts occurred in the correct
179+
// ordinal order matching our Array of original values, but now with incrementing ID values!
180+
//This validates that data is inserted as expected for Identity columns and is validated
181+
// correctly by sorting on the Incrementing Identity value when Queried (e.g. ORDER BY Id)
182+
// which must then match our original order of data.
183+
var resultsSorted = results.OrderBy(r => r.MyId).ToList();
184+
Assert.AreEqual(resultsSorted.Count(), testDataWithMappedProps.Count);
185+
186+
var i = 0;
187+
foreach (var result in resultsSorted)
188+
{
189+
Assert.IsNotNull(result);
190+
Assert.IsTrue(result.MyId > 0);
191+
Assert.AreEqual((object)result.MyKey, testDataWithMappedProps[i].MyKey);
192+
Assert.AreEqual((object)result.MyValue, testDataWithMappedProps[i].MyValue);
193+
i++;
194+
}
195+
}
196+
}
144197
}
145198
}

0 commit comments

Comments
 (0)