Skip to content

Commit 678a05f

Browse files
committed
🐞fix: Mapping return values for enum, datetime
When mapping return values for objects containing enums and datetime the mapping failed. Enum value mapping fails as they are stored by their value, so when matching the results the equality would fail. This has been replaced with enum specific matcher by value. DateTime matching fails for two reasons. First if using SqlServer datetime then the precision between .Net and SqlServer is not the same and will fail the match. Second reason is that the SqlServer might be on a different TZ than the server which when using datetime in DB will result failing matches. This has not been resolved properly the datetime match now always returns true.
1 parent 8712ef2 commit 678a05f

File tree

4 files changed

+311
-14
lines changed

4 files changed

+311
-14
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
using Xunit;
2+
using System.Linq;
3+
using Dapper.Contrib.Extensions;
4+
using Simpleverse.Dapper.SqlServer.Merge;
5+
using Simpleverse.Dapper.SqlServer;
6+
using System;
7+
8+
namespace Simpleverse.Dapper.Test.SqlServer.Merge
9+
{
10+
[Collection("SqlServerCollection")]
11+
public class MergeTests : IClassFixture<DatabaseFixture>
12+
{
13+
readonly DatabaseFixture fixture;
14+
15+
public MergeTests(DatabaseFixture fixture)
16+
{
17+
this.fixture = fixture;
18+
}
19+
20+
[Fact]
21+
public void UpsertAsyncExplicitKeyTest()
22+
{
23+
using (var connection = fixture.GetConnection())
24+
{
25+
// arange
26+
connection.Open();
27+
connection.Truncate<ExplicitKey>();
28+
var record = TestData.ExplicitKeyData(1).FirstOrDefault();
29+
var inserted = connection.Insert(record);
30+
31+
record.Name = (record.Id + 2).ToString();
32+
33+
// act
34+
var updated = connection
35+
.MergeAsync(
36+
record,
37+
matched: options =>
38+
{
39+
options.Update();
40+
options.CheckConditionOnColumns();
41+
},
42+
notMatchedByTarget: options => options.Insert(),
43+
notMatchedBySource: options => options.Delete()
44+
)
45+
.Result;
46+
47+
// assert
48+
var updatedRecords = connection.GetAll<ExplicitKey>();
49+
Assert.Equal(1, updated);
50+
var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
51+
Assert.NotNull(updatedRecord);
52+
Assert.Equal("3", updatedRecord.Name);
53+
Assert.Equal(record.Name, updatedRecord.Name);
54+
}
55+
}
56+
57+
//[Fact]
58+
//public void UpsertAsyncIdentityTest()
59+
//{
60+
// using (var connection = fixture.GetConnection())
61+
// {
62+
// // arange
63+
// connection.Open();
64+
// connection.Truncate<Identity>();
65+
// var record = TestData.IdentityWithIdData(1).FirstOrDefault();
66+
// var inserted = connection.Insert(record);
67+
68+
// record.Name = (record.Id + 2).ToString();
69+
70+
// // act
71+
// var updated = connection.UpsertAsync(record).Result;
72+
73+
// // assert
74+
// var updatedRecords = connection.GetAll<Identity>();
75+
// Assert.Equal(1, updated);
76+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
77+
// Assert.NotNull(updatedRecord);
78+
// Assert.Equal("3", updatedRecord.Name);
79+
// Assert.Equal(record.Name, updatedRecord.Name);
80+
// }
81+
//}
82+
83+
//[Fact]
84+
//public void UpsertAsyncWriteAttributeTest()
85+
//{
86+
// using (var connection = fixture.GetConnection())
87+
// {
88+
// // arange
89+
// connection.Open();
90+
// connection.Truncate<Write>();
91+
// var record = TestData.WriteData(1).FirstOrDefault();
92+
// var inserted = connection.Insert(record);
93+
94+
// record.Ignored = record.Id + 2;
95+
// record.NotIgnored = record.Id + 2;
96+
97+
// // act
98+
// var updated = connection.UpsertAsync(record).Result;
99+
100+
// // assert
101+
// var updatedRecords = connection.GetAll<Write>();
102+
// Assert.Equal(1, updated);
103+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
104+
// Assert.NotNull(updatedRecord);
105+
// Assert.Null(updatedRecord.Ignored);
106+
// Assert.Equal(3, updatedRecord.NotIgnored);
107+
// Assert.Equal(record.NotIgnored, updatedRecord.NotIgnored);
108+
// }
109+
//}
110+
111+
//[Fact]
112+
//public void UpsertAsyncComputedAttributeTest()
113+
//{
114+
// using (var connection = fixture.GetConnection())
115+
// {
116+
// // arange
117+
// connection.Open();
118+
// connection.Truncate<Computed>();
119+
// var record = TestData.ComputedData(1).FirstOrDefault();
120+
// var inserted = connection.Insert(record);
121+
122+
// record.Name = (record.Id + 2).ToString();
123+
// record.Value = record.Value + 2;
124+
// record.ValueDate = record.ValueDate.AddDays(30);
125+
126+
// // act
127+
// var updated = connection.UpsertAsync(record).Result;
128+
129+
// // assert
130+
// var updatedRecords = connection.GetAll<Computed>();
131+
// Assert.Equal(1, updated);
132+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
133+
// Assert.NotNull(updatedRecord);
134+
// Assert.Equal(record.Name, updatedRecord.Name);
135+
// Assert.Equal(5, updatedRecord.Value);
136+
// Assert.Equal(new DateTime(2022, 05, 02), updatedRecord.ValueDate);
137+
// }
138+
//}
139+
140+
//[Fact]
141+
//public void UpsertBulkAsyncExplicitTest()
142+
//{
143+
// using (var connection = fixture.GetConnection())
144+
// {
145+
// // arange
146+
// connection.Open();
147+
// connection.Truncate<ExplicitKey>();
148+
// var records = TestData.ExplicitKeyData(10);
149+
// var inserted = connection.Insert(records);
150+
// records = records.Skip(1);
151+
// foreach(var record in records)
152+
// {
153+
// record.Name = (record.Id + 2).ToString();
154+
// }
155+
156+
// // act
157+
// var updated = connection.UpsertBulkAsync(records).Result;
158+
159+
// // assert
160+
// var updatedRecords = connection.GetAll<ExplicitKey>();
161+
// Assert.Equal(9, updated);
162+
// Assert.Equal("1", updatedRecords.First(x => x.Id == 1).Name);
163+
// for (int i = 0; i < records.Count(); i++)
164+
// {
165+
// var record = records.ElementAt(i);
166+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
167+
// Assert.NotNull(updatedRecord);
168+
// Assert.Equal(record.Name, updatedRecord.Name);
169+
// }
170+
// }
171+
//}
172+
173+
//[Fact]
174+
//public void UpsertBulkAsyncIdentityTest()
175+
//{
176+
// using (var connection = fixture.GetConnection())
177+
// {
178+
// // arange
179+
// connection.Open();
180+
// connection.Truncate<Identity>();
181+
// var records = TestData.IdentityWithIdData(10);
182+
// var inserted = connection.Insert(records);
183+
// records = records.Skip(1);
184+
// foreach (var record in records)
185+
// {
186+
// record.Name = (record.Id + 2).ToString();
187+
// }
188+
189+
// // act
190+
// var updated = connection.UpsertBulkAsync(records).Result;
191+
192+
// // assert
193+
// var updatedRecords = connection.GetAll<Identity>();
194+
// Assert.Equal(9, updated);
195+
// Assert.Equal("1", updatedRecords.First(x => x.Id == 1).Name);
196+
// for (int i = 0; i < records.Count(); i++)
197+
// {
198+
// var record = records.ElementAt(i);
199+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
200+
// Assert.NotNull(updatedRecord);
201+
// Assert.Equal(record.Name, updatedRecord.Name);
202+
// }
203+
// }
204+
//}
205+
206+
//[Fact]
207+
//public void UpsertBulkAsyncWriteAttributeTest()
208+
//{
209+
// using (var connection = fixture.GetConnection())
210+
// {
211+
// // arange
212+
// connection.Open();
213+
// connection.Truncate<Write>();
214+
// var records = TestData.WriteData(10);
215+
// var inserted = connection.Insert(records);
216+
// records = records.Skip(1);
217+
// foreach (var record in records)
218+
// {
219+
// record.Ignored = (record.Id + 2);
220+
// record.NotIgnored = (record.Id + 3);
221+
// }
222+
223+
// // act
224+
// var updated = connection.UpsertBulkAsync(records).Result;
225+
226+
// // assert
227+
// var updatedRecords = connection.GetAll<Write>();
228+
// Assert.Equal(9, updated);
229+
// Assert.Null(updatedRecords.First(x => x.Id == 1).Ignored);
230+
// Assert.Equal(100, updatedRecords.First(x => x.Id == 1).NotIgnored);
231+
// for (int i = 0; i < records.Count(); i++)
232+
// {
233+
// var record = records.ElementAt(i);
234+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
235+
// Assert.NotNull(updatedRecord);
236+
// Assert.Null(updatedRecord.Ignored);
237+
// Assert.Equal(record.NotIgnored, updatedRecord.NotIgnored);
238+
// }
239+
// }
240+
//}
241+
242+
//[Fact]
243+
//public void UpsertBulkAsyncComputedAttributeTest()
244+
//{
245+
// using (var connection = fixture.GetConnection())
246+
// {
247+
// // arange
248+
// connection.Open();
249+
// connection.Truncate<Computed>();
250+
// var records = TestData.ComputedData(10);
251+
// var inserted = connection.Insert(records);
252+
// records = records.Skip(1);
253+
// foreach (var record in records)
254+
// {
255+
// record.Name = (record.Id + 2).ToString();
256+
// record.Value = record.Value + 2;
257+
// record.ValueDate = record.ValueDate.AddDays(30);
258+
// }
259+
260+
// // act
261+
// var updated = connection.UpsertAsync(records).Result;
262+
263+
// // assert
264+
// var updatedRecords = connection.GetAll<Computed>();
265+
// Assert.Equal(9, updated);
266+
// Assert.Equal("1", updatedRecords.First(x => x.Id == 1).Name);
267+
// for (int i = 0; i < records.Count(); i++)
268+
// {
269+
// var record = records.ElementAt(i);
270+
// var updatedRecord = updatedRecords.FirstOrDefault(x => x.Id == record.Id);
271+
// Assert.NotNull(updatedRecord);
272+
// Assert.Equal(record.Name, updatedRecord.Name);
273+
// Assert.Equal(5, updatedRecord.Value);
274+
// Assert.Equal(new DateTime(2022, 05, 02), updatedRecord.ValueDate);
275+
// }
276+
// }
277+
//}
278+
}
279+
}

Simpleverse.Dapper.Test/SqlServer/Merge/UpsertTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ public void UpsertBulkAsyncWriteAttributeTest()
228228
}
229229
}
230230

231+
[Fact(Skip = "Doesn't work without key properties")]
231232
public void UpsertBulkAsyncComputedAttributeTest()
232233
{
233234
using (var connection = fixture.GetConnection())

Simpleverse.Dapper/Simpleverse.Dapper.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
1414
<PackageTags>Dapper, Bulk, Merge, Upsert, Delete, Insert, Update</PackageTags>
1515
<PackageLicenseFile>LICENSE</PackageLicenseFile>
16-
<Version>1.0.27</Version>
16+
<Version>1.0.28</Version>
1717
<Description>High performance operation for MS SQL Server built for Dapper ORM. Including bulk operations Insert, Update, Delete, Get as well as Upsert both single and bulk.</Description>
18-
<AssemblyVersion>1.0.27.0</AssemblyVersion>
19-
<FileVersion>1.0.27.0</FileVersion>
18+
<AssemblyVersion>1.0.28.0</AssemblyVersion>
19+
<FileVersion>1.0.28.0</FileVersion>
2020
</PropertyGroup>
2121

2222
<ItemGroup>

Simpleverse.Dapper/SqlServer/BulkExtensions.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88
using Dapper;
99
using Dapper.Contrib.Extensions;
1010
using System.Dynamic;
11-
using System.Xml;
12-
using System.IO.MemoryMappedFiles;
13-
using System.Security.Cryptography;
14-
using Microsoft.Win32.SafeHandles;
1511

1612
namespace Simpleverse.Dapper.SqlServer
1713
{
@@ -280,7 +276,8 @@ await connection.ExecuteAsync(
280276
MapGeneratedValues(
281277
entitiesToInsert,
282278
outputValues,
283-
typeMeta.PropertiesExceptKeyAndComputed, propertiesKeyAndComputed,
279+
typeMeta.PropertiesExceptKeyAndComputed,
280+
propertiesKeyAndComputed,
284281
true
285282
);
286283

@@ -465,14 +462,10 @@ private static void MapGeneratedValues<T>(
465462
var found = true;
466463
foreach (var property in matchProperties)
467464
{
468-
var entityValue = property.GetValue(entity.Entity);
465+
var entityValue = (object) property.GetValue(entity.Entity);
469466
var resultValue = result[property.Name];
470467

471-
if (!(
472-
(entityValue == null && resultValue == null) ||
473-
(entityValue != null && entityValue.Equals(resultValue)) ||
474-
(resultValue != null && resultValue.Equals(entityValue))
475-
))
468+
if (!IsEqual(entityValue, resultValue))
476469
{
477470
found = false;
478471
break;
@@ -494,6 +487,30 @@ private static void MapGeneratedValues<T>(
494487
}
495488
}
496489

490+
private static bool IsEqual(object entityValue, object resultValue)
491+
{
492+
if (entityValue == null && resultValue == null)
493+
return true;
494+
495+
if (entityValue != null && entityValue.Equals(resultValue))
496+
return true;
497+
498+
if (resultValue != null && resultValue.Equals(entityValue))
499+
return true;
500+
501+
if (entityValue != null && resultValue != null)
502+
{
503+
var type = entityValue.GetType();
504+
if (type.IsEnum && Enum.IsDefined(type, resultValue))
505+
return true;
506+
507+
if (entityValue is DateTime && resultValue is DateTime)
508+
return true;
509+
}
510+
511+
return false;
512+
}
513+
497514
public static async Task<(string source, DynamicParameters parameters)> BulkSourceAsync<T>(
498515
this SqlConnection connection,
499516
IEnumerable<T> entities,

0 commit comments

Comments
 (0)