Skip to content

Commit ed2465b

Browse files
committed
Fix automatic conversion to mostly work as documented in current repodb issues, but also pass integration tests.
1 parent 39204e3 commit ed2465b

File tree

13 files changed

+552
-134
lines changed

13 files changed

+552
-134
lines changed

RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/BaseEnferredInheritanceTest.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
2-
using Microsoft.Data.SqlClient;
32
using System.Linq;
3+
using Microsoft.Data.SqlClient;
44
using Microsoft.VisualStudio.TestTools.UnitTesting;
55
using RepoDb.IntegrationTests.Models;
66
using RepoDb.IntegrationTests.Setup;
@@ -100,6 +100,7 @@ public void TestSqlConnectionInsertForInherited()
100100
public void TestSqlConnectionInsertAllForInherited()
101101
{
102102
// Setup
103+
// warning CA2021: This call will always result in an empty sequence because type 'RepoDb.IntegrationTests.Models.InheritedIdentityTable' is incompatible with type 'RepoDb.IntegrationTests.Models.Entity<RepoDb.IntegrationTests.Models.InheritedIdentityTable>' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021)
103104
var entities = Helper.CreateInheritedIdentityTables(10)
104105
.OfType<Entity<InheritedIdentityTable>>().ToList();
105106

@@ -192,6 +193,7 @@ public void TestSqlConnectionMergeForInheritedWithNonEmptyTable()
192193
public void TestSqlConnectionMergeAllForInherited()
193194
{
194195
// Setup
196+
// warning CA2021: This call will always result in an empty sequence because type 'RepoDb.IntegrationTests.Models.InheritedIdentityTable' is incompatible with type 'RepoDb.IntegrationTests.Models.Entity<RepoDb.IntegrationTests.Models.InheritedIdentityTable>' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021)
195197
var entities = Helper.CreateInheritedIdentityTables(10)
196198
.OfType<Entity<InheritedIdentityTable>>().ToList();
197199

@@ -217,6 +219,7 @@ public void TestSqlConnectionMergeAllForInherited()
217219
public void TestSqlConnectionMergeAllForInheritedWithNonEmptyTables()
218220
{
219221
// Setup
222+
// warning CA2021: This call will always result in an empty sequence because type 'RepoDb.IntegrationTests.Models.InheritedIdentityTable' is incompatible with type 'RepoDb.IntegrationTests.Models.Entity<RepoDb.IntegrationTests.Models.InheritedIdentityTable>' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021)
220223
var entities = Helper.CreateInheritedIdentityTables(10)
221224
.OfType<Entity<InheritedIdentityTable>>().ToList();
222225

@@ -344,6 +347,7 @@ public void TestSqlConnectionUpdateForInheritedViaPrimaryKey()
344347
public void TestSqlConnectionUpdateAllForInherited()
345348
{
346349
// Setup
350+
// warning CA2021: This call will always result in an empty sequence because type 'RepoDb.IntegrationTests.Models.InheritedIdentityTable' is incompatible with type 'RepoDb.IntegrationTests.Models.Entity<RepoDb.IntegrationTests.Models.InheritedIdentityTable>' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021)
347351
var entities = Helper.CreateInheritedIdentityTables(10)
348352
.OfType<Entity<InheritedIdentityTable>>().ToList();
349353

RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/Setup/Database.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,19 @@ public static class Database
1313
/// </summary>
1414
public static void Initialize()
1515
{
16-
// Get the connection string
17-
var connectionStringForMaster = Environment.GetEnvironmentVariable("REPODB_CONSTR_MASTER", EnvironmentVariableTarget.Process);
18-
var connectionString = Environment.GetEnvironmentVariable("REPODB_CONSTR", EnvironmentVariableTarget.Process);
19-
2016
// Master connection
21-
ConnectionStringForMaster = (connectionStringForMaster ?? @"Server=(local);Database=master;Integrated Security=SSPI;TrustServerCertificate=True;");
17+
ConnectionStringForMaster =
18+
Environment.GetEnvironmentVariable("REPODB_SQLSERVER_CONSTR_MASTER")
19+
?? Environment.GetEnvironmentVariable("REPODB_CONSTR_MASTER")
20+
// ?? "Server=tcp:127.0.0.1,41433;Database=master;User ID=sa;Password=ddd53e85-b15e-4da8-91e5-a7d3b00a0ab2;TrustServerCertificate=True;" // Docker test configuration
21+
?? "Server=(local);Database=master;Integrated Security=SSPI;TrustServerCertificate=True;";
2222

2323
// RepoDb connection
24-
ConnectionStringForRepoDb = (connectionString ?? @"Server=(local);Database=RepoDb;Integrated Security=SSPI;TrustServerCertificate=True;");
24+
ConnectionStringForRepoDb =
25+
Environment.GetEnvironmentVariable("REPODB_SQLSERVER_CONSTR_REPODB")
26+
?? Environment.GetEnvironmentVariable("REPODB_CONSTR")
27+
// ?? "Server=tcp:127.0.0.1,41433;Database=RepoDb;User ID=sa;Password=ddd53e85-b15e-4da8-91e5-a7d3b00a0ab2;TrustServerCertificate=True;" // Docker test configuration
28+
?? "Server=(local);Database=RepoDb;Integrated Security=SSPI;TrustServerCertificate=True;";
2529

2630
// Set the proper values for type mapper
2731
TypeMapper.Add(typeof(DateTime), System.Data.DbType.DateTime2, true);

RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/TypeConversionsTest.cs

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.ComponentModel;
23
using System.Linq;
34
using Microsoft.Data.SqlClient;
45
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -428,6 +429,298 @@ public void TestSqlConnectionExecuteQueryConversionFromBitToString()
428429

429430
#endregion
430431

432+
433+
enum Direction
434+
{
435+
None,
436+
North,
437+
East,
438+
South,
439+
West
440+
}
441+
enum Direction2
442+
{
443+
None,
444+
North,
445+
East,
446+
South,
447+
West
448+
}
449+
450+
enum Direction3
451+
{
452+
None,
453+
North,
454+
East,
455+
South,
456+
West
457+
}
458+
459+
enum Direction4
460+
{
461+
None,
462+
North,
463+
East,
464+
South,
465+
West
466+
}
467+
468+
[TestMethod]
469+
public void TestSqlConnectionExecuteQueryConversionFromIntToEnum()
470+
{
471+
using var _ = new CultureScope("EN-US");
472+
473+
// With automatic conversion
474+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
475+
{
476+
// Act Query
477+
var data = connection.ExecuteQuery<Direction>("SELECT CONVERT(INT, 1) AS Value;").First();
478+
var data0 = connection.ExecuteQuery<Direction>("SELECT CONVERT(INT, 0) AS Value;").First();
479+
var data10 = connection.ExecuteQuery<Direction>("SELECT CONVERT(INT, 10) AS Value;").First();
480+
481+
// Assert
482+
Assert.AreEqual(Direction.North, data);
483+
Assert.AreEqual(Direction.None, data0);
484+
Assert.AreEqual((Direction)10, data10);
485+
}
486+
487+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.UseDefault });
488+
489+
// With default conversion
490+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
491+
{
492+
// Act Query
493+
var data = connection.ExecuteQuery<Direction2>("SELECT CONVERT(INT, 1) AS Value;").First();
494+
var data0 = connection.ExecuteQuery<Direction2>("SELECT CONVERT(INT, 0) AS Value;").First();
495+
var data10 = connection.ExecuteQuery<Direction2>("SELECT CONVERT(INT, 10) AS Value;").First();
496+
497+
// Assert
498+
Assert.AreEqual(Direction2.North, data);
499+
Assert.AreEqual(Direction2.None, data0);
500+
Assert.AreEqual(Direction2.None, data10);
501+
}
502+
503+
504+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.ThrowError });
505+
506+
// With default conversion
507+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
508+
{
509+
// Act Query
510+
var data = connection.ExecuteQuery<Direction3>("SELECT CONVERT(INT, 1) AS Value;").First();
511+
var data0 = connection.ExecuteQuery<Direction3>("SELECT CONVERT(INT, 0) AS Value;").First();
512+
513+
// Assert
514+
Assert.AreEqual(Direction3.North, data);
515+
Assert.AreEqual(Direction3.None, data0);
516+
517+
try
518+
{
519+
var data10 = connection.ExecuteQuery<Direction3>("SELECT CONVERT(INT, 10) AS Value;").First();
520+
Assert.Fail("Should have failed");
521+
}
522+
catch (InvalidEnumArgumentException e)
523+
{
524+
Assert.IsTrue(e.Message.Contains(nameof(Direction3)));
525+
526+
// We are in an EN-US scope so the message should match
527+
Assert.AreEqual("The value of argument 'value' (10) is invalid for Enum type 'Direction3'. (Parameter 'value')", e.Message);
528+
}
529+
}
530+
}
531+
532+
[TestMethod]
533+
public void TestSqlConnectionExecuteQueryConversionFromStringToEnum()
534+
{
535+
using var _ = new CultureScope("EN-US");
536+
537+
// With automatic conversion
538+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
539+
{
540+
// Act Query
541+
var data = connection.ExecuteQuery<Direction>("SELECT 'North' AS Value;").First();
542+
var data0 = connection.ExecuteQuery<Direction>("SELECT 'None' AS Value;").First();
543+
var data3 = connection.ExecuteQuery<Direction>("SELECT '3' AS Value;").First();
544+
var data9 = connection.ExecuteQuery<Direction>("SELECT '9' AS Value;").First();
545+
try
546+
{
547+
var data10 = connection.ExecuteQuery<Direction>("SELECT 'Center' AS Value;").First();
548+
Assert.Fail("Should have failed Direction/3");
549+
}
550+
catch (ArgumentOutOfRangeException e)
551+
{
552+
Assert.IsTrue(e.Message.Contains(nameof(Direction)));
553+
554+
// We are in an EN-US scope so the message should match
555+
Assert.AreEqual("Invalid value for Direction (Parameter 'value')\nActual value was Center.", e.Message.Replace("\r", ""));
556+
}
557+
558+
// Assert
559+
Assert.AreEqual(Direction.North, data);
560+
Assert.AreEqual(Direction.South, data3);
561+
Assert.AreEqual(Direction.None, data0);
562+
Assert.AreEqual((Direction)9, data9);
563+
}
564+
565+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.UseDefault });
566+
567+
// With default conversion
568+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
569+
{
570+
// Act Query
571+
var data = connection.ExecuteQuery<Direction2>("SELECT 'North' AS Value;").First();
572+
var data0 = connection.ExecuteQuery<Direction2>("SELECT 'None' AS Value;").First();
573+
var data3 = connection.ExecuteQuery<Direction2>("SELECT '3' AS Value;").First();
574+
var data9 = connection.ExecuteQuery<Direction2>("SELECT '9' AS Value;").First();
575+
var data10 = connection.ExecuteQuery<Direction2>("SELECT 'Center' AS Value;").First();
576+
577+
// Assert
578+
Assert.AreEqual(Direction2.North, data);
579+
Assert.AreEqual(Direction2.None, data0);
580+
Assert.AreEqual(Direction2.South, data3);
581+
Assert.AreEqual(Direction2.None, data9);
582+
Assert.AreEqual(Direction2.None, data10);
583+
}
584+
585+
586+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.ThrowError });
587+
588+
// With default conversion
589+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
590+
{
591+
// Act Query
592+
var data = connection.ExecuteQuery<Direction3>("SELECT 'North' AS Value;").First();
593+
var data0 = connection.ExecuteQuery<Direction3>("SELECT 'None' AS Value;").First();
594+
595+
try
596+
{
597+
var data10 = connection.ExecuteQuery<Direction3>("SELECT 'Center' AS Value;").First();
598+
Assert.Fail("Should have failed Direction3/Center");
599+
}
600+
catch (ArgumentOutOfRangeException e)
601+
{
602+
Assert.IsTrue(e.Message.Contains(nameof(Direction3)));
603+
604+
// We are in an EN-US scope so the message should match
605+
Assert.AreEqual("Invalid value for Direction3 (Parameter 'value')\nActual value was Center.", e.Message.Replace("\r", ""));
606+
}
607+
608+
try
609+
{
610+
var data9 = connection.ExecuteQuery<Direction3>("SELECT '9' AS Value;").First();
611+
Assert.Fail("Should have failed Direction3/9");
612+
}
613+
catch (ArgumentOutOfRangeException e)
614+
{
615+
Assert.IsTrue(e.Message.Contains(nameof(Direction3)));
616+
617+
// We are in an EN-US scope so the message should match
618+
Assert.AreEqual("Invalid value for Direction3 (Parameter 'value')\nActual value was 9.", e.Message.Replace("\r", ""));
619+
}
620+
621+
// Assert
622+
Assert.AreEqual(Direction3.North, data);
623+
Assert.AreEqual(Direction3.None, data0);
624+
}
625+
626+
627+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.ThrowError });
628+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
629+
{
630+
// Act Query
631+
var data = connection.ExecuteQuery<Direction4>("SELECT 'North' AS Value;").First();
632+
var data0 = connection.ExecuteQuery<Direction4>("SELECT 'None' AS Value;").First();
633+
var data3 = connection.ExecuteQuery<Direction4>("SELECT '3' AS Value;").First();
634+
try
635+
{
636+
var data10 = connection.ExecuteQuery<Direction4>("SELECT 'Center' AS Value;").First();
637+
Assert.Fail("Should have failed Direction/3");
638+
}
639+
catch (ArgumentOutOfRangeException e)
640+
{
641+
Assert.IsTrue(e.Message.Contains(nameof(Direction4)));
642+
643+
// We are in an EN-US scope so the message should match
644+
Assert.AreEqual("Invalid value for Direction4 (Parameter 'value')\nActual value was Center.", e.Message.Replace("\r", ""));
645+
}
646+
647+
try
648+
{
649+
var data9 = connection.ExecuteQuery<Direction4>("SELECT '9' AS Value;").First();
650+
Assert.Fail("Should have failed Direction/9");
651+
}
652+
catch (ArgumentOutOfRangeException e)
653+
{
654+
Assert.IsTrue(e.Message.Contains(nameof(Direction4)));
655+
656+
// We are in an EN-US scope so the message should match
657+
Assert.AreEqual("Invalid value for Direction4 (Parameter 'value')\nActual value was 9.", e.Message.Replace("\r", ""));
658+
}
659+
660+
// Assert
661+
Assert.AreEqual(Direction4.North, data);
662+
Assert.AreEqual(Direction4.South, data3);
663+
Assert.AreEqual(Direction4.None, data0);
664+
}
665+
}
666+
667+
[TestMethod]
668+
public void TestSqlConnectionExecuteQueryConversionFromStringToEnumFlags()
669+
{
670+
using var _ = new CultureScope("EN-US");
671+
672+
// With automatic conversion
673+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
674+
{
675+
// Act Query
676+
var data = connection.ExecuteQuery<Direction>("SELECT 'North, East' AS Value;").First();
677+
var data2 = connection.ExecuteQuery<Direction>("SELECT 'North, West' AS Value;").First();
678+
679+
// Assert
680+
Assert.AreEqual(Direction.South, data); // Flag behavior
681+
Assert.AreEqual(Direction.North | Direction.West, data2); // Flag behavior
682+
}
683+
684+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.UseDefault });
685+
686+
// With default conversion
687+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
688+
{
689+
// Act Query
690+
var data = connection.ExecuteQuery<Direction2>("SELECT 'North, East' AS Value;").First();
691+
var data2 = connection.ExecuteQuery<Direction2>("SELECT 'North, West' AS Value;").First();
692+
693+
// Assert
694+
Assert.AreEqual(Direction2.South, data); // Flag behavior
695+
Assert.AreEqual(Direction2.None, data2); // Not defined
696+
}
697+
698+
699+
GlobalConfiguration.Setup(new() { ConversionType = ConversionType.Default, EnumHandling = EnumHandling.ThrowError });
700+
701+
// With default conversion
702+
using (var connection = new SqlConnection(Database.ConnectionStringForRepoDb).EnsureOpen())
703+
{
704+
// Act Query
705+
var data = connection.ExecuteQuery<Direction3>("SELECT 'North, East' AS Value;").First();
706+
707+
try
708+
{
709+
var data2 = connection.ExecuteQuery<Direction3>("SELECT 'North, West' AS Value;").First();
710+
Assert.Fail("Should have failed Direction3/North, West");
711+
}
712+
catch (ArgumentOutOfRangeException e)
713+
{
714+
Assert.IsTrue(e.Message.Contains(nameof(Direction3)));
715+
716+
// We are in an EN-US scope so the message should match
717+
Assert.AreEqual("Invalid value for Direction3 (Parameter 'value')\nActual value was North, West.", e.Message.Replace("\r", ""));
718+
}
719+
720+
Assert.AreEqual(Direction3.South, data);
721+
}
722+
}
723+
431724
#endregion
432725

433726
#region Query

RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Mappers/MappingSequenceTest.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
using Microsoft.VisualStudio.TestTools.UnitTesting;
2-
using RepoDb.Interfaces;
3-
using RepoDb.Attributes;
4-
using RepoDb.UnitTests.CustomObjects;
5-
using System;
6-
using System.Linq;
7-
using System.Collections.Generic;
1+
using System;
82
using System.Data;
9-
using System.Text;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
using RepoDb.Attributes;
5+
using RepoDb.Interfaces;
106
using RepoDb.Options;
7+
using RepoDb.UnitTests.CustomObjects;
118

129
namespace RepoDb.UnitTests.Mappers
1310
{

RepoDb.Core/RepoDb/Compat.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace System.Runtime.CompilerServices
2+
{
3+
#if !NET
4+
// Required to allow init properties in netstandard
5+
internal sealed class IsExternalInit : Attribute
6+
{
7+
}
8+
#endif
9+
}

0 commit comments

Comments
 (0)