Skip to content

Commit 6157780

Browse files
authored
Merge pull request #3: Kros.Utils 1.10.1, BulkUpdate fix
- Update Kros.Utils to version 1.10.1 - Fix BulkUpdate to support composite primary keys
2 parents b70727f + 04d7df5 commit 6157780

17 files changed

+240
-85
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __Kros.Utils.MsAccess__ is a general library of various utilities to simplify th
44

55
For some (especially database) stuff to work properly, the library needs to be initialized when the program starts by calling [LibraryInitializer.InitLibrary](https://kros-sk.github.io/Kros.Libs.Documentation/api/Kros.Utils.MsAccess/Kros.Utils.MsAccess.LibraryInitializer.html#Kros_Utils_MsAccess_LibraryInitializer_InitLibrary "LibraryInitializer InitLibrary").
66

7-
Library is compiled for .NET Framework 4.7.
7+
Library is compiled for .NET Framework 4.6.
88

99
## Documentation
1010

src/Data/BulkActions/MsAccess/MsAccessBulkUpdate.cs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
using System.Data;
55
using System.Data.Common;
66
using System.Data.OleDb;
7+
using System.Diagnostics.CodeAnalysis;
78
using System.IO;
9+
using System.Linq;
810
using System.Threading.Tasks;
911

1012
namespace Kros.Data.BulkActions.MsAccess
@@ -91,13 +93,26 @@ protected override string GetTempTableName()
9193
}
9294

9395
/// <inheritdoc/>
94-
protected override void CreateTempTable(IDataReader reader, string tempTableName)
96+
[SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities")]
97+
protected override void CreateTempTableCore(IDataReader reader, string tempTableName)
9598
{
9699
using (var cmd = _tempDatabase.CreateCommand())
97100
{
98-
cmd.CommandText = $"SELECT {GetColumnNamesForTempTable(reader)} INTO {(tempTableName)} " +
99-
$"FROM [{(_connection as OleDbConnection).DataSource}].[{DestinationTableName}] " +
100-
$"WHERE (1 = 2)";
101+
cmd.CommandText =$"SELECT {GetColumnNamesForTempTable(reader, null)} INTO {(tempTableName)} " +
102+
$"FROM [{(_connection as OleDbConnection).DataSource}].[{DestinationTableName}] WHERE (1 = 2)";
103+
cmd.ExecuteNonQuery();
104+
}
105+
CreateTempTablePrimaryKey(tempTableName);
106+
}
107+
108+
private void CreateTempTablePrimaryKey(string tempTableName)
109+
{
110+
using (var cmd = CreateCommandForPrimaryKey())
111+
{
112+
string pkList = string.Join(", ", PrimaryKeyColumns.Select(item => $"[{item}]"));
113+
cmd.CommandText = $"ALTER TABLE [{tempTableName}] " +
114+
$"ADD CONSTRAINT [PK_{tempTableName.Trim(PrefixTempTable)}] " +
115+
$"PRIMARY KEY NONCLUSTERED ({pkList})";
101116
cmd.ExecuteNonQuery();
102117
}
103118
}
@@ -118,7 +133,12 @@ protected async override Task UpdateDestinationTableAsync(IDataReader reader, st
118133
{
119134
var tempDatabasePathAndTable = $"[{_tempDatabasePath}].[{tempTableName}]";
120135
var tempAlias = Guid.NewGuid();
121-
var innerJoin = $"[{DestinationTableName}].[{PrimaryKeyColumn}] = [{tempAlias}].[{PrimaryKeyColumn}]";
136+
var innerJoin = new System.Text.StringBuilder();
137+
foreach (string pkColumn in PrimaryKeyColumns)
138+
{
139+
innerJoin.Append($"([{DestinationTableName}].[{pkColumn}] = [{tempAlias}].[{pkColumn}]) AND ");
140+
}
141+
innerJoin.Length -= 5;
122142

123143
cmd.Transaction = ExternalTransaction;
124144
cmd.CommandText = $"UPDATE [{DestinationTableName}] " +

src/Kros.Utils.MsAccess.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
<SignAssembly>false</SignAssembly>
3939
</PropertyGroup>
4040
<ItemGroup>
41-
<Reference Include="Kros.Utils, Version=1.9.0.0, Culture=neutral, processorArchitecture=MSIL">
42-
<HintPath>..\packages\Kros.Utils.1.9.0\lib\net46\Kros.Utils.dll</HintPath>
41+
<Reference Include="Kros.Utils, Version=1.10.1.0, Culture=neutral, processorArchitecture=MSIL">
42+
<HintPath>..\packages\Kros.Utils.1.10.1\lib\net46\Kros.Utils.dll</HintPath>
4343
</Reference>
4444
<Reference Include="System" />
4545
<Reference Include="System.Core" />
@@ -50,7 +50,7 @@
5050
</Reference>
5151
<Reference Include="System.Data.DataSetExtensions" />
5252
<Reference Include="System.Data.SqlClient, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
53-
<HintPath>..\packages\System.Data.SqlClient.4.5.0\lib\net46\System.Data.SqlClient.dll</HintPath>
53+
<HintPath>..\packages\System.Data.SqlClient.4.7.0\lib\net46\System.Data.SqlClient.dll</HintPath>
5454
</Reference>
5555
<Reference Include="System.Net.Http" />
5656
<Reference Include="System.Xml" />

src/Kros.Utils.MsAccess.nuspec

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
<owners>$author$</owners>
99
<license type="expression">MIT</license>
1010
<projectUrl>https://github.com/Kros-sk/Kros.Utils.MsAccess/blob/master/README.md</projectUrl>
11-
<iconUrl>https://en.gravatar.com/userimage/137934964/524e95fbd8c2e8779e02819ab6902bef.png</iconUrl>
11+
<icon>icon.png</icon>
1212
<requireLicenseAcceptance>false</requireLicenseAcceptance>
1313
<description>$description$</description>
1414
<releaseNotes>https://github.com/Kros-sk/Kros.Utils.MsAccess/releases</releaseNotes>
1515
<copyright>Copyright © KROS a.s.</copyright>
1616
<tags>Kros;Utils;Utility;Helpers;Extensions;DatabaseSchema;BulkInsert;BulkOperation;BulkUpdate;Bulk Update;Bulk Insert;MsAccess</tags>
1717
<dependencies>
18-
<dependency id="Kros.Utils" version="1.7.2" />
18+
<dependency id="Kros.Utils" version="1.10.1" />
1919
</dependencies>
2020
</metadata>
21+
<files>
22+
<file src="Resources\icon.png" target="" />
23+
</files>
2124
</package>

src/Resources/icon.png

2.66 KB
Loading

src/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="Kros.Utils" version="1.9.0" targetFramework="net46" />
3+
<package id="Kros.Utils" version="1.10.1" targetFramework="net46" />
44
<package id="System.Data.Common" version="4.3.0" targetFramework="net46" />
5-
<package id="System.Data.SqlClient" version="4.5.0" targetFramework="net46" />
5+
<package id="System.Data.SqlClient" version="4.7.0" targetFramework="net46" />
66
</packages>

tests/Data/BulkInsert/MsAccessBulkActionFactoryShould.cs renamed to tests/Data/BulkActions/MsAccessBulkActionFactoryShould.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Data.OleDb;
44
using Xunit;
55

6-
namespace Kros.Utils.MsAccess.UnitTests.Data.BulkInsert
6+
namespace Kros.Utils.MsAccess.UnitTests.Data.BulkActions
77
{
88
public class MsAccessBulkActionFactoryShould
99
{
File renamed without changes.

tests/Data/BulkInsert/MsAccessBulkInsertShould.cs renamed to tests/Data/BulkActions/MsAccessBulkInsertShould.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using Kros.Data.BulkActions.MsAccess;
44
using Kros.Data.MsAccess;
55
using Kros.UnitTests;
6+
using Kros.Utils.UnitTests;
7+
using Kros.Utils.UnitTests.Data.BulkActions;
68
using Nito.AsyncEx;
79
using System;
810
using System.Collections.Generic;
@@ -13,7 +15,7 @@
1315
using System.Threading.Tasks;
1416
using Xunit;
1517

16-
namespace Kros.Utils.UnitTests.Data.BulkActions
18+
namespace Kros.Utils.MsAccess.UnitTests.Data.BulkActions
1719
{
1820
public class MsAccessBulkInsertShould
1921
{

tests/Data/BulkInsert/MsAccessBulkUpdateShould.cs renamed to tests/Data/BulkActions/MsAccessBulkUpdateShould.cs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
1-
using Kros.Data.BulkActions;
1+
using FluentAssertions;
2+
using Kros.Data.BulkActions;
23
using Kros.Data.BulkActions.MsAccess;
34
using Kros.Data.MsAccess;
45
using Kros.UnitTests;
6+
using Kros.Utils.UnitTests;
7+
using Kros.Utils.UnitTests.Data.BulkActions;
58
using Nito.AsyncEx;
69
using System;
710
using System.Collections.Generic;
811
using System.Data;
912
using System.Data.OleDb;
13+
using System.Diagnostics.CodeAnalysis;
1014
using System.IO;
1115
using System.Linq;
1216
using System.Reflection;
1317
using System.Threading.Tasks;
1418
using Xunit;
1519

16-
namespace Kros.Utils.UnitTests.Data.BulkActions
20+
namespace Kros.Utils.MsAccess.UnitTests.Data.BulkActions
1721
{
1822
public class MsAccessBulkUpdateShould
1923
{
@@ -32,13 +36,31 @@ private class BulkUpdateItem
3236
public BulkUpdateItem Clone() => (BulkUpdateItem)MemberwiseClone();
3337
}
3438

39+
private class BulkUpdateItemComposite
40+
{
41+
public int Id1 { get; set; }
42+
public int Id2 { get; set; }
43+
public string DataValue { get; set; }
44+
public override bool Equals(object obj)
45+
{
46+
if (obj is BulkUpdateItemComposite item)
47+
{
48+
return (Id1 == item.Id1) && (Id2 == item.Id2) && (DataValue == item.DataValue);
49+
}
50+
return base.Equals(obj);
51+
}
52+
public override int GetHashCode()
53+
=> Id1.GetHashCode() ^ Id2.GetHashCode() ^ (DataValue is null ? 0 : DataValue.GetHashCode());
54+
}
55+
3556
#endregion
3657

3758
#region Constants
3859

3960
private const string AccdbFileName = "MsAccessBulkUpdate.accdb";
4061
private const string MdbFileName = "MsAccessBulkUpdate.mdb";
4162
private const string TableName = "BulkUpdateTest";
63+
private const string Composite_TableName = "BulkUpdateTestCompositePK";
4264
private const string PrimaryKeyColumn = "Id";
4365
private const string ShortTextAction = "dolor sit amet";
4466
private const double DoubleMinimum = -999999999999999999999.999999999999;
@@ -83,6 +105,74 @@ private void DataTableBulkUpdateCore(OleDbConnection cn)
83105
MsAccessBulkHelper.CompareTables(actualData, expectedData);
84106
}
85107

108+
[SkippableFact]
109+
public async Task BulkUpdateDataWithCompositePkAccdb()
110+
{
111+
Helpers.SkipTestIfAceProviderNotAvailable();
112+
using (var helper = CreateHelper(ProviderType.Ace, AccdbFileName))
113+
{
114+
await BulkUpdateDataWithCompositePk(helper.Connection);
115+
}
116+
}
117+
118+
[SkippableFact]
119+
public async Task BulkUpdateDataWithCompositePkMdb()
120+
{
121+
Helpers.SkipTestIfJetProviderNotAvailable();
122+
using (var helper = CreateHelper(ProviderType.Jet, MdbFileName))
123+
{
124+
await BulkUpdateDataWithCompositePk(helper.Connection);
125+
}
126+
}
127+
128+
private async Task BulkUpdateDataWithCompositePk(OleDbConnection cn)
129+
{
130+
List<BulkUpdateItemComposite> actualData = null;
131+
132+
using (var bulkUpdate = new MsAccessBulkUpdate(cn))
133+
{
134+
var dataToUpdate = new EnumerableDataReader<BulkUpdateItemComposite>(
135+
new[] {
136+
new BulkUpdateItemComposite() { Id1 = 1, Id2 = 2, DataValue = "lorem ipsum 1" },
137+
new BulkUpdateItemComposite() { Id1 = 2, Id2 = 2, DataValue = "lorem ipsum 2" },
138+
new BulkUpdateItemComposite() { Id1 = 3, Id2 = 2, DataValue = "lorem ipsum 3" }
139+
},
140+
new[] { nameof(BulkUpdateItemComposite.Id1), nameof(BulkUpdateItemComposite.Id2), nameof(BulkUpdateItemComposite.DataValue) });
141+
142+
bulkUpdate.DestinationTableName = Composite_TableName;
143+
bulkUpdate.PrimaryKeyColumn = nameof(BulkUpdateItemComposite.Id1) + ", " + nameof(BulkUpdateItemComposite.Id2);
144+
await bulkUpdate.UpdateAsync(dataToUpdate);
145+
146+
actualData = LoadDataForTableWithCompositePk(cn, Composite_TableName);
147+
}
148+
149+
actualData.Should().Equal(new List<BulkUpdateItemComposite>(new[]
150+
{
151+
new BulkUpdateItemComposite() { Id1 = 1, Id2 = 1, DataValue = "1 - 1" },
152+
new BulkUpdateItemComposite() { Id1 = 1, Id2 = 2, DataValue = "lorem ipsum 1" },
153+
new BulkUpdateItemComposite() { Id1 = 2, Id2 = 1, DataValue = "2 - 1" },
154+
new BulkUpdateItemComposite() { Id1 = 2, Id2 = 2, DataValue = "lorem ipsum 2" },
155+
new BulkUpdateItemComposite() { Id1 = 3, Id2 = 1, DataValue = "3 - 1" },
156+
new BulkUpdateItemComposite() { Id1 = 3, Id2 = 2, DataValue = "lorem ipsum 3" },
157+
}));
158+
}
159+
160+
[SuppressMessage("Security", "CA2100:Review SQL queries for security vulnerabilities")]
161+
private List<BulkUpdateItemComposite> LoadDataForTableWithCompositePk(OleDbConnection cn, string tableName)
162+
{
163+
var data = new List<BulkUpdateItemComposite>();
164+
165+
using (var cmd = new OleDbCommand($"SELECT [Id1], [Id2], [DataValue] FROM [{tableName}] ORDER BY [Id1], [Id2]", cn))
166+
using (var reader = cmd.ExecuteReader())
167+
{
168+
while (reader.Read())
169+
{
170+
data.Add(new BulkUpdateItemComposite() { Id1 = reader.GetInt32(0), Id2 = reader.GetInt32(1), DataValue = reader.GetString(2) });
171+
}
172+
}
173+
return data;
174+
}
175+
86176
[SkippableFact]
87177
public void BulkUpdateDataFromIBulkActionDataReaderIntoAccdb()
88178
{

0 commit comments

Comments
 (0)