From 726253670a76e17ec7fecaf7df7aa6af41a667f2 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 11:55:04 +0100 Subject: [PATCH 01/18] Execute integration tests --- .appveyor.yml | 3 + Dommel.sln | 12 +++ build.ps1 | 3 + .../Dommel.IntegrationTests.csproj | 1 + .../Dommel.IntegrationTests/DommelTestBase.cs | 69 +++++++++++++ test/Dommel.IntegrationTests/InsertTests.cs | 96 +++++++++++++++++++ test/Dommel.IntegrationTests/PagingTests.cs | 14 +-- 7 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 test/Dommel.IntegrationTests/DommelTestBase.cs create mode 100644 test/Dommel.IntegrationTests/InsertTests.cs diff --git a/.appveyor.yml b/.appveyor.yml index 09a48161..d9d40197 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -13,6 +13,9 @@ environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true CI: true + AppVeyor: true +services: + - mssql2016 artifacts: - path: .\src\Dommel\artifacts\**\*.nupkg name: NuGet diff --git a/Dommel.sln b/Dommel.sln index c43e0003..056217af 100644 --- a/Dommel.sln +++ b/Dommel.sln @@ -12,6 +12,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dommel.Tests", "test\Dommel EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dommel.IntegrationTests", "test\Dommel.IntegrationTests\Dommel.IntegrationTests.csproj", "{305C1E12-1F6D-4510-BD42-0C3F1BD838A1}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B850D48C-10D3-4EF7-AE61-873068211FD2}" + ProjectSection(SolutionItems) = preProject + .appveyor.yml = .appveyor.yml + .gitignore = .gitignore + .travis.yml = .travis.yml + build.cmd = build.cmd + build.ps1 = build.ps1 + build.sh = build.sh + NuGet.Config = NuGet.Config + README.md = README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/build.ps1 b/build.ps1 index 4b0abdc1..73c6b885 100644 --- a/build.ps1 +++ b/build.ps1 @@ -29,6 +29,9 @@ echo "build: Executing tests" Push-Location -Path .\test\Dommel.Tests exec { & dotnet test -c Release --no-build } Pop-Location +Push-Location -Path .\test\Dommel.IntegrationTests +exec { & dotnet test -c Release --no-build } +Pop-Location if ($env:APPVEYOR_BUILD_NUMBER) { $versionSuffix = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10) diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index 0ef653b6..e12149e7 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -2,6 +2,7 @@ netcoreapp2.1 false + latest diff --git a/test/Dommel.IntegrationTests/DommelTestBase.cs b/test/Dommel.IntegrationTests/DommelTestBase.cs new file mode 100644 index 00000000..c842a813 --- /dev/null +++ b/test/Dommel.IntegrationTests/DommelTestBase.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Threading.Tasks; +using Dapper; +using Xunit; + +[assembly:CollectionBehavior(DisableTestParallelization = true)] + +namespace Dommel.IntegrationTests +{ + [Collection("IntegrationTests")] + public abstract class DommelTestBase : IAsyncLifetime + { + protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; + + protected virtual string GetConnectionString(string databaseName = "DommelTests") => + IsAppVeyor + ? $"Server=(local)\\SQL2016;Database={databaseName};User ID=sa;Password=Password12!" + : $"Server=(LocalDb)\\mssqllocaldb;Database={databaseName};Integrated Security=True"; + + public virtual async Task InitializeAsync() + { + using (var con = new SqlConnection(GetConnectionString("tempdb"))) + { + await con.ExecuteAsync("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'DommelTests') BEGIN CREATE DATABASE DommelTests; END;"); + } + + using (var connection = new SqlConnection(GetConnectionString())) + { + await connection.OpenAsync(); + var sql = "IF OBJECT_ID(N'dbo.Products', N'U') IS NULL BEGIN CREATE TABLE dbo.Products (Id int IDENTITY(1,1) PRIMARY KEY, Name varchar(255) not null); SELECT 1; END;"; + var created = await connection.ExecuteScalarAsync(sql); + if (created != null) + { + // Table was just created, insert dummy data + var products = new List + { + new Product { Name = "Chai" }, + new Product { Name = "Chang" }, + new Product { Name = "Aniseed Syrup" }, + new Product { Name = "Chef Anton's Cajun Seasoning" }, + new Product { Name = "Chef Anton's Gumbo Mix" }, + + new Product { Name = "Chai 2" }, + new Product { Name = "Chang 2" }, + new Product { Name = "Aniseed Syrup 2" }, + new Product { Name = "Chef Anton's Cajun Seasoning 2" }, + new Product { Name = "Chef Anton's Gumbo Mix 2" }, + + new Product { Name = "Chai 3" }, + new Product { Name = "Chang 3" }, + new Product { Name = "Aniseed Syrup 3" }, + }; + + await connection.InsertAsyncAll(products); + } + } + } + + public virtual async Task DisposeAsync() + { + using (var con = new SqlConnection(GetConnectionString())) + { + await con.ExecuteAsync("IF OBJECT_ID(N'dbo.Products', N'U') IS NOT NULL BEGIN DROP TABLE dbo.Products; END;"); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/InsertTests.cs b/test/Dommel.IntegrationTests/InsertTests.cs new file mode 100644 index 00000000..7eb4071c --- /dev/null +++ b/test/Dommel.IntegrationTests/InsertTests.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class InsertTests : DommelTestBase + { + [Fact] + public void Insert() + { + using (var con = new SqlConnection(GetConnectionString())) + { + var id = Convert.ToInt32(con.Insert(new Product { Name = "blah" })); + var product = con.Get(id); + Assert.NotNull(product); + Assert.Equal("blah", product.Name); + Assert.Equal(id, product.Id); + } + } + + [Fact] + public async Task InsertAsync() + { + using (var con = new SqlConnection(GetConnectionString())) + { + var id = Convert.ToInt32(await con.InsertAsync(new Product { Name = "blah" })); + var product = await con.GetAsync(id); + Assert.NotNull(product); + Assert.Equal("blah", product.Name); + Assert.Equal(id, product.Id); + } + } + + [Fact] + public void InsertAll() + { + using (var con = new SqlConnection(GetConnectionString())) + { + var ps = new List + { + new Product { Name = "blah"}, + new Product { Name = "blah"}, + new Product { Name = "blah"}, + }; + + con.InsertAll(ps); + + var blahs = con.Select(p => p.Name == "blah"); + Assert.Equal(3, blahs.Count()); + } + } + + [Fact] + public async Task InsertAllAsync() + { + using (var con = new SqlConnection(GetConnectionString())) + { + var ps = new List + { + new Product { Name = "blah"}, + new Product { Name = "blah"}, + new Product { Name = "blah"}, + }; + + await con.InsertAsyncAll(ps); + + var blahs = await con.SelectAsync(p => p.Name == "blah"); + Assert.Equal(3, blahs.Count()); + } + } + + [Fact] + public void InsertAllEmtyList() + { + using (var con = new SqlConnection(GetConnectionString())) + { + var ps = new List(); + con.InsertAll(ps); + } + } + + [Fact] + public async Task InsertAllAsyncEmtyList() + { + using (var con = new SqlConnection(GetConnectionString())) + { + var ps = new List(); + await con.InsertAsyncAll(ps); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/PagingTests.cs b/test/Dommel.IntegrationTests/PagingTests.cs index 32685dff..c1f22676 100644 --- a/test/Dommel.IntegrationTests/PagingTests.cs +++ b/test/Dommel.IntegrationTests/PagingTests.cs @@ -1,21 +1,13 @@ -using System.Data.SqlClient; -using System.IO; +using System.Collections.Generic; +using System.Data.SqlClient; using System.Linq; using System.Threading.Tasks; using Xunit; namespace Dommel.IntegrationTests { - public class PagingTests + public class PagingTests : DommelTestBase { - private static string GetConnectionString() - { - var currentDir = new DirectoryInfo(Directory.GetCurrentDirectory()); - var fileName = Path.Combine(currentDir.Parent.Parent.Parent.FullName, "App_Data", "Dommel.mdf"); - var conStr = "Data Source=(localdb)\\MSSQLLocalDB;Integrated Security=True;AttachDBFilename=" + fileName; - return conStr; - } - [Fact] public void Fetches_FirstPage() { From 1c6a32d1b773a5a9b111b6e614b61d494a54d683 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 15:37:20 +0100 Subject: [PATCH 02/18] Execute tests against SQL Server and MySQL --- .appveyor.yml | 1 + src/Dommel/DommelMapper.cs | 17 +++ .../AutoMultiMapTests.cs | 51 ++++++++ test/Dommel.IntegrationTests/Database.cs | 114 ++++++++++++++++++ .../Dommel.IntegrationTests.csproj | 1 + .../Dommel.IntegrationTests/DommelTestBase.cs | 74 +++++++----- test/Dommel.IntegrationTests/InsertTests.cs | 33 +++-- test/Dommel.IntegrationTests/Models.cs | 45 +++++++ test/Dommel.IntegrationTests/PagingTests.cs | 39 +++--- 9 files changed, 320 insertions(+), 55 deletions(-) create mode 100644 test/Dommel.IntegrationTests/AutoMultiMapTests.cs create mode 100644 test/Dommel.IntegrationTests/Database.cs create mode 100644 test/Dommel.IntegrationTests/Models.cs diff --git a/.appveyor.yml b/.appveyor.yml index d9d40197..b6a9df44 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -16,6 +16,7 @@ environment: AppVeyor: true services: - mssql2016 + - mysql artifacts: - path: .\src\Dommel\artifacts\**\*.nupkg name: NuGet diff --git a/src/Dommel/DommelMapper.cs b/src/Dommel/DommelMapper.cs index f4f67317..78eb2848 100644 --- a/src/Dommel/DommelMapper.cs +++ b/src/Dommel/DommelMapper.cs @@ -25,5 +25,22 @@ public static partial class DommelMapper private static void LogQuery(string query, [CallerMemberName]string method = null) => LogReceived?.Invoke(method != null ? $"{method}<{typeof(T).Name}>: {query}" : query); + + /// + /// Clears the query caches. + /// + public static void ClearQueryCache() + { + // Keep this in sync with all query caches + _columnNameCache.Clear(); + _countQueryCache.Clear(); + _deleteAllQueryCache.Clear(); + _deleteQueryCache.Clear(); + _getAllQueryCache.Clear(); + _getByIdsQueryCache.Clear(); + _getQueryCache.Clear(); + _insertQueryCache.Clear(); + _updateQueryCache.Clear(); + } } } diff --git a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs new file mode 100644 index 00000000..b844ab7d --- /dev/null +++ b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class AutoMultiMapTestsSqlServer : AutoMultiMapTests, IClassFixture + { + public AutoMultiMapTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class AutoMultiMapTestsMySql : AutoMultiMapTests, IClassFixture + { + public AutoMultiMapTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class AutoMultiMapTests : DommelTestBase + { + public AutoMultiMapTests(Database database) : base(database) + { + } + + [Fact] + public async Task OneToOne() + { + using (var con = GetConnection()) + { + var product = await con.GetAsync(1); + Assert.NotNull(product); + Assert.NotNull(product.Category); + Assert.Equal("Food", product.Category.Name); + Assert.Equal(product.CategoryId, product.Category.Id); + } + } + + [Fact] + public async Task OneToMany() + { + using (var con = GetConnection()) + { + var order = await con.GetAsync(1); + Assert.NotNull(order); + Assert.NotNull(order.OrderLines); + Assert.Equal(3, order.OrderLines.Count); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/Database.cs b/test/Dommel.IntegrationTests/Database.cs new file mode 100644 index 00000000..e026d78b --- /dev/null +++ b/test/Dommel.IntegrationTests/Database.cs @@ -0,0 +1,114 @@ +using System; +using System.Data.Common; +using System.Data.SqlClient; +using System.Threading.Tasks; +using Dapper; +using MySql.Data.MySqlClient; + +namespace Dommel.IntegrationTests +{ + public abstract class Database + { + protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; + + public virtual string TempDbDatabaseName => "tempdb"; + + public virtual string DefaultDatabaseName => "DommelTests"; + + public abstract DbConnection GetConnection(string databaseName); + + public abstract Task CreateDatabase(); + + public abstract Task CreateTables(); + + public virtual async Task DropTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + await con.ExecuteAsync(@" +DROP TABLE Categories; +DROP TABLE Products; +DROP TABLE Orders; +DROP TABLE OrderLines;"); + } + } + } + + public class SqlServerDatabase : Database + { + public override DbConnection GetConnection(string databaseName) + { + var connectionString = IsAppVeyor + ? $"Server=(local)\\SQL2016;Database={databaseName};User ID=sa;Password=Password12!" + : $"Server=(LocalDb)\\mssqllocaldb;Database={databaseName};Integrated Security=True"; + + return new SqlConnection(connectionString); + } + + public override async Task CreateDatabase() + { + using (var con = GetConnection(TempDbDatabaseName)) + { + await con.ExecuteAsync("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'DommelTests') BEGIN CREATE DATABASE DommelTests; END;"); + } + } + + public override async Task CreateTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + var sql = @"IF OBJECT_ID(N'dbo.Products', N'U') IS NULL +BEGIN + CREATE TABLE dbo.Categories (Id int IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255)); + CREATE TABLE dbo.Products (Id int IDENTITY(1,1) PRIMARY KEY, CategoryId int, Name VARCHAR(255)); + CREATE TABLE dbo.Orders (Id int IDENTITY(1,1) PRIMARY KEY, Created DATETIME NOT NULL); + CREATE TABLE dbo.OrderLines (Id int IDENTITY(1,1) PRIMARY KEY, OrderId int, Line VARCHAR(255)); + SELECT 1; +END"; + var created = await con.ExecuteScalarAsync(sql); + + // A result means the tables were just created + return created != null; + } + } + } + + public class MySqlDatabase : Database + { + public override DbConnection GetConnection(string databaseName) + { + var connectionString = IsAppVeyor + ? $"Server=localhost;Database={databaseName};Uid=root;Pwd=Password12!;" + : $"Server=localhost;Database={databaseName};Uid=dommeltest;Pwd=test;"; + + return new MySqlConnection(connectionString); + } + + public override string TempDbDatabaseName => "mysql"; + + public override async Task CreateDatabase() + { + using (var con = GetConnection(TempDbDatabaseName)) + { + await con.ExecuteAsync("CREATE DATABASE IF NOT EXISTS DommelTests"); + } + } + + public override async Task CreateTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + var sql = @" +SELECT * FROM information_schema.tables where table_name = 'Products' LIMIT 1; +CREATE TABLE IF NOT EXISTS Categories (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(255)); +CREATE TABLE IF NOT EXISTS Products (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, CategoryId int, Name VARCHAR(255)); +CREATE TABLE IF NOT EXISTS Orders (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Created DATETIME NOT NULL); +CREATE TABLE IF NOT EXISTS OrderLines (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, OrderId int, Line VARCHAR(255));"; + var created = await con.ExecuteScalarAsync(sql); + + // No result means the tables were just created + return created == null; + } + } + } +} diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index e12149e7..21a3ba84 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -5,6 +5,7 @@ latest + diff --git a/test/Dommel.IntegrationTests/DommelTestBase.cs b/test/Dommel.IntegrationTests/DommelTestBase.cs index c842a813..f0a45262 100644 --- a/test/Dommel.IntegrationTests/DommelTestBase.cs +++ b/test/Dommel.IntegrationTests/DommelTestBase.cs @@ -1,69 +1,79 @@ using System; using System.Collections.Generic; -using System.Data.SqlClient; +using System.Data.Common; using System.Threading.Tasks; -using Dapper; using Xunit; -[assembly:CollectionBehavior(DisableTestParallelization = true)] +[assembly: CollectionBehavior(DisableTestParallelization = true)] namespace Dommel.IntegrationTests { [Collection("IntegrationTests")] public abstract class DommelTestBase : IAsyncLifetime { - protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; + public DommelTestBase(Database database) + { + Database = database; + } + + protected Database Database { get; } - protected virtual string GetConnectionString(string databaseName = "DommelTests") => - IsAppVeyor - ? $"Server=(local)\\SQL2016;Database={databaseName};User ID=sa;Password=Password12!" - : $"Server=(LocalDb)\\mssqllocaldb;Database={databaseName};Integrated Security=True"; + protected DbConnection GetConnection() => Database.GetConnection(Database.DefaultDatabaseName); public virtual async Task InitializeAsync() { - using (var con = new SqlConnection(GetConnectionString("tempdb"))) + using (var connection = Database.GetConnection(Database.TempDbDatabaseName)) { - await con.ExecuteAsync("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'DommelTests') BEGIN CREATE DATABASE DommelTests; END;"); + await Database.CreateDatabase(); } - using (var connection = new SqlConnection(GetConnectionString())) + using (var connection = GetConnection()) { await connection.OpenAsync(); - var sql = "IF OBJECT_ID(N'dbo.Products', N'U') IS NULL BEGIN CREATE TABLE dbo.Products (Id int IDENTITY(1,1) PRIMARY KEY, Name varchar(255) not null); SELECT 1; END;"; - var created = await connection.ExecuteScalarAsync(sql); - if (created != null) + var created = await Database.CreateTables(); + + // Is the table created? If so, insert dummy data + if (created) { - // Table was just created, insert dummy data + var categoryId = Convert.ToInt32(await connection.InsertAsync(new Category { Name = "Food" })); + var products = new List { - new Product { Name = "Chai" }, - new Product { Name = "Chang" }, - new Product { Name = "Aniseed Syrup" }, - new Product { Name = "Chef Anton's Cajun Seasoning" }, - new Product { Name = "Chef Anton's Gumbo Mix" }, + new Product { CategoryId = categoryId, Name = "Chai" }, + new Product { CategoryId = categoryId, Name = "Chang" }, + new Product { CategoryId = categoryId, Name = "Aniseed Syrup" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix" }, - new Product { Name = "Chai 2" }, - new Product { Name = "Chang 2" }, - new Product { Name = "Aniseed Syrup 2" }, - new Product { Name = "Chef Anton's Cajun Seasoning 2" }, - new Product { Name = "Chef Anton's Gumbo Mix 2" }, + new Product { CategoryId = categoryId, Name = "Chai 2" }, + new Product { CategoryId = categoryId, Name = "Chang 2" }, + new Product { CategoryId = categoryId, Name = "Aniseed Syrup 2" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning 2" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix 2" }, - new Product { Name = "Chai 3" }, - new Product { Name = "Chang 3" }, - new Product { Name = "Aniseed Syrup 3" }, + new Product { CategoryId = categoryId, Name = "Chai 3" }, + new Product { CategoryId = categoryId, Name = "Chang 3" }, + new Product { CategoryId = categoryId, Name = "Aniseed Syrup 3" }, }; await connection.InsertAsyncAll(products); + + var orderId = Convert.ToInt32(await connection.InsertAsync(new Order())); + var orderLines = new List + { + new OrderLine { OrderId = orderId, Line = "Line 1"}, + new OrderLine { OrderId = orderId, Line = "Line 2"}, + new OrderLine { OrderId = orderId, Line = "Line 3"}, + }; + await connection.InsertAsyncAll(orderLines); } } } public virtual async Task DisposeAsync() { - using (var con = new SqlConnection(GetConnectionString())) - { - await con.ExecuteAsync("IF OBJECT_ID(N'dbo.Products', N'U') IS NOT NULL BEGIN DROP TABLE dbo.Products; END;"); - } + await Database.DropTables(); + DommelMapper.ClearQueryCache(); } } } diff --git a/test/Dommel.IntegrationTests/InsertTests.cs b/test/Dommel.IntegrationTests/InsertTests.cs index 7eb4071c..45dfd3c1 100644 --- a/test/Dommel.IntegrationTests/InsertTests.cs +++ b/test/Dommel.IntegrationTests/InsertTests.cs @@ -1,18 +1,35 @@ using System; using System.Collections.Generic; -using System.Data.SqlClient; using System.Linq; using System.Threading.Tasks; using Xunit; namespace Dommel.IntegrationTests { - public class InsertTests : DommelTestBase + public class InsertTestsSqlServer : InsertTests, IClassFixture { + public InsertTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class InsertTestsMySql : InsertTests, IClassFixture + { + public InsertTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class InsertTests : DommelTestBase + { + public InsertTests(Database database) : base(database) + { + } + [Fact] public void Insert() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var id = Convert.ToInt32(con.Insert(new Product { Name = "blah" })); var product = con.Get(id); @@ -25,7 +42,7 @@ public void Insert() [Fact] public async Task InsertAsync() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var id = Convert.ToInt32(await con.InsertAsync(new Product { Name = "blah" })); var product = await con.GetAsync(id); @@ -38,7 +55,7 @@ public async Task InsertAsync() [Fact] public void InsertAll() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var ps = new List { @@ -57,7 +74,7 @@ public void InsertAll() [Fact] public async Task InsertAllAsync() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var ps = new List { @@ -76,7 +93,7 @@ public async Task InsertAllAsync() [Fact] public void InsertAllEmtyList() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var ps = new List(); con.InsertAll(ps); @@ -86,7 +103,7 @@ public void InsertAllEmtyList() [Fact] public async Task InsertAllAsyncEmtyList() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var ps = new List(); await con.InsertAsyncAll(ps); diff --git a/test/Dommel.IntegrationTests/Models.cs b/test/Dommel.IntegrationTests/Models.cs new file mode 100644 index 00000000..d5a41ac2 --- /dev/null +++ b/test/Dommel.IntegrationTests/Models.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Dommel.IntegrationTests +{ + public class Product + { + public int Id { get; set; } + + public string Name { get; set; } + + // The foreign key to Categories table + public int CategoryId { get; set; } + + // The navigation property + public Category Category { get; set; } + } + + public class Category + { + public int Id { get; set; } + + public string Name { get; set; } + } + + public class Order + { + public int Id { get; set; } + + public DateTime Created { get; set; } = DateTime.UtcNow; + + [ForeignKey(nameof(OrderLine.OrderId))] + public ICollection OrderLines { get; set; } + } + + public class OrderLine + { + public int Id { get; set; } + + public int OrderId { get; set; } + + public string Line { get; set; } + } +} diff --git a/test/Dommel.IntegrationTests/PagingTests.cs b/test/Dommel.IntegrationTests/PagingTests.cs index c1f22676..33176b74 100644 --- a/test/Dommel.IntegrationTests/PagingTests.cs +++ b/test/Dommel.IntegrationTests/PagingTests.cs @@ -1,17 +1,33 @@ -using System.Collections.Generic; -using System.Data.SqlClient; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Xunit; namespace Dommel.IntegrationTests { - public class PagingTests : DommelTestBase + public class PagingTestsSqlServer : PagingTests, IClassFixture { + public PagingTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class PagingTestsMySql : PagingTests, IClassFixture + { + public PagingTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class PagingTests : DommelTestBase + { + public PagingTests(Database database) : base(database) + { + } + [Fact] public void Fetches_FirstPage() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var paged = con.GetPaged(1, 5).ToArray(); Assert.Equal(5, paged.Length); @@ -27,7 +43,7 @@ public void Fetches_FirstPage() [Fact] public void Fetches_SecondPage() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var paged = con.GetPaged(2, 5).ToArray(); Assert.Equal(5, paged.Length); @@ -37,7 +53,7 @@ public void Fetches_SecondPage() [Fact] public async Task Fetches_ThirdPartialPage() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var paged = (await con.GetPagedAsync(3, 5)).ToArray(); Assert.Equal(3, paged.Length); @@ -47,18 +63,11 @@ public async Task Fetches_ThirdPartialPage() [Fact] public async Task SelectPaged_FetchesFirstPage() { - using (var con = new SqlConnection(GetConnectionString())) + using (var con = GetConnection()) { var paged = (await con.SelectPagedAsync(p => p.Name == "Chai", 1, 5)).ToArray(); Assert.Single(paged); } } } - - public class Product - { - public int Id { get; set; } - - public string Name { get; set; } - } } From d8d611c21223444b484e6206081a219b903a7834 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 19:56:29 +0100 Subject: [PATCH 03/18] No dotnet --info --- build.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/build.ps1 b/build.ps1 index 73c6b885..ad23fd27 100644 --- a/build.ps1 +++ b/build.ps1 @@ -13,7 +13,6 @@ function Exec if(Test-Path .\src\Dommel\artifacts) { Remove-Item .\src\Dommel\artifacts -Force -Recurse } -exec { & dotnet --info } exec { & dotnet restore } $branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; From bc8789bdc76766566ad7228130871ca4a51f6df8 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 20:18:43 +0100 Subject: [PATCH 04/18] Add @ to SQL parameter --- src/Dommel/DommelMapper.SqlExpression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dommel/DommelMapper.SqlExpression.cs b/src/Dommel/DommelMapper.SqlExpression.cs index e7c9f61e..6299581f 100644 --- a/src/Dommel/DommelMapper.SqlExpression.cs +++ b/src/Dommel/DommelMapper.SqlExpression.cs @@ -168,7 +168,7 @@ protected virtual object VisitContainsExpression(MethodCallExpression expression } AddParameter(textLike, out var paramName); - return $"{column} like {paramName}"; + return $"{column} like @{paramName}"; } /// From c7d920252dd88e3e665fcadebbfe69068d53d887 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 20:28:47 +0100 Subject: [PATCH 05/18] InsertAsyncAll -> InsertAllAsync --- src/Dommel/DommelMapper.Insert.cs | 2 +- test/Dommel.IntegrationTests/InsertTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Dommel/DommelMapper.Insert.cs b/src/Dommel/DommelMapper.Insert.cs index 062ae122..5200dbe7 100644 --- a/src/Dommel/DommelMapper.Insert.cs +++ b/src/Dommel/DommelMapper.Insert.cs @@ -66,7 +66,7 @@ public static void InsertAll(this IDbConnection connection, IEnumerable /// The entities to be inserted. /// Optional transaction for the command. /// The id of the inserted entity. - public static async Task InsertAsyncAll(this IDbConnection connection, IEnumerable entities, IDbTransaction transaction = null) where TEntity : class + public static async Task InsertAllAsync(this IDbConnection connection, IEnumerable entities, IDbTransaction transaction = null) where TEntity : class { var sql = BuildInsertQuery(connection, typeof(TEntity)); LogQuery(sql); diff --git a/test/Dommel.IntegrationTests/InsertTests.cs b/test/Dommel.IntegrationTests/InsertTests.cs index 45dfd3c1..5484ad81 100644 --- a/test/Dommel.IntegrationTests/InsertTests.cs +++ b/test/Dommel.IntegrationTests/InsertTests.cs @@ -83,7 +83,7 @@ public async Task InsertAllAsync() new Product { Name = "blah"}, }; - await con.InsertAsyncAll(ps); + await con.InsertAllAsync(ps); var blahs = await con.SelectAsync(p => p.Name == "blah"); Assert.Equal(3, blahs.Count()); @@ -106,7 +106,7 @@ public async Task InsertAllAsyncEmtyList() using (var con = GetConnection()) { var ps = new List(); - await con.InsertAsyncAll(ps); + await con.InsertAllAsync(ps); } } } From 27b0b4b5b521d42d014a7a214b47056b2227da42 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 20:29:17 +0100 Subject: [PATCH 06/18] Add non-async auto-multi-mapped Get methods --- src/Dommel/DommelMapper.AutoMultiMap.cs | 129 ++++++++++++++++++ .../AutoMultiMapTests.cs | 73 +++++++++- 2 files changed, 200 insertions(+), 2 deletions(-) diff --git a/src/Dommel/DommelMapper.AutoMultiMap.cs b/src/Dommel/DommelMapper.AutoMultiMap.cs index 9592de2c..842bfecd 100644 --- a/src/Dommel/DommelMapper.AutoMultiMap.cs +++ b/src/Dommel/DommelMapper.AutoMultiMap.cs @@ -9,6 +9,25 @@ namespace Dommel { public static partial class DommelMapper { + /// + /// Retrieves the automatically mapped entity of type with the specified + /// joined with the types specified as type parameters. + /// + /// The first type parameter. This is the source entity. + /// The second type parameter. + /// The return type parameter. + /// The connection to the database. This can either be open or closed. + /// The id of the entity in the database. + /// Optional transaction for the command. + /// The entity with the corresponding id joined with the specified types. + public static TReturn Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where T1 : TReturn + => MultiMap( + connection, + CreateMapDelegate(), + id, + transaction) + .FirstOrDefault(); + /// /// Retrieves the automatically mapped entity of type with the specified /// joined with the types specified as type parameters. @@ -28,6 +47,26 @@ public static async Task GetAsync(this IDbConnection c transaction)) .FirstOrDefault(); + /// + /// Retrieves the automatically mapped entity of type with the specified + /// joined with the types specified as type parameters. + /// + /// The first type parameter. This is the source entity. + /// The second type parameter. + /// The third type parameter. + /// The return type parameter. + /// The connection to the database. This can either be open or closed. + /// The id of the entity in the database. + /// Optional transaction for the command. + /// The entity with the corresponding id joined with the specified types. + public static TReturn Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where T1 : TReturn + => MultiMap( + connection, + CreateMapDelegate(), + id, + transaction) + .FirstOrDefault(); + /// /// Retrieves the automatically mapped entity of type with the specified /// joined with the types specified as type parameters. @@ -48,6 +87,27 @@ public static async Task GetAsync(this IDbConnecti transaction)) .FirstOrDefault(); + /// + /// Retrieves the automatically mapped entity of type with the specified + /// joined with the types specified as type parameters. + /// + /// The first type parameter. This is the source entity. + /// The second type parameter. + /// The third type parameter. + /// The fourth type parameter. + /// The return type parameter. + /// The connection to the database. This can either be open or closed. + /// The id of the entity in the database. + /// Optional transaction for the command. + /// The entity with the corresponding id joined with the specified types. + public static TReturn Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where T1 : TReturn + => MultiMap( + connection, + CreateMapDelegate(), + id, + transaction) + .FirstOrDefault(); + /// /// Retrieves the automatically mapped entity of type with the specified /// joined with the types specified as type parameters. @@ -69,6 +129,28 @@ public static async Task GetAsync(this IDbConn transaction)) .FirstOrDefault(); + /// + /// Retrieves the automatically mapped entity of type with the specified + /// joined with the types specified as type parameters. + /// + /// The first type parameter. This is the source entity. + /// The second type parameter. + /// The third type parameter. + /// The fourth type parameter. + /// The fifth type parameter. + /// The return type parameter. + /// The connection to the database. This can either be open or closed. + /// The id of the entity in the database. + /// Optional transaction for the command. + /// The entity with the corresponding id joined with the specified types. + public static TReturn Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where T1 : TReturn + => MultiMap( + connection, + CreateMapDelegate(), + id, + transaction) + .FirstOrDefault(); + /// /// Retrieves the automatically mapped entity of type with the specified /// joined with the types specified as type parameters. @@ -91,6 +173,29 @@ public static async Task GetAsync(this IDb transaction)) .FirstOrDefault(); + /// + /// Retrieves the automatically mapped entity of type with the specified + /// joined with the types specified as type parameters. + /// + /// The first type parameter. This is the source entity. + /// The second type parameter. + /// The third type parameter. + /// The fourth type parameter. + /// The fifth type parameter. + /// The sixth type parameter. + /// The return type parameter. + /// The connection to the database. This can either be open or closed. + /// The id of the entity in the database. + /// Optional transaction for the command. + /// The entity with the corresponding id joined with the specified types. + public static TReturn Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where T1 : TReturn + => MultiMap( + connection, + CreateMapDelegate(), + id, + transaction) + .FirstOrDefault(); + /// /// Retrieves the automatically mapped entity of type with the specified /// joined with the types specified as type parameters. @@ -114,6 +219,30 @@ public static async Task GetAsync(this transaction)) .FirstOrDefault(); + /// + /// Retrieves the automatically mapped entity of type with the specified + /// joined with the types specified as type parameters. + /// + /// The first type parameter. This is the source entity. + /// The second type parameter. + /// The third type parameter. + /// The fourth type parameter. + /// The fifth type parameter. + /// The sixth type parameter. + /// The seventh type parameter. + /// The return type parameter. + /// The connection to the database. This can either be open or closed. + /// The id of the entity in the database. + /// Optional transaction for the command. + /// The entity with the corresponding id joined with the specified types. + public static TReturn Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where T1 : TReturn + => MultiMap( + connection, + CreateMapDelegate(), + id, + transaction) + .FirstOrDefault(); + /// /// Retrieves the automatically mapped entity of type with the specified /// joined with the types specified as type parameters. diff --git a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs index b844ab7d..406f0277 100644 --- a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs +++ b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs @@ -24,7 +24,54 @@ public AutoMultiMapTests(Database database) : base(database) } [Fact] - public async Task OneToOne() + public void OneToOne() + { + using (var con = GetConnection()) + { + var product = con.Get(1); + Assert.NotNull(product); + Assert.NotNull(product.Category); + Assert.Equal("Food", product.Category.Name); + Assert.Equal(product.CategoryId, product.Category.Id); + } + } + + [Fact] + public void OneToOneNotExisting() + { + using (var con = GetConnection()) + { + var product = con.Get(11); + Assert.NotNull(product); + Assert.Null(product.Category); + } + } + + [Fact] + public void OneToMany() + { + using (var con = GetConnection()) + { + var order = con.Get(1); + Assert.NotNull(order); + Assert.NotNull(order.OrderLines); + Assert.Equal(3, order.OrderLines.Count); + } + } + + [Fact] + public void OneToManyNonExsting() + { + using (var con = GetConnection()) + { + var order = con.Get(2); + Assert.NotNull(order); + Assert.Null(order.OrderLines); + } + } + + [Fact] + public async Task OneToOneAsync() { using (var con = GetConnection()) { @@ -37,7 +84,18 @@ public async Task OneToOne() } [Fact] - public async Task OneToMany() + public async Task OneToOneNotExistingAsync() + { + using (var con = GetConnection()) + { + var product = await con.GetAsync(11); + Assert.NotNull(product); + Assert.Null(product.Category); + } + } + + [Fact] + public async Task OneToManyAsync() { using (var con = GetConnection()) { @@ -47,5 +105,16 @@ public async Task OneToMany() Assert.Equal(3, order.OrderLines.Count); } } + + [Fact] + public async Task OneToManyNonExstingAsync() + { + using (var con = GetConnection()) + { + var order = await con.GetAsync(2); + Assert.NotNull(order); + Assert.Null(order.OrderLines); + } + } } } From 4bd56558b682971e1f966eb9502a8b7d54796737 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 20:37:06 +0100 Subject: [PATCH 07/18] Tests for most of public API --- test/Dommel.IntegrationTests/DeleteTests.cs | 161 ++++++++++++++++++ .../Dommel.IntegrationTests/DommelTestBase.cs | 14 +- test/Dommel.IntegrationTests/GetAllTests.cs | 48 ++++++ test/Dommel.IntegrationTests/GetTests.cs | 48 ++++++ test/Dommel.IntegrationTests/SelectTests.cs | 66 +++++++ test/Dommel.IntegrationTests/TestSample.cs | 44 +++++ test/Dommel.IntegrationTests/UpdateTests.cs | 58 +++++++ 7 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 test/Dommel.IntegrationTests/DeleteTests.cs create mode 100644 test/Dommel.IntegrationTests/GetAllTests.cs create mode 100644 test/Dommel.IntegrationTests/GetTests.cs create mode 100644 test/Dommel.IntegrationTests/SelectTests.cs create mode 100644 test/Dommel.IntegrationTests/TestSample.cs create mode 100644 test/Dommel.IntegrationTests/UpdateTests.cs diff --git a/test/Dommel.IntegrationTests/DeleteTests.cs b/test/Dommel.IntegrationTests/DeleteTests.cs new file mode 100644 index 00000000..8532a6bf --- /dev/null +++ b/test/Dommel.IntegrationTests/DeleteTests.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class DeleteTestsSqlServer : DeleteTests, IClassFixture + { + public DeleteTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class DeleteTestsMySql : DeleteTests, IClassFixture + { + public DeleteTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class DeleteTests : DommelTestBase + { + public DeleteTests(Database database) : base(database) + { + } + + [Fact] + public void Delete() + { + using (var con = GetConnection()) + { + var id = Convert.ToInt32(con.Insert(new Product { Name = "blah" })); + var product = con.Get(id); + Assert.NotNull(product); + Assert.Equal("blah", product.Name); + Assert.Equal(id, product.Id); + + con.Delete(product); + Assert.Null(con.Get(id)); + } + } + + [Fact] + public async Task DeleteAsync() + { + using (var con = GetConnection()) + { + var id = Convert.ToInt32(await con.InsertAsync(new Product { Name = "blah" })); + var product = await con.GetAsync(id); + Assert.NotNull(product); + Assert.Equal("blah", product.Name); + Assert.Equal(id, product.Id); + + await con.DeleteAsync(product); + Assert.Null(await con.GetAsync(id)); + } + } + + [Fact] + public void DeleteAll() + { + using (var con = GetConnection()) + { + Assert.True(con.DeleteAll()); + Assert.Empty(con.GetAll()); + } + } + + [Fact] + public async Task DeleteAllAsync() + { + using (var con = GetConnection()) + { + Assert.True(await con.DeleteAllAsync()); + Assert.Empty(await con.GetAllAsync()); + } + } + + [Fact] + public void DeleteMultiple() + { + using (var con = GetConnection()) + { + var ps = new List + { + new Product { Name = "blah"}, + new Product { Name = "blah"}, + new Product { Name = "blah"}, + }; + + con.InsertAll(ps); + + Assert.Equal(3, con.Select(p => p.Name == "blah").Count()); + + con.DeleteMultiple(p => p.Name == "blah"); + } + } + + [Fact] + public async Task DeleteMultipleAsync() + { + using (var con = GetConnection()) + { + var ps = new List + { + new Product { Name = "blah"}, + new Product { Name = "blah"}, + new Product { Name = "blah"}, + }; + + await con.InsertAllAsync(ps); + + Assert.Equal(3, (await con.SelectAsync(p => p.Name == "blah")).Count()); + + con.DeleteMultiple(p => p.Name == "blah"); + } + } + + [Fact] + public void DeleteMultipleLike() + { + using (var con = GetConnection()) + { + var ps = new List + { + new Product { Name = "blah"}, + new Product { Name = "blah"}, + new Product { Name = "blah"}, + }; + + con.InsertAll(ps); + + Assert.Equal(3, con.Select(p => p.Name == "blah").Count()); + + con.DeleteMultiple(p => p.Name.Contains("bla")); + } + } + + [Fact] + public async Task DeleteMultipleAsyncLike() + { + using (var con = GetConnection()) + { + var ps = new List + { + new Product { Name = "blah"}, + new Product { Name = "blah"}, + new Product { Name = "blah"}, + }; + + await con.InsertAllAsync(ps); + + Assert.Equal(3, (await con.SelectAsync(p => p.Name == "blah")).Count()); + + con.DeleteMultiple(p => p.Name.Contains("bla")); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/DommelTestBase.cs b/test/Dommel.IntegrationTests/DommelTestBase.cs index f0a45262..c8a8f9a9 100644 --- a/test/Dommel.IntegrationTests/DommelTestBase.cs +++ b/test/Dommel.IntegrationTests/DommelTestBase.cs @@ -51,13 +51,14 @@ public virtual async Task InitializeAsync() new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning 2" }, new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix 2" }, - new Product { CategoryId = categoryId, Name = "Chai 3" }, - new Product { CategoryId = categoryId, Name = "Chang 3" }, - new Product { CategoryId = categoryId, Name = "Aniseed Syrup 3" }, + new Product { Name = "Foo" }, // 11 + new Product { Name = "Bar" }, // 12 + new Product { Name = "Baz" }, // 13 }; - await connection.InsertAsyncAll(products); + await connection.InsertAllAsync(products); + // Order 1 var orderId = Convert.ToInt32(await connection.InsertAsync(new Order())); var orderLines = new List { @@ -65,7 +66,10 @@ public virtual async Task InitializeAsync() new OrderLine { OrderId = orderId, Line = "Line 2"}, new OrderLine { OrderId = orderId, Line = "Line 3"}, }; - await connection.InsertAsyncAll(orderLines); + await connection.InsertAllAsync(orderLines); + + // Order 2 + _ = await connection.InsertAsync(new Order()); } } } diff --git a/test/Dommel.IntegrationTests/GetAllTests.cs b/test/Dommel.IntegrationTests/GetAllTests.cs new file mode 100644 index 00000000..85a07761 --- /dev/null +++ b/test/Dommel.IntegrationTests/GetAllTests.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class GetAllTestsSqlServer : GetAllTests, IClassFixture + { + public GetAllTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class GetAllTestsMySql : GetAllTests, IClassFixture + { + public GetAllTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class GetAllTests : DommelTestBase + { + public GetAllTests(Database database) : base(database) + { + } + + [Fact] + public void GetAll() + { + using (var con = GetConnection()) + { + var products = con.GetAll(); + Assert.NotEmpty(products); + Assert.All(products, p => Assert.NotEmpty(p.Name)); + } + } + + [Fact] + public async Task GetAllAsync() + { + using (var con = GetConnection()) + { + var products = await con.GetAllAsync(); + Assert.NotEmpty(products); + Assert.All(products, p => Assert.NotEmpty(p.Name)); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/GetTests.cs b/test/Dommel.IntegrationTests/GetTests.cs new file mode 100644 index 00000000..278644b9 --- /dev/null +++ b/test/Dommel.IntegrationTests/GetTests.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class GetTestsSqlServer : GetTests, IClassFixture + { + public GetTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class GetTestsMySql : GetTests, IClassFixture + { + public GetTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class GetTests : DommelTestBase + { + public GetTests(Database database) : base(database) + { + } + + [Fact] + public void Get() + { + using (var con = GetConnection()) + { + var product = con.Get(1); + Assert.NotNull(product); + Assert.NotEmpty(product.Name); + } + } + + [Fact] + public async Task GetAsync() + { + using (var con = GetConnection()) + { + var product = await con.GetAsync(1); + Assert.NotNull(product); + Assert.NotEmpty(product.Name); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/SelectTests.cs b/test/Dommel.IntegrationTests/SelectTests.cs new file mode 100644 index 00000000..b57c7c30 --- /dev/null +++ b/test/Dommel.IntegrationTests/SelectTests.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class SelectTestsSqlServer : SelectTests, IClassFixture + { + public SelectTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class SelectTestsMySql : SelectTests, IClassFixture + { + public SelectTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class SelectTests : DommelTestBase + { + public SelectTests(Database database) : base(database) + { + } + + [Fact] + public void SelectEqual() + { + using (var con = GetConnection()) + { + var products = con.Select(p => p.CategoryId == 1); + Assert.NotEmpty(products); + } + } + + [Fact] + public async Task SelectAsyncEqual() + { + using (var con = GetConnection()) + { + var products = await con.SelectAsync(p => p.CategoryId == 1); + Assert.NotEmpty(products); + } + } + + [Fact] + public void SelectContains() + { + using (var con = GetConnection()) + { + var products = con.Select(p => p.Name.Contains("Chai")); + Assert.NotEmpty(products); + } + } + + [Fact] + public async Task SelectAsyncContains() + { + using (var con = GetConnection()) + { + var products = await con.SelectAsync(p => p.Name.Contains("Chai")); + Assert.NotEmpty(products); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/TestSample.cs b/test/Dommel.IntegrationTests/TestSample.cs new file mode 100644 index 00000000..27b494e1 --- /dev/null +++ b/test/Dommel.IntegrationTests/TestSample.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class SampleTestsSqlServer : SampleTests, IClassFixture + { + public SampleTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class SampleTestsMySql : SampleTests, IClassFixture + { + public SampleTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class SampleTests : DommelTestBase + { + public SampleTests(Database database) : base(database) + { + } + + [Fact] + public void Sample() + { + using (var con = GetConnection()) + { + _ = con.GetAll(); + } + } + + [Fact] + public async Task SampleAsync() + { + using (var con = GetConnection()) + { + _ = await con.GetAllAsync(); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/UpdateTests.cs b/test/Dommel.IntegrationTests/UpdateTests.cs new file mode 100644 index 00000000..0fc1ec80 --- /dev/null +++ b/test/Dommel.IntegrationTests/UpdateTests.cs @@ -0,0 +1,58 @@ +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class UpdateTestsSqlServer : UpdateTests, IClassFixture + { + public UpdateTestsSqlServer(SqlServerDatabase database) : base(database) + { + } + } + + public class UpdateTestsMySql : UpdateTests, IClassFixture + { + public UpdateTestsMySql(MySqlDatabase database) : base(database) + { + } + } + + public abstract class UpdateTests : DommelTestBase + { + public UpdateTests(Database database) : base(database) + { + } + + [Fact] + public void Update() + { + using (var con = GetConnection()) + { + var product = con.Get(1); + Assert.NotNull(product); + + product.Name = "Test"; + con.Update(product); + + var newProduct = con.Get(1); + Assert.Equal("Test", newProduct.Name); + } + } + + [Fact] + public async Task UpdateAsync() + { + using (var con = GetConnection()) + { + var product = await con.GetAsync(1); + Assert.NotNull(product); + + product.Name = "Test"; + con.Update(product); + + var newProduct = await con.GetAsync(1); + Assert.Equal("Test", newProduct.Name); + } + } + } +} From 9d7aa2bf5d632aa8440ab67866a3c36e79fbae62 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 22:13:07 +0100 Subject: [PATCH 08/18] Cache insert queries per connection type --- src/Dommel/DommelMapper.Insert.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Dommel/DommelMapper.Insert.cs b/src/Dommel/DommelMapper.Insert.cs index 5200dbe7..f8297692 100644 --- a/src/Dommel/DommelMapper.Insert.cs +++ b/src/Dommel/DommelMapper.Insert.cs @@ -11,7 +11,7 @@ namespace Dommel { public static partial class DommelMapper { - private static readonly ConcurrentDictionary _insertQueryCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _insertQueryCache = new ConcurrentDictionary(); /// /// Inserts the specified entity into the database and returns the ID. @@ -75,7 +75,8 @@ public static async Task InsertAllAsync(this IDbConnection connection, private static string BuildInsertQuery(IDbConnection connection, Type type) { - if (!_insertQueryCache.TryGetValue(type, out var sql)) + var cacheKey = $"{connection.GetType().Name}.{type.Name}"; + if (!_insertQueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type); var keyProperty = Resolvers.KeyProperty(type, out var isIdentity); @@ -104,7 +105,7 @@ private static string BuildInsertQuery(IDbConnection connection, Type type) var builder = GetSqlBuilder(connection); sql = builder.BuildInsert(tableName, columnNames, paramNames, keyProperty); - _insertQueryCache.TryAdd(type, sql); + _insertQueryCache.TryAdd(cacheKey, sql); } return sql; From b5d36ecd9304653fca958a7da3bff77454a29d74 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 22:14:27 +0100 Subject: [PATCH 09/18] Split database providers and add Postgres --- test/Dommel.IntegrationTests/Database.cs | 114 ------------------ .../Databases/Database.cs | 34 ++++++ .../Databases/MySqlDatabase.cs | 46 +++++++ .../Databases/PostgresDatabase.cs | 74 ++++++++++++ .../Databases/SqlServerDatabase.cs | 46 +++++++ .../Dommel.IntegrationTests.csproj | 1 + .../Dommel.IntegrationTests/DommelTestBase.cs | 3 +- 7 files changed, 202 insertions(+), 116 deletions(-) delete mode 100644 test/Dommel.IntegrationTests/Database.cs create mode 100644 test/Dommel.IntegrationTests/Databases/Database.cs create mode 100644 test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs create mode 100644 test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs create mode 100644 test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs diff --git a/test/Dommel.IntegrationTests/Database.cs b/test/Dommel.IntegrationTests/Database.cs deleted file mode 100644 index e026d78b..00000000 --- a/test/Dommel.IntegrationTests/Database.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Data.Common; -using System.Data.SqlClient; -using System.Threading.Tasks; -using Dapper; -using MySql.Data.MySqlClient; - -namespace Dommel.IntegrationTests -{ - public abstract class Database - { - protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; - - public virtual string TempDbDatabaseName => "tempdb"; - - public virtual string DefaultDatabaseName => "DommelTests"; - - public abstract DbConnection GetConnection(string databaseName); - - public abstract Task CreateDatabase(); - - public abstract Task CreateTables(); - - public virtual async Task DropTables() - { - using (var con = GetConnection(DefaultDatabaseName)) - { - await con.ExecuteAsync(@" -DROP TABLE Categories; -DROP TABLE Products; -DROP TABLE Orders; -DROP TABLE OrderLines;"); - } - } - } - - public class SqlServerDatabase : Database - { - public override DbConnection GetConnection(string databaseName) - { - var connectionString = IsAppVeyor - ? $"Server=(local)\\SQL2016;Database={databaseName};User ID=sa;Password=Password12!" - : $"Server=(LocalDb)\\mssqllocaldb;Database={databaseName};Integrated Security=True"; - - return new SqlConnection(connectionString); - } - - public override async Task CreateDatabase() - { - using (var con = GetConnection(TempDbDatabaseName)) - { - await con.ExecuteAsync("IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'DommelTests') BEGIN CREATE DATABASE DommelTests; END;"); - } - } - - public override async Task CreateTables() - { - using (var con = GetConnection(DefaultDatabaseName)) - { - var sql = @"IF OBJECT_ID(N'dbo.Products', N'U') IS NULL -BEGIN - CREATE TABLE dbo.Categories (Id int IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255)); - CREATE TABLE dbo.Products (Id int IDENTITY(1,1) PRIMARY KEY, CategoryId int, Name VARCHAR(255)); - CREATE TABLE dbo.Orders (Id int IDENTITY(1,1) PRIMARY KEY, Created DATETIME NOT NULL); - CREATE TABLE dbo.OrderLines (Id int IDENTITY(1,1) PRIMARY KEY, OrderId int, Line VARCHAR(255)); - SELECT 1; -END"; - var created = await con.ExecuteScalarAsync(sql); - - // A result means the tables were just created - return created != null; - } - } - } - - public class MySqlDatabase : Database - { - public override DbConnection GetConnection(string databaseName) - { - var connectionString = IsAppVeyor - ? $"Server=localhost;Database={databaseName};Uid=root;Pwd=Password12!;" - : $"Server=localhost;Database={databaseName};Uid=dommeltest;Pwd=test;"; - - return new MySqlConnection(connectionString); - } - - public override string TempDbDatabaseName => "mysql"; - - public override async Task CreateDatabase() - { - using (var con = GetConnection(TempDbDatabaseName)) - { - await con.ExecuteAsync("CREATE DATABASE IF NOT EXISTS DommelTests"); - } - } - - public override async Task CreateTables() - { - using (var con = GetConnection(DefaultDatabaseName)) - { - var sql = @" -SELECT * FROM information_schema.tables where table_name = 'Products' LIMIT 1; -CREATE TABLE IF NOT EXISTS Categories (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(255)); -CREATE TABLE IF NOT EXISTS Products (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, CategoryId int, Name VARCHAR(255)); -CREATE TABLE IF NOT EXISTS Orders (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Created DATETIME NOT NULL); -CREATE TABLE IF NOT EXISTS OrderLines (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, OrderId int, Line VARCHAR(255));"; - var created = await con.ExecuteScalarAsync(sql); - - // No result means the tables were just created - return created == null; - } - } - } -} diff --git a/test/Dommel.IntegrationTests/Databases/Database.cs b/test/Dommel.IntegrationTests/Databases/Database.cs new file mode 100644 index 00000000..0cf019cc --- /dev/null +++ b/test/Dommel.IntegrationTests/Databases/Database.cs @@ -0,0 +1,34 @@ +using System; +using System.Data.Common; +using System.Threading.Tasks; +using Dapper; + +namespace Dommel.IntegrationTests +{ + public abstract class Database + { + protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; + + public virtual string TempDbDatabaseName => "tempdb"; + + public virtual string DefaultDatabaseName => "dommeltests"; + + public abstract DbConnection GetConnection(string databaseName); + + public abstract Task CreateDatabase(); + + public abstract Task CreateTables(); + + public virtual async Task DropTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + await con.ExecuteAsync(@" +DROP TABLE Categories; +DROP TABLE Products; +DROP TABLE Orders; +DROP TABLE OrderLines;"); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs b/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs new file mode 100644 index 00000000..7c9622e3 --- /dev/null +++ b/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs @@ -0,0 +1,46 @@ +using System.Data.Common; +using System.Threading.Tasks; +using Dapper; +using MySql.Data.MySqlClient; + +namespace Dommel.IntegrationTests +{ + public class MySqlDatabase : Database + { + public override DbConnection GetConnection(string databaseName) + { + var connectionString = IsAppVeyor + ? $"Server=localhost;Database={databaseName};Uid=root;Pwd=Password12!;" + : $"Server=localhost;Database={databaseName};Uid=dommeltest;Pwd=test;"; + + return new MySqlConnection(connectionString); + } + + public override string TempDbDatabaseName => "mysql"; + + public override async Task CreateDatabase() + { + using (var con = GetConnection(TempDbDatabaseName)) + { + await con.ExecuteAsync($"CREATE DATABASE IF NOT EXISTS {DefaultDatabaseName}"); + } + } + + public override async Task CreateTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + var sql = @" +SELECT * FROM information_schema.tables where table_name = 'Products' LIMIT 1; +CREATE TABLE IF NOT EXISTS Categories (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(255)); +CREATE TABLE IF NOT EXISTS Products (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, CategoryId int, Name VARCHAR(255)); +CREATE TABLE IF NOT EXISTS Orders (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Created DATETIME NOT NULL); +CREATE TABLE IF NOT EXISTS OrderLines (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, OrderId int, Line VARCHAR(255));"; + var created = await con.ExecuteScalarAsync(sql); + + // No result means the tables were just created + return created == null; + } + } + } +} diff --git a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs b/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs new file mode 100644 index 00000000..5be84182 --- /dev/null +++ b/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs @@ -0,0 +1,74 @@ +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using Dapper; +using Npgsql; + +namespace Dommel.IntegrationTests +{ + public class PostgresDatabase : Database + { + public override DbConnection GetConnection(string databaseName) + { + var connectionString = IsAppVeyor + ? $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=Password12!;" + : $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=root;"; + + return new NpgsqlConnection(connectionString); + } + + public override string TempDbDatabaseName => "postgres"; + + private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); + + public override async Task CreateDatabase() + { + DommelMapper.EscapeCharacterStart = DommelMapper.EscapeCharacterEnd = '"'; + using (var con = GetConnection(TempDbDatabaseName)) + { + try + { + // Always try to create the database as you'll run into + // race conditions when tests run in parallel. + await con.ExecuteAsync($"CREATE DATABASE {DefaultDatabaseName}"); + } + catch (PostgresException pex) when (pex.SqlState == "42P04") + { + // Ignore errors that the database already exists + } + } + } + + public override async Task CreateTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + var sql = @" +SELECT * FROM information_schema.tables where table_name = 'Products' LIMIT 1; +CREATE TABLE IF NOT EXISTS ""Categories"" (""Id"" serial primary key, ""Name"" VARCHAR(255)); +CREATE TABLE IF NOT EXISTS ""Products"" (""Id"" serial primary key, ""CategoryId"" int, ""Name"" VARCHAR(255)); +CREATE TABLE IF NOT EXISTS ""Orders"" (""Id"" serial primary key, ""Created"" TIMESTAMP NOT NULL); +CREATE TABLE IF NOT EXISTS ""OrderLines"" (""Id"" serial primary key, ""OrderId"" int, ""Line"" VARCHAR(255));"; + var created = await con.ExecuteScalarAsync(sql); + + // No result means the tables were just created + return created == null; + } + } + + public override async Task DropTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + await con.ExecuteAsync(@" +DROP TABLE ""Categories""; +DROP TABLE ""Products""; +DROP TABLE ""Orders""; +DROP TABLE ""OrderLines"";"); + con.Close(); + } + + DommelMapper.EscapeCharacterStart = DommelMapper.EscapeCharacterEnd = default; + } + } +} diff --git a/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs b/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs new file mode 100644 index 00000000..aad699a0 --- /dev/null +++ b/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs @@ -0,0 +1,46 @@ +using System.Data.Common; +using System.Data.SqlClient; +using System.Threading.Tasks; +using Dapper; + +namespace Dommel.IntegrationTests +{ + public class SqlServerDatabase : Database + { + public override DbConnection GetConnection(string databaseName) + { + var connectionString = IsAppVeyor + ? $"Server=(local)\\SQL2016;Database={databaseName};User ID=sa;Password=Password12!" + : $"Server=(LocalDb)\\mssqllocaldb;Database={databaseName};Integrated Security=True"; + + return new SqlConnection(connectionString); + } + + public override async Task CreateDatabase() + { + using (var con = GetConnection(TempDbDatabaseName)) + { + await con.ExecuteAsync($"IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = N'{DefaultDatabaseName}') BEGIN CREATE DATABASE {DefaultDatabaseName}; END;"); + } + } + + public override async Task CreateTables() + { + using (var con = GetConnection(DefaultDatabaseName)) + { + var sql = @"IF OBJECT_ID(N'dbo.Products', N'U') IS NULL +BEGIN + CREATE TABLE dbo.Categories (Id int IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255)); + CREATE TABLE dbo.Products (Id int IDENTITY(1,1) PRIMARY KEY, CategoryId int, Name VARCHAR(255)); + CREATE TABLE dbo.Orders (Id int IDENTITY(1,1) PRIMARY KEY, Created DATETIME NOT NULL); + CREATE TABLE dbo.OrderLines (Id int IDENTITY(1,1) PRIMARY KEY, OrderId int, Line VARCHAR(255)); + SELECT 1; +END"; + var created = await con.ExecuteScalarAsync(sql); + + // A result means the tables were just created + return created != null; + } + } + } +} diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index 21a3ba84..9cc59604 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -6,6 +6,7 @@ + diff --git a/test/Dommel.IntegrationTests/DommelTestBase.cs b/test/Dommel.IntegrationTests/DommelTestBase.cs index c8a8f9a9..00f22344 100644 --- a/test/Dommel.IntegrationTests/DommelTestBase.cs +++ b/test/Dommel.IntegrationTests/DommelTestBase.cs @@ -4,11 +4,10 @@ using System.Threading.Tasks; using Xunit; -[assembly: CollectionBehavior(DisableTestParallelization = true)] +[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] namespace Dommel.IntegrationTests { - [Collection("IntegrationTests")] public abstract class DommelTestBase : IAsyncLifetime { public DommelTestBase(Database database) From b5ef3b1ff428cd05777c96052842c2ba11a13d3f Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Wed, 14 Nov 2018 22:51:44 +0100 Subject: [PATCH 10/18] Add QuoteIdentifier to SQL builder --- src/Dommel/DommelMapper.Count.cs | 10 ++-- src/Dommel/DommelMapper.Delete.cs | 28 +++++----- src/Dommel/DommelMapper.Get.cs | 34 ++++++------ src/Dommel/DommelMapper.Insert.cs | 4 +- src/Dommel/DommelMapper.MultiMap.cs | 20 +++---- src/Dommel/DommelMapper.MySqlSqlBuilder.cs | 16 ++---- src/Dommel/DommelMapper.PostgresSqlBuilder.cs | 7 ++- src/Dommel/DommelMapper.Resolvers.cs | 38 ++++++++----- src/Dommel/DommelMapper.Select.cs | 18 +++---- src/Dommel/DommelMapper.SqlBuilders.cs | 14 +++-- src/Dommel/DommelMapper.SqlExpression.cs | 19 ++++++- .../DommelMapper.SqlServerCeSqlBuilder.cs | 9 ++-- .../DommelMapper.SqlServerSqlBuilder.cs | 9 ++-- src/Dommel/DommelMapper.SqliteSqlBuilder.cs | 3 ++ src/Dommel/DommelMapper.Update.cs | 14 +++-- src/Dommel/DommelMapper.cs | 3 +- .../Databases/PostgresDatabase.cs | 3 -- test/Dommel.Tests/LikeTests.cs | 54 +++++++++---------- test/Dommel.Tests/LoggingTests.cs | 6 +-- 19 files changed, 171 insertions(+), 138 deletions(-) diff --git a/src/Dommel/DommelMapper.Count.cs b/src/Dommel/DommelMapper.Count.cs index e7580ccf..62dd936d 100644 --- a/src/Dommel/DommelMapper.Count.cs +++ b/src/Dommel/DommelMapper.Count.cs @@ -21,7 +21,7 @@ public static partial class DommelMapper /// The number of entities matching the specified predicate. public static long Count(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildCountSql(predicate, out var parameters); + var sql = BuildCountSql(connection, predicate, out var parameters); LogQuery(sql); return connection.ExecuteScalar(sql, parameters, transaction); } @@ -36,22 +36,22 @@ public static long Count(this IDbConnection connection, ExpressionThe number of entities matching the specified predicate. public static Task CountAsync(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildCountSql(predicate, out var parameters); + var sql = BuildCountSql(connection, predicate, out var parameters); LogQuery(sql); return connection.ExecuteScalarAsync(sql, parameters, transaction); } - private static string BuildCountSql(Expression> predicate, out DynamicParameters parameters) + private static string BuildCountSql(IDbConnection connection, Expression> predicate, out DynamicParameters parameters) { var type = typeof(TEntity); if (!_countQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); sql = $"select count(*) from {tableName}"; _countQueryCache.TryAdd(type, sql); } - sql += new SqlExpression() + sql += new SqlExpression(GetSqlBuilder(connection)) .Where(predicate) .ToSql(out parameters); return sql; diff --git a/src/Dommel/DommelMapper.Delete.cs b/src/Dommel/DommelMapper.Delete.cs index 44c8190a..2817107e 100644 --- a/src/Dommel/DommelMapper.Delete.cs +++ b/src/Dommel/DommelMapper.Delete.cs @@ -23,7 +23,7 @@ public static partial class DommelMapper /// A value indicating whether the delete operation succeeded. public static bool Delete(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null) { - var sql = BuildDeleteQuery(typeof(TEntity)); + var sql = BuildDeleteQuery(connection, typeof(TEntity)); LogQuery(sql); return connection.Execute(sql, entity, transaction) > 0; } @@ -39,18 +39,18 @@ public static bool Delete(this IDbConnection connection, TEntity entity /// A value indicating whether the delete operation succeeded. public static async Task DeleteAsync(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null) { - var sql = BuildDeleteQuery(typeof(TEntity)); + var sql = BuildDeleteQuery(connection, typeof(TEntity)); LogQuery(sql); return await connection.ExecuteAsync(sql, entity, transaction) > 0; } - private static string BuildDeleteQuery(Type type) + private static string BuildDeleteQuery(IDbConnection connection,Type type) { if (!_deleteQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type); - var keyColumnName = Resolvers.Column(keyProperty); + var keyColumnName = Resolvers.Column(keyProperty, connection); sql = $"delete from {tableName} where {keyColumnName} = @{keyProperty.Name}"; @@ -71,7 +71,7 @@ private static string BuildDeleteQuery(Type type) /// A value indicating whether the delete operation succeeded. public static bool DeleteMultiple(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildDeleteMultipleQuery(predicate, out var parameters); + var sql = BuildDeleteMultipleQuery(connection, predicate, out var parameters); LogQuery(sql); return connection.Execute(sql, parameters, transaction) > 0; } @@ -87,22 +87,22 @@ public static bool DeleteMultiple(this IDbConnection connection, Expres /// A value indicating whether the delete operation succeeded. public static async Task DeleteMultipleAsync(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildDeleteMultipleQuery(predicate, out var parameters); + var sql = BuildDeleteMultipleQuery(connection, predicate, out var parameters); LogQuery(sql); return await connection.ExecuteAsync(sql, parameters, transaction) > 0; } - private static string BuildDeleteMultipleQuery(Expression> predicate, out DynamicParameters parameters) + private static string BuildDeleteMultipleQuery(IDbConnection connection, Expression> predicate, out DynamicParameters parameters) { var type = typeof(TEntity); if (!_deleteAllQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); sql = $"delete from {tableName}"; _deleteAllQueryCache.TryAdd(type, sql); } - sql += new SqlExpression() + sql += new SqlExpression(GetSqlBuilder(connection)) .Where(predicate) .ToSql(out parameters); return sql; @@ -118,7 +118,7 @@ private static string BuildDeleteMultipleQuery(ExpressionA value indicating whether the delete operation succeeded. public static bool DeleteAll(this IDbConnection connection, IDbTransaction transaction = null) { - var sql = BuildDeleteAllQuery(typeof(TEntity)); + var sql = BuildDeleteAllQuery(connection, typeof(TEntity)); LogQuery(sql); return connection.Execute(sql, transaction: transaction) > 0; } @@ -133,16 +133,16 @@ public static bool DeleteAll(this IDbConnection connection, IDbTransact /// A value indicating whether the delete operation succeeded. public static async Task DeleteAllAsync(this IDbConnection connection, IDbTransaction transaction = null) { - var sql = BuildDeleteAllQuery(typeof(TEntity)); + var sql = BuildDeleteAllQuery(connection, typeof(TEntity)); LogQuery(sql); return await connection.ExecuteAsync(sql, transaction: transaction) > 0; } - private static string BuildDeleteAllQuery(Type type) + private static string BuildDeleteAllQuery(IDbConnection connection, Type type) { if (!_deleteAllQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); sql = $"delete from {tableName}"; _deleteAllQueryCache.TryAdd(type, sql); } diff --git a/src/Dommel/DommelMapper.Get.cs b/src/Dommel/DommelMapper.Get.cs index dba6e65f..93b8a355 100644 --- a/src/Dommel/DommelMapper.Get.cs +++ b/src/Dommel/DommelMapper.Get.cs @@ -25,7 +25,7 @@ public static partial class DommelMapper /// The entity with the corresponding id. public static TEntity Get(this IDbConnection connection, object id, IDbTransaction transaction = null) where TEntity : class { - var sql = BuildGetById(typeof(TEntity), id, out var parameters); + var sql = BuildGetById(connection, typeof(TEntity), id, out var parameters); LogQuery(sql); return connection.QueryFirstOrDefault(sql, parameters, transaction); } @@ -40,18 +40,18 @@ public static TEntity Get(this IDbConnection connection, object id, IDb /// The entity with the corresponding id. public static Task GetAsync(this IDbConnection connection, object id, IDbTransaction transaction = null) where TEntity : class { - var sql = BuildGetById(typeof(TEntity), id, out var parameters); + var sql = BuildGetById(connection, typeof(TEntity), id, out var parameters); LogQuery(sql); return connection.QueryFirstOrDefaultAsync(sql, parameters, transaction); } - private static string BuildGetById(Type type, object id, out DynamicParameters parameters) + private static string BuildGetById(IDbConnection connection, Type type, object id, out DynamicParameters parameters) { if (!_getQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type); - var keyColumnName = Resolvers.Column(keyProperty); + var keyColumnName = Resolvers.Column(keyProperty, connection); sql = $"select * from {tableName} where {keyColumnName} = @Id"; _getQueryCache.TryAdd(type, sql); @@ -88,7 +88,7 @@ public static TEntity Get(this IDbConnection connection, object[] ids, return Get(connection, ids[0], transaction); } - var sql = BuildGetByIds(typeof(TEntity), ids, out var parameters); + var sql = BuildGetByIds(connection, typeof(TEntity), ids, out var parameters); LogQuery(sql); return connection.QueryFirstOrDefault(sql, parameters); } @@ -118,18 +118,18 @@ public static Task GetAsync(this IDbConnection connection, obj return GetAsync(connection, ids[0], transaction); } - var sql = BuildGetByIds(typeof(TEntity), ids, out var parameters); + var sql = BuildGetByIds(connection, typeof(TEntity), ids, out var parameters); LogQuery(sql); return connection.QueryFirstOrDefaultAsync(sql, parameters); } - private static string BuildGetByIds(Type type, object[] ids, out DynamicParameters parameters) + private static string BuildGetByIds(IDbConnection connection, Type type, object[] ids, out DynamicParameters parameters) { if (!_getByIdsQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); var keyProperties = Resolvers.KeyProperties(type); - var keyColumnsNames = keyProperties.Select(Resolvers.Column).ToArray(); + var keyColumnsNames = keyProperties.Select(p => Resolvers.Column(p, connection)).ToArray(); if (keyColumnsNames.Length != ids.Length) { throw new InvalidOperationException($"Number of key columns ({keyColumnsNames.Length}) of type {type.Name} does not match with the number of specified IDs ({ids.Length})."); @@ -174,7 +174,7 @@ private static string BuildGetByIds(Type type, object[] ids, out DynamicParamete /// A collection of entities of type . public static IEnumerable GetAll(this IDbConnection connection, IDbTransaction transaction = null, bool buffered = true) where TEntity : class { - var sql = BuildGetAllQuery(typeof(TEntity)); + var sql = BuildGetAllQuery(connection, typeof(TEntity)); LogQuery(sql); return connection.Query(sql, transaction: transaction, buffered: buffered); } @@ -188,16 +188,16 @@ public static IEnumerable GetAll(this IDbConnection connection /// A collection of entities of type . public static Task> GetAllAsync(this IDbConnection connection, IDbTransaction transaction = null) where TEntity : class { - var sql = BuildGetAllQuery(typeof(TEntity)); + var sql = BuildGetAllQuery(connection, typeof(TEntity)); LogQuery(sql); return connection.QueryAsync(sql, transaction: transaction); } - private static string BuildGetAllQuery(Type type) + private static string BuildGetAllQuery(IDbConnection connection, Type type) { if (!_getAllQueryCache.TryGetValue(type, out var sql)) { - sql = "select * from " + Resolvers.Table(type); ; + sql = "select * from " + Resolvers.Table(type, connection); _getAllQueryCache.TryAdd(type, sql); } @@ -243,10 +243,10 @@ public static Task> GetPagedAsync(this IDbConnecti private static string BuildPagedQuery(IDbConnection connection, Type type, int pageNumber, int pageSize) { // Start with the select query part. - var sql = BuildGetAllQuery(type); + var sql = BuildGetAllQuery(connection, type); - // Append the paging part including the order by. - var orderBy = "order by " + Resolvers.Column(Resolvers.KeyProperty(type)); + // Append the paging part including the order by. + var orderBy = "order by " + Resolvers.Column(Resolvers.KeyProperty(type), connection); sql += GetSqlBuilder(connection).BuildPaging(orderBy, pageNumber, pageSize); return sql; } diff --git a/src/Dommel/DommelMapper.Insert.cs b/src/Dommel/DommelMapper.Insert.cs index f8297692..3f2f78aa 100644 --- a/src/Dommel/DommelMapper.Insert.cs +++ b/src/Dommel/DommelMapper.Insert.cs @@ -78,7 +78,7 @@ private static string BuildInsertQuery(IDbConnection connection, Type type) var cacheKey = $"{connection.GetType().Name}.{type.Name}"; if (!_insertQueryCache.TryGetValue(cacheKey, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type, out var isIdentity); var typeProperties = new List(); @@ -99,7 +99,7 @@ private static string BuildInsertQuery(IDbConnection connection, Type type) } } - var columnNames = typeProperties.Select(Resolvers.Column).ToArray(); + var columnNames = typeProperties.Select(p => Resolvers.Column(p, connection)).ToArray(); var paramNames = typeProperties.Select(p => "@" + p.Name).ToArray(); var builder = GetSqlBuilder(connection); diff --git a/src/Dommel/DommelMapper.MultiMap.cs b/src/Dommel/DommelMapper.MultiMap.cs index 431394ca..da5cf202 100644 --- a/src/Dommel/DommelMapper.MultiMap.cs +++ b/src/Dommel/DommelMapper.MultiMap.cs @@ -661,7 +661,7 @@ private static IEnumerable MultiMap t != typeof(DontMap)) .ToArray(); - var sql = BuildMultiMapQuery(resultType, includeTypes, id, out var parameters); + var sql = BuildMultiMapQuery(connection, resultType, includeTypes, id, out var parameters); LogQuery(sql); switch (includeTypes.Length) @@ -699,7 +699,7 @@ private static Task> MultiMapAsync t != typeof(DontMap)) .ToArray(); - var sql = BuildMultiMapQuery(resultType, includeTypes, id, out var parameters); + var sql = BuildMultiMapQuery(connection, resultType, includeTypes, id, out var parameters); LogQuery(sql); switch (includeTypes.Length) @@ -721,36 +721,36 @@ private static Task> MultiMapAsync - public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty) - { - if (EscapeCharacterStart == char.MinValue && EscapeCharacterEnd == char.MinValue) - { - // Fall back to the default behavior. - return $"insert into `{tableName}` (`{string.Join("`, `", columnNames)}`) values ({string.Join(", ", paramNames)}); select LAST_INSERT_ID() id"; - } - - // Table and column names are already escaped. - return $"insert into {tableName} ({string.Join(", ", columnNames)}) values ({string.Join(", ", paramNames)}); select LAST_INSERT_ID() id"; - } + public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty) => + $"insert into {tableName} ({string.Join(", ", columnNames)}) values ({string.Join(", ", paramNames)}); select LAST_INSERT_ID() id"; /// public string BuildPaging(string orderBy, int pageNumber, int pageSize) @@ -28,6 +19,9 @@ public string BuildPaging(string orderBy, int pageNumber, int pageSize) var start = pageNumber >= 1 ? (pageNumber - 1) * pageSize : 0; return $" {orderBy} LIMIT {start}, {pageSize}"; } + + /// + public string QuoteIdentifier(string identifier) => $"`{identifier}`"; } } } diff --git a/src/Dommel/DommelMapper.PostgresSqlBuilder.cs b/src/Dommel/DommelMapper.PostgresSqlBuilder.cs index aebf02f0..635cee80 100644 --- a/src/Dommel/DommelMapper.PostgresSqlBuilder.cs +++ b/src/Dommel/DommelMapper.PostgresSqlBuilder.cs @@ -17,8 +17,8 @@ public string BuildInsert(string tableName, string[] columnNames, string[] param if (keyProperty != null) { - var keyColumnName = Resolvers.Column(keyProperty); - + // We know it's Postgres here + var keyColumnName = Resolvers.Column(keyProperty, new PostgresSqlBuilder()); sql += " RETURNING " + keyColumnName; } else @@ -36,6 +36,9 @@ public string BuildPaging(string orderBy, int pageNumber, int pageSize) var start = pageNumber >= 1 ? (pageNumber - 1) * pageSize : 0; return $" {orderBy} OFFSET {start} LIMIT {pageSize}"; } + + /// + public string QuoteIdentifier(string identifier) => $"\"{identifier}\""; } } } diff --git a/src/Dommel/DommelMapper.Resolvers.cs b/src/Dommel/DommelMapper.Resolvers.cs index 0351341a..991c0627 100644 --- a/src/Dommel/DommelMapper.Resolvers.cs +++ b/src/Dommel/DommelMapper.Resolvers.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Reflection; @@ -155,22 +156,28 @@ public static IEnumerable Properties(Type type) return properties; } + /// + /// Gets the name of the table in the database for the specified type, + /// using the configured . + /// + /// The to get the table name for. + /// The database connection instance. + /// The table name in the database for . + public static string Table(Type type, IDbConnection connection) => + Table(type, GetSqlBuilder(connection)); /// /// Gets the name of the table in the database for the specified type, /// using the configured . /// /// The to get the table name for. + /// The SQL builder instance. /// The table name in the database for . - public static string Table(Type type) + public static string Table(Type type, ISqlBuilder sqlBuilder) { if (!_typeTableNameCache.TryGetValue(type, out var name)) { - name = _tableNameResolver.ResolveTableName(type); - if (EscapeCharacterStart != char.MinValue || EscapeCharacterEnd != char.MinValue) - { - name = EscapeCharacterStart + name + EscapeCharacterEnd; - } + name = sqlBuilder.QuoteIdentifier(_tableNameResolver.ResolveTableName(type)); _typeTableNameCache.TryAdd(type, name); } @@ -183,17 +190,24 @@ public static string Table(Type type) /// using the configured . /// /// The to get the column name for. + /// The database connection instance. + /// The column name in the database for . + public static string Column(PropertyInfo propertyInfo, IDbConnection connection) + => Column(propertyInfo, GetSqlBuilder(connection)); + + /// + /// Gets the name of the column in the database for the specified type, + /// using the configured . + /// + /// The to get the column name for. + /// The SQL builder instance. /// The column name in the database for . - public static string Column(PropertyInfo propertyInfo) + public static string Column(PropertyInfo propertyInfo, ISqlBuilder sqlBuilder) { var key = $"{propertyInfo.DeclaringType}.{propertyInfo.Name}"; if (!_columnNameCache.TryGetValue(key, out var columnName)) { - columnName = _columnNameResolver.ResolveColumnName(propertyInfo); - if (EscapeCharacterStart != char.MinValue || EscapeCharacterEnd != char.MinValue) - { - columnName = EscapeCharacterStart + columnName + EscapeCharacterEnd; - } + columnName = sqlBuilder.QuoteIdentifier(_columnNameResolver.ResolveColumnName(propertyInfo)); _columnNameCache.TryAdd(key, columnName); } diff --git a/src/Dommel/DommelMapper.Select.cs b/src/Dommel/DommelMapper.Select.cs index a650a28e..b53a3ad2 100644 --- a/src/Dommel/DommelMapper.Select.cs +++ b/src/Dommel/DommelMapper.Select.cs @@ -26,7 +26,7 @@ public static partial class DommelMapper /// public static IEnumerable Select(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null, bool buffered = true) { - var sql = BuildSelectSql(predicate, out var parameters); + var sql = BuildSelectSql(connection, predicate, out var parameters); LogQuery(sql); return connection.Query(sql, parameters, transaction, buffered); } @@ -44,7 +44,7 @@ public static IEnumerable Select(this IDbConnection connection /// public static Task> SelectAsync(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildSelectSql(predicate, out var parameters); + var sql = BuildSelectSql(connection, predicate, out var parameters); LogQuery(sql); return connection.QueryAsync(sql, parameters, transaction); } @@ -62,7 +62,7 @@ public static Task> SelectAsync(this IDbConnection /// public static TEntity FirstOrDefault(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildSelectSql(predicate, out var parameters); + var sql = BuildSelectSql(connection, predicate, out var parameters); LogQuery(sql); return connection.QueryFirstOrDefault(sql, parameters, transaction); } @@ -80,22 +80,22 @@ public static TEntity FirstOrDefault(this IDbConnection connection, Exp /// public static Task FirstOrDefaultAsync(this IDbConnection connection, Expression> predicate, IDbTransaction transaction = null) { - var sql = BuildSelectSql(predicate, out var parameters); + var sql = BuildSelectSql(connection, predicate, out var parameters); LogQuery(sql); return connection.QueryFirstOrDefaultAsync(sql, parameters, transaction); } - private static string BuildSelectSql(Expression> predicate, out DynamicParameters parameters) + private static string BuildSelectSql(IDbConnection connection, Expression> predicate, out DynamicParameters parameters) { var type = typeof(TEntity); if (!_getAllQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); sql = $"select * from {tableName}"; _getAllQueryCache.TryAdd(type, sql); } - sql += new SqlExpression() + sql += new SqlExpression(GetSqlBuilder(connection)) .Where(predicate) .ToSql(out parameters); return sql; @@ -148,10 +148,10 @@ public static Task> SelectPagedAsync(this IDbConne private static string BuildSelectPagedQuery(IDbConnection connection, Expression> predicate, int pageNumber, int pageSize, out DynamicParameters parameters) { // Start with the select query part. - var sql = BuildSelectSql(predicate, out parameters); + var sql = BuildSelectSql(connection, predicate, out parameters); // Append the paging part including the order by. - var orderBy = "order by " + Resolvers.Column(Resolvers.KeyProperty(typeof(TEntity))); + var orderBy = "order by " + Resolvers.Column(Resolvers.KeyProperty(typeof(TEntity)), connection); sql += GetSqlBuilder(connection).BuildPaging(orderBy, pageNumber, pageSize); return sql; } diff --git a/src/Dommel/DommelMapper.SqlBuilders.cs b/src/Dommel/DommelMapper.SqlBuilders.cs index 5a029d4d..ef3698c8 100644 --- a/src/Dommel/DommelMapper.SqlBuilders.cs +++ b/src/Dommel/DommelMapper.SqlBuilders.cs @@ -25,10 +25,7 @@ public static partial class DommelMapper /// Example: typeof(SqlConnection). /// /// An implementation of the interface. - public static void AddSqlBuilder(Type connectionType, ISqlBuilder builder) - { - _sqlBuilders[connectionType.Name.ToLower()] = builder; - } + public static void AddSqlBuilder(Type connectionType, ISqlBuilder builder) => _sqlBuilders[connectionType.Name.ToLower()] = builder; /// /// Gets the configured for the specified instance. @@ -38,7 +35,7 @@ public static void AddSqlBuilder(Type connectionType, ISqlBuilder builder) public static ISqlBuilder GetSqlBuilder(IDbConnection connection) { var connectionTypeName = connection.GetType().Name; - var builder =_sqlBuilders.TryGetValue(connectionTypeName.ToLower(), out var b) ? b : new SqlServerSqlBuilder(); + var builder = _sqlBuilders.TryGetValue(connectionTypeName.ToLower(), out var b) ? b : new SqlServerSqlBuilder(); LogReceived?.Invoke($"Selected SQL Builder '{builder.GetType().Name}' for connection type '{connectionTypeName}'"); return builder; } @@ -70,6 +67,13 @@ public interface ISqlBuilder /// The page size. /// The paging part of a query. string BuildPaging(string orderBy, int pageNumber, int pageSize); + + /// + /// Adds quotes around (or at the start) of an identifier such as a table or column name. + /// + /// The identifier add quotes around. E.g. a table or column name. + /// The quoted . + string QuoteIdentifier(string identifier); } } } diff --git a/src/Dommel/DommelMapper.SqlExpression.cs b/src/Dommel/DommelMapper.SqlExpression.cs index 6299581f..a060ca35 100644 --- a/src/Dommel/DommelMapper.SqlExpression.cs +++ b/src/Dommel/DommelMapper.SqlExpression.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -19,6 +20,21 @@ public class SqlExpression private readonly DynamicParameters _parameters = new DynamicParameters(); private int _parameterIndex; + /// + /// Initializes a new instance of the + /// class using the specified . + /// + /// The instance. + public SqlExpression(ISqlBuilder sqlBuilder) + { + SqlBuilder = sqlBuilder; + } + + /// + /// Gets the instance used to build queries with. + /// + public ISqlBuilder SqlBuilder { get; } + /// /// Builds a SQL expression for the specified filter expression. /// @@ -317,7 +333,8 @@ protected virtual object VisitMemberAccess(MemberExpression expression) /// /// The member expression. /// The result of the processing. - protected virtual string MemberToColumn(MemberExpression expression) => Resolvers.Column((PropertyInfo)expression.Member); + protected virtual string MemberToColumn(MemberExpression expression) => + Resolvers.Column((PropertyInfo)expression.Member, SqlBuilder); /// /// Returns the expression operand for the specified expression type. diff --git a/src/Dommel/DommelMapper.SqlServerCeSqlBuilder.cs b/src/Dommel/DommelMapper.SqlServerCeSqlBuilder.cs index 88e7945f..78e8d52a 100644 --- a/src/Dommel/DommelMapper.SqlServerCeSqlBuilder.cs +++ b/src/Dommel/DommelMapper.SqlServerCeSqlBuilder.cs @@ -10,10 +10,8 @@ public static partial class DommelMapper public sealed class SqlServerCeSqlBuilder : ISqlBuilder { /// - public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty) - { - return $"insert into {tableName} ({string.Join(", ", columnNames)}) values ({string.Join(", ", paramNames)}) select cast(@@IDENTITY as int)"; - } + public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty) => + $"insert into {tableName} ({string.Join(", ", columnNames)}) values ({string.Join(", ", paramNames)}) select cast(@@IDENTITY as int)"; /// public string BuildPaging(string orderBy, int pageNumber, int pageSize) @@ -21,6 +19,9 @@ public string BuildPaging(string orderBy, int pageNumber, int pageSize) var start = pageNumber >= 1 ? (pageNumber - 1) * pageSize : 0; return $" {orderBy} offset {start} rows fetch next {pageSize} rows only"; } + + /// + public string QuoteIdentifier(string identifier) => identifier; } } } diff --git a/src/Dommel/DommelMapper.SqlServerSqlBuilder.cs b/src/Dommel/DommelMapper.SqlServerSqlBuilder.cs index 7586e908..663e23ef 100644 --- a/src/Dommel/DommelMapper.SqlServerSqlBuilder.cs +++ b/src/Dommel/DommelMapper.SqlServerSqlBuilder.cs @@ -10,10 +10,8 @@ public static partial class DommelMapper public sealed class SqlServerSqlBuilder : ISqlBuilder { /// - public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty) - { - return $"set nocount on insert into {tableName} ({string.Join(", ", columnNames)}) values ({string.Join(", ", paramNames)}) select cast(scope_identity() as int)"; - } + public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty) => + $"set nocount on insert into {tableName} ({string.Join(", ", columnNames)}) values ({string.Join(", ", paramNames)}) select cast(scope_identity() as int)"; /// public string BuildPaging(string orderBy, int pageNumber, int pageSize) @@ -21,6 +19,9 @@ public string BuildPaging(string orderBy, int pageNumber, int pageSize) var start = pageNumber >= 1 ? (pageNumber - 1) * pageSize : 0; return $" {orderBy} offset {start} rows fetch next {pageSize} rows only"; } + + /// + public string QuoteIdentifier(string identifier) => identifier; } } } diff --git a/src/Dommel/DommelMapper.SqliteSqlBuilder.cs b/src/Dommel/DommelMapper.SqliteSqlBuilder.cs index 532552ce..d595f21a 100644 --- a/src/Dommel/DommelMapper.SqliteSqlBuilder.cs +++ b/src/Dommel/DommelMapper.SqliteSqlBuilder.cs @@ -21,6 +21,9 @@ public string BuildPaging(string orderBy, int pageNumber, int pageSize) var start = pageNumber >= 1 ? (pageNumber - 1) * pageSize : 0; return $" {orderBy} LIMIT {start}, {pageSize}"; } + + /// + public string QuoteIdentifier(string identifier) => identifier; } } } diff --git a/src/Dommel/DommelMapper.Update.cs b/src/Dommel/DommelMapper.Update.cs index a4d3999e..3b4848c6 100644 --- a/src/Dommel/DommelMapper.Update.cs +++ b/src/Dommel/DommelMapper.Update.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Data; using System.Linq; -using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using Dapper; @@ -25,7 +23,7 @@ public static partial class DommelMapper /// A value indicating whether the update operation succeeded. public static bool Update(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null) { - var sql = BuildUpdateQuery(typeof(TEntity)); + var sql = BuildUpdateQuery(connection, typeof(TEntity)); LogQuery(sql); return connection.Execute(sql, entity, transaction) > 0; } @@ -41,16 +39,16 @@ public static bool Update(this IDbConnection connection, TEntity entity /// A value indicating whether the update operation succeeded. public static async Task UpdateAsync(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null) { - var sql = BuildUpdateQuery(typeof(TEntity)); + var sql = BuildUpdateQuery(connection, typeof(TEntity)); LogQuery(sql); return await connection.ExecuteAsync(sql, entity, transaction) > 0; } - private static string BuildUpdateQuery(Type type) + private static string BuildUpdateQuery(IDbConnection connection, Type type) { if (!_updateQueryCache.TryGetValue(type, out var sql)) { - var tableName = Resolvers.Table(type); + var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type); // Use all properties which are settable. @@ -59,9 +57,9 @@ private static string BuildUpdateQuery(Type type) .Where(p => p.GetSetMethod() != null) .ToArray(); - var columnNames = typeProperties.Select(p => $"{Resolvers.Column(p)} = @{p.Name}").ToArray(); + var columnNames = typeProperties.Select(p => $"{Resolvers.Column(p, connection)} = @{p.Name}").ToArray(); - sql = $"update {tableName} set {string.Join(", ", columnNames)} where {Resolvers.Column(keyProperty)} = @{keyProperty.Name}"; + sql = $"update {tableName} set {string.Join(", ", columnNames)} where {Resolvers.Column(keyProperty, connection)} = @{keyProperty.Name}"; _updateQueryCache.TryAdd(type, sql); } diff --git a/src/Dommel/DommelMapper.cs b/src/Dommel/DommelMapper.cs index 78eb2848..fa5ad2d7 100644 --- a/src/Dommel/DommelMapper.cs +++ b/src/Dommel/DommelMapper.cs @@ -11,13 +11,14 @@ public static partial class DommelMapper /// /// The escape character to use for escaping the start of column and table names in queries. /// + [Obsolete("Escape chars are obsolete and set by default in the ISqlBuilder. They will be removed in the 2.0 release.")] public static char EscapeCharacterStart; /// /// The escape character to use for escaping the end of column and table names in queries. /// + [Obsolete("Escape chars are obsolete and set by default in the ISqlBuilder. They will be removed in the 2.0 release.")] public static char EscapeCharacterEnd; - /// /// A callback which gets invoked when queries and other information are logged. /// diff --git a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs b/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs index 5be84182..7bcba3f8 100644 --- a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs @@ -23,7 +23,6 @@ public override DbConnection GetConnection(string databaseName) public override async Task CreateDatabase() { - DommelMapper.EscapeCharacterStart = DommelMapper.EscapeCharacterEnd = '"'; using (var con = GetConnection(TempDbDatabaseName)) { try @@ -67,8 +66,6 @@ await con.ExecuteAsync(@" DROP TABLE ""OrderLines"";"); con.Close(); } - - DommelMapper.EscapeCharacterStart = DommelMapper.EscapeCharacterEnd = default; } } } diff --git a/test/Dommel.Tests/LikeTests.cs b/test/Dommel.Tests/LikeTests.cs index cbc9d8b6..dc977ab5 100644 --- a/test/Dommel.Tests/LikeTests.cs +++ b/test/Dommel.Tests/LikeTests.cs @@ -1,35 +1,25 @@ -using Dapper; -using Moq; -using Moq.Dapper; -using System; -using System.Collections.Generic; +using System; using System.ComponentModel.DataAnnotations.Schema; -using System.Data; -using System.Linq; -using System.Text; +using Moq; using Xunit; +using static Dommel.DommelMapper; namespace Dommel.Tests { public class LikeTests { - private readonly DommelMapper.SqlExpression sqlExpression = new DommelMapper.SqlExpression(); - private readonly List logs = new List(); - private readonly Mock mock = new Mock(); - - public LikeTests() - { - mock.SetupDapper(x => x.QueryFirstOrDefault(It.IsAny(), It.IsAny(), null, null, null)) - .Returns(new Foo()); - } - [Fact] public void LikeOperandContains() { + // Arrange + var sqlExpression = new SqlExpression(new SqlServerSqlBuilder()); + + // Act var expression = sqlExpression.Where(p => p.Bar.Contains("test")); var sql = expression.ToSql(out var dynamicParameters); - Assert.Equal("where Bar like p1", sql.Trim()); + // Assert + Assert.Equal("where Bar like @p1", sql.Trim()); Assert.Single(dynamicParameters.ParameterNames); Assert.Equal("%test%", dynamicParameters.Get("p1")); } @@ -37,23 +27,33 @@ public void LikeOperandContains() [Fact] public void LikeOperandStartsWith() { - var expressao = sqlExpression.Where(p => p.Bar.StartsWith("teste")); - var sql = expressao.ToSql(out var dynamicParameters); + // Arrange + var sqlExpression = new SqlExpression(new SqlServerSqlBuilder()); + + // Act + var expression = sqlExpression.Where(p => p.Bar.StartsWith("test")); + var sql = expression.ToSql(out var dynamicParameters); - Assert.Equal("where Bar like p1", sql.Trim()); + // Assert + Assert.Equal("where Bar like @p1", sql.Trim()); Assert.Single(dynamicParameters.ParameterNames); - Assert.Equal("teste%", dynamicParameters.Get("p1")); + Assert.Equal("test%", dynamicParameters.Get("p1")); } [Fact] public void LikeOperandEndsWith() { - var expressao = sqlExpression.Where(p => p.Bar.EndsWith("teste")); - var sql = expressao.ToSql(out var dynamicParameters); + // Arrange + var sqlExpression = new SqlExpression(new SqlServerSqlBuilder()); + + // Act + var expression = sqlExpression.Where(p => p.Bar.EndsWith("test")); + var sql = expression.ToSql(out var dynamicParameters); - Assert.Equal("where Bar like p1", sql.Trim()); + // Assert + Assert.Equal("where Bar like @p1", sql.Trim()); Assert.Single(dynamicParameters.ParameterNames); - Assert.Equal("%teste", dynamicParameters.Get("p1")); + Assert.Equal("%test", dynamicParameters.Get("p1")); } [Table("tblFoo")] diff --git a/test/Dommel.Tests/LoggingTests.cs b/test/Dommel.Tests/LoggingTests.cs index b208ec95..f9d07bdc 100644 --- a/test/Dommel.Tests/LoggingTests.cs +++ b/test/Dommel.Tests/LoggingTests.cs @@ -30,7 +30,7 @@ public void GetLogsSql() Assert.Single(logs); Assert.Equal("Get: select * from Foos where Id = @Id", logs[0]); } - + [Fact] public void GetByIdsLogsSql() { @@ -87,8 +87,8 @@ public void GetBuilderLogsChosenBuilder() DommelMapper.GetSqlBuilder(mock.Object); // Assert - Assert.Single(logs); - Assert.Equal("Selected SQL Builder 'SqlServerSqlBuilder' for connection type 'IDbConnectionProxy'", logs[0]); + Assert.True(logs.Count > 0); + Assert.Contains("Selected SQL Builder 'SqlServerSqlBuilder' for connection type 'IDbConnectionProxy'", logs); } } From fce3b2b6cefb812bcfbfa6fa775ab466997fa0b4 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:16:57 +0100 Subject: [PATCH 11/18] Take database connection type into account when caching queries and table/column names --- src/Dommel/DommelMapper.Cache.cs | 54 ++++++++++++++++++++++++ src/Dommel/DommelMapper.Count.cs | 8 ++-- src/Dommel/DommelMapper.DapperTypeMap.cs | 1 + src/Dommel/DommelMapper.Delete.cs | 25 +++++------ src/Dommel/DommelMapper.Get.cs | 21 +++++---- src/Dommel/DommelMapper.Insert.cs | 9 ++-- src/Dommel/DommelMapper.Resolvers.cs | 9 ++-- src/Dommel/DommelMapper.Select.cs | 10 ++--- src/Dommel/DommelMapper.Update.cs | 9 ++-- src/Dommel/DommelMapper.cs | 17 -------- 10 files changed, 93 insertions(+), 70 deletions(-) create mode 100644 src/Dommel/DommelMapper.Cache.cs diff --git a/src/Dommel/DommelMapper.Cache.cs b/src/Dommel/DommelMapper.Cache.cs new file mode 100644 index 00000000..8773154a --- /dev/null +++ b/src/Dommel/DommelMapper.Cache.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Concurrent; +using System.Data; +using System.Reflection; + +namespace Dommel +{ + public static partial class DommelMapper + { + internal enum QueryCacheType + { + Get, + GetByMultipleIds, + GetAll, + Count, + Insert, + Update, + Delete, + DeleteAll, + } + +#pragma warning disable IDE1006 // Naming Styles + internal static ConcurrentDictionary QueryCache = new ConcurrentDictionary(); + internal static ConcurrentDictionary ResolverCache = new ConcurrentDictionary(); +#pragma warning restore IDE1006 // Naming Styles + + internal struct QueryCacheKey : IEquatable + { + public QueryCacheKey(QueryCacheType cacheType, IDbConnection connection, MemberInfo memberInfo) + { + ConnectionType = connection.GetType(); + CacheType = cacheType; + MemberInfo = memberInfo; + } + +#if NETSTANDARD1_3 + public QueryCacheKey(QueryCacheType cacheType, IDbConnection connection, Type type) + { + ConnectionType = connection.GetType(); + CacheType = cacheType; + MemberInfo = type.GetTypeInfo(); + } +#endif + + public QueryCacheType CacheType { get; } + + public Type ConnectionType { get; } + + public MemberInfo MemberInfo { get; } + + public bool Equals(QueryCacheKey other) => CacheType == other.CacheType && ConnectionType == other.ConnectionType && MemberInfo == other.MemberInfo; + } + } +} diff --git a/src/Dommel/DommelMapper.Count.cs b/src/Dommel/DommelMapper.Count.cs index 62dd936d..8a56889f 100644 --- a/src/Dommel/DommelMapper.Count.cs +++ b/src/Dommel/DommelMapper.Count.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Data; using System.Linq.Expressions; using System.Threading.Tasks; @@ -9,8 +8,6 @@ namespace Dommel { public static partial class DommelMapper { - private static readonly ConcurrentDictionary _countQueryCache = new ConcurrentDictionary(); - /// /// Returns the number of entities matching the specified predicate. /// @@ -44,11 +41,12 @@ public static Task CountAsync(this IDbConnection connection, Expr private static string BuildCountSql(IDbConnection connection, Expression> predicate, out DynamicParameters parameters) { var type = typeof(TEntity); - if (!_countQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.Count, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); sql = $"select count(*) from {tableName}"; - _countQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } sql += new SqlExpression(GetSqlBuilder(connection)) diff --git a/src/Dommel/DommelMapper.DapperTypeMap.cs b/src/Dommel/DommelMapper.DapperTypeMap.cs index 154e1d86..f4bc86f4 100644 --- a/src/Dommel/DommelMapper.DapperTypeMap.cs +++ b/src/Dommel/DommelMapper.DapperTypeMap.cs @@ -16,6 +16,7 @@ public static partial class DommelMapper static DommelMapper() { + // Type mapper for [Column] attribute SqlMapper.TypeMapProvider = type => CreateMap(type); SqlMapper.ITypeMap CreateMap(Type t) => new CustomPropertyTypeMap(t, diff --git a/src/Dommel/DommelMapper.Delete.cs b/src/Dommel/DommelMapper.Delete.cs index 2817107e..6cf68dab 100644 --- a/src/Dommel/DommelMapper.Delete.cs +++ b/src/Dommel/DommelMapper.Delete.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Data; using System.Linq.Expressions; using System.Threading.Tasks; @@ -9,9 +8,6 @@ namespace Dommel { public static partial class DommelMapper { - private static readonly ConcurrentDictionary _deleteQueryCache = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary _deleteAllQueryCache = new ConcurrentDictionary(); - /// /// Deletes the specified entity from the database. /// Returns a value indicating whether the operation succeeded. @@ -44,9 +40,10 @@ public static async Task DeleteAsync(this IDbConnection connectio return await connection.ExecuteAsync(sql, entity, transaction) > 0; } - private static string BuildDeleteQuery(IDbConnection connection,Type type) + private static string BuildDeleteQuery(IDbConnection connection, Type type) { - if (!_deleteQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.Delete, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type); @@ -54,7 +51,7 @@ private static string BuildDeleteQuery(IDbConnection connection,Type type) sql = $"delete from {tableName} where {keyColumnName} = @{keyProperty.Name}"; - _deleteQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } return sql; @@ -94,14 +91,11 @@ public static async Task DeleteMultipleAsync(this IDbConnection c private static string BuildDeleteMultipleQuery(IDbConnection connection, Expression> predicate, out DynamicParameters parameters) { + // Build the delete all query var type = typeof(TEntity); - if (!_deleteAllQueryCache.TryGetValue(type, out var sql)) - { - var tableName = Resolvers.Table(type, connection); - sql = $"delete from {tableName}"; - _deleteAllQueryCache.TryAdd(type, sql); - } + var sql = BuildDeleteAllQuery(connection, type); + // Append the where statement sql += new SqlExpression(GetSqlBuilder(connection)) .Where(predicate) .ToSql(out parameters); @@ -140,11 +134,12 @@ public static async Task DeleteAllAsync(this IDbConnection connec private static string BuildDeleteAllQuery(IDbConnection connection, Type type) { - if (!_deleteAllQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.DeleteAll, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); sql = $"delete from {tableName}"; - _deleteAllQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } return sql; diff --git a/src/Dommel/DommelMapper.Get.cs b/src/Dommel/DommelMapper.Get.cs index 93b8a355..60e9f952 100644 --- a/src/Dommel/DommelMapper.Get.cs +++ b/src/Dommel/DommelMapper.Get.cs @@ -1,8 +1,8 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using Dapper; @@ -11,10 +11,6 @@ namespace Dommel { public static partial class DommelMapper { - private static readonly ConcurrentDictionary _getQueryCache = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary _getByIdsQueryCache = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary _getAllQueryCache = new ConcurrentDictionary(); - /// /// Retrieves the entity of type with the specified id. /// @@ -47,14 +43,15 @@ public static Task GetAsync(this IDbConnection connection, obj private static string BuildGetById(IDbConnection connection, Type type, object id, out DynamicParameters parameters) { - if (!_getQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.Get, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type); var keyColumnName = Resolvers.Column(keyProperty, connection); sql = $"select * from {tableName} where {keyColumnName} = @Id"; - _getQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } parameters = new DynamicParameters(); @@ -125,7 +122,8 @@ public static Task GetAsync(this IDbConnection connection, obj private static string BuildGetByIds(IDbConnection connection, Type type, object[] ids, out DynamicParameters parameters) { - if (!_getByIdsQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.GetByMultipleIds, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); var keyProperties = Resolvers.KeyProperties(type); @@ -149,7 +147,7 @@ private static string BuildGetByIds(IDbConnection connection, Type type, object[ } sql = sb.ToString(); - _getByIdsQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } parameters = new DynamicParameters(); @@ -195,10 +193,11 @@ public static Task> GetAllAsync(this IDbConnection private static string BuildGetAllQuery(IDbConnection connection, Type type) { - if (!_getAllQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.GetAll, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { sql = "select * from " + Resolvers.Table(type, connection); - _getAllQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } return sql; diff --git a/src/Dommel/DommelMapper.Insert.cs b/src/Dommel/DommelMapper.Insert.cs index 3f2f78aa..8f7d09b2 100644 --- a/src/Dommel/DommelMapper.Insert.cs +++ b/src/Dommel/DommelMapper.Insert.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Linq; @@ -11,8 +10,6 @@ namespace Dommel { public static partial class DommelMapper { - private static readonly ConcurrentDictionary _insertQueryCache = new ConcurrentDictionary(); - /// /// Inserts the specified entity into the database and returns the ID. /// @@ -75,8 +72,8 @@ public static async Task InsertAllAsync(this IDbConnection connection, private static string BuildInsertQuery(IDbConnection connection, Type type) { - var cacheKey = $"{connection.GetType().Name}.{type.Name}"; - if (!_insertQueryCache.TryGetValue(cacheKey, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.Insert, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type, out var isIdentity); @@ -105,7 +102,7 @@ private static string BuildInsertQuery(IDbConnection connection, Type type) var builder = GetSqlBuilder(connection); sql = builder.BuildInsert(tableName, columnNames, paramNames, keyProperty); - _insertQueryCache.TryAdd(cacheKey, sql); + QueryCache.TryAdd(cacheKey, sql); } return sql; diff --git a/src/Dommel/DommelMapper.Resolvers.cs b/src/Dommel/DommelMapper.Resolvers.cs index 991c0627..e773973b 100644 --- a/src/Dommel/DommelMapper.Resolvers.cs +++ b/src/Dommel/DommelMapper.Resolvers.cs @@ -50,7 +50,7 @@ public static partial class DommelMapper /// public static class Resolvers { - private static readonly ConcurrentDictionary _typeTableNameCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _typeTableNameCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary _columnNameCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary _typeKeyPropertiesCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary _typePropertiesCache = new ConcurrentDictionary(); @@ -175,10 +175,11 @@ public static string Table(Type type, IDbConnection connection) => /// The table name in the database for . public static string Table(Type type, ISqlBuilder sqlBuilder) { - if (!_typeTableNameCache.TryGetValue(type, out var name)) + var key = $"{sqlBuilder.GetType().Name}.{type.Name}"; + if (!_typeTableNameCache.TryGetValue(key, out var name)) { name = sqlBuilder.QuoteIdentifier(_tableNameResolver.ResolveTableName(type)); - _typeTableNameCache.TryAdd(type, name); + _typeTableNameCache.TryAdd(key, name); } LogReceived?.Invoke($"Resolved table name '{name}' for '{type.Name}'"); @@ -204,7 +205,7 @@ public static string Column(PropertyInfo propertyInfo, IDbConnection connection) /// The column name in the database for . public static string Column(PropertyInfo propertyInfo, ISqlBuilder sqlBuilder) { - var key = $"{propertyInfo.DeclaringType}.{propertyInfo.Name}"; + var key = $"{sqlBuilder.GetType().Name}.{propertyInfo.DeclaringType}.{propertyInfo.Name}"; if (!_columnNameCache.TryGetValue(key, out var columnName)) { columnName = sqlBuilder.QuoteIdentifier(_columnNameResolver.ResolveColumnName(propertyInfo)); diff --git a/src/Dommel/DommelMapper.Select.cs b/src/Dommel/DommelMapper.Select.cs index b53a3ad2..3bfb4ce8 100644 --- a/src/Dommel/DommelMapper.Select.cs +++ b/src/Dommel/DommelMapper.Select.cs @@ -88,13 +88,11 @@ public static Task FirstOrDefaultAsync(this IDbConnection conn private static string BuildSelectSql(IDbConnection connection, Expression> predicate, out DynamicParameters parameters) { var type = typeof(TEntity); - if (!_getAllQueryCache.TryGetValue(type, out var sql)) - { - var tableName = Resolvers.Table(type, connection); - sql = $"select * from {tableName}"; - _getAllQueryCache.TryAdd(type, sql); - } + // Build the select all part + var sql = BuildGetAllQuery(connection, type); + + // Append the where statement sql += new SqlExpression(GetSqlBuilder(connection)) .Where(predicate) .ToSql(out parameters); diff --git a/src/Dommel/DommelMapper.Update.cs b/src/Dommel/DommelMapper.Update.cs index 3b4848c6..35a40c24 100644 --- a/src/Dommel/DommelMapper.Update.cs +++ b/src/Dommel/DommelMapper.Update.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Data; using System.Linq; using System.Reflection; @@ -10,8 +9,6 @@ namespace Dommel { public static partial class DommelMapper { - private static readonly ConcurrentDictionary _updateQueryCache = new ConcurrentDictionary(); - /// /// Updates the values of the specified entity in the database. /// The return value indicates whether the operation succeeded. @@ -46,7 +43,8 @@ public static async Task UpdateAsync(this IDbConnection connectio private static string BuildUpdateQuery(IDbConnection connection, Type type) { - if (!_updateQueryCache.TryGetValue(type, out var sql)) + var cacheKey = new QueryCacheKey(QueryCacheType.Update, connection, type); + if (!QueryCache.TryGetValue(cacheKey, out var sql)) { var tableName = Resolvers.Table(type, connection); var keyProperty = Resolvers.KeyProperty(type); @@ -58,10 +56,9 @@ private static string BuildUpdateQuery(IDbConnection connection, Type type) .ToArray(); var columnNames = typeProperties.Select(p => $"{Resolvers.Column(p, connection)} = @{p.Name}").ToArray(); - sql = $"update {tableName} set {string.Join(", ", columnNames)} where {Resolvers.Column(keyProperty, connection)} = @{keyProperty.Name}"; - _updateQueryCache.TryAdd(type, sql); + QueryCache.TryAdd(cacheKey, sql); } return sql; diff --git a/src/Dommel/DommelMapper.cs b/src/Dommel/DommelMapper.cs index fa5ad2d7..3a15cc34 100644 --- a/src/Dommel/DommelMapper.cs +++ b/src/Dommel/DommelMapper.cs @@ -26,22 +26,5 @@ public static partial class DommelMapper private static void LogQuery(string query, [CallerMemberName]string method = null) => LogReceived?.Invoke(method != null ? $"{method}<{typeof(T).Name}>: {query}" : query); - - /// - /// Clears the query caches. - /// - public static void ClearQueryCache() - { - // Keep this in sync with all query caches - _columnNameCache.Clear(); - _countQueryCache.Clear(); - _deleteAllQueryCache.Clear(); - _deleteQueryCache.Clear(); - _getAllQueryCache.Clear(); - _getByIdsQueryCache.Clear(); - _getQueryCache.Clear(); - _insertQueryCache.Clear(); - _updateQueryCache.Clear(); - } } } From f221cf8b779b4099c894d1d7e2147e6ffccb7161 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:35:44 +0100 Subject: [PATCH 12/18] Refactor integration tests and add Postgres tests --- .../AutoMultiMapTests.cs | 77 ++++++++--------- .../Databases/Database.cs | 73 ++++++++++++++-- .../Databases/MySqlDatabase.cs | 8 +- .../Databases/PostgresDatabase.cs | 10 ++- .../Databases/SqlServerDatabase.cs | 8 +- test/Dommel.IntegrationTests/DeleteTests.cs | 85 +++++++++---------- .../Dommel.IntegrationTests/DommelTestBase.cs | 82 ------------------ test/Dommel.IntegrationTests/GetAllTests.cs | 35 +++----- test/Dommel.IntegrationTests/GetTests.cs | 35 +++----- .../Infrastructure/DatabaseCollection.cs | 10 +++ .../Infrastructure/DatabaseFixture.cs | 43 ++++++++++ .../Infrastructure/DatabaseTestData.cs | 17 ++++ test/Dommel.IntegrationTests/InsertTests.cs | 83 ++++++++---------- test/Dommel.IntegrationTests/Models.cs | 14 +++ test/Dommel.IntegrationTests/PagingTests.cs | 51 +++++------ test/Dommel.IntegrationTests/SelectTests.cs | 49 ++++------- test/Dommel.IntegrationTests/TestSample.cs | 35 +++----- test/Dommel.IntegrationTests/UpdateTests.cs | 35 +++----- 18 files changed, 354 insertions(+), 396 deletions(-) delete mode 100644 test/Dommel.IntegrationTests/DommelTestBase.cs create mode 100644 test/Dommel.IntegrationTests/Infrastructure/DatabaseCollection.cs create mode 100644 test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs create mode 100644 test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs diff --git a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs index 406f0277..0fa92b1d 100644 --- a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs +++ b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs @@ -3,30 +3,14 @@ namespace Dommel.IntegrationTests { - public class AutoMultiMapTestsSqlServer : AutoMultiMapTests, IClassFixture + [Collection("Database")] + public class AutoMultiMapTests { - public AutoMultiMapTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void OneToOne(Database database) { - } - } - - public class AutoMultiMapTestsMySql : AutoMultiMapTests, IClassFixture - { - public AutoMultiMapTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class AutoMultiMapTests : DommelTestBase - { - public AutoMultiMapTests(Database database) : base(database) - { - } - - [Fact] - public void OneToOne() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = con.Get(1); Assert.NotNull(product); @@ -36,10 +20,11 @@ public void OneToOne() } } - [Fact] - public void OneToOneNotExisting() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void OneToOneNotExisting(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = con.Get(11); Assert.NotNull(product); @@ -47,10 +32,11 @@ public void OneToOneNotExisting() } } - [Fact] - public void OneToMany() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void OneToMany(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var order = con.Get(1); Assert.NotNull(order); @@ -59,10 +45,11 @@ public void OneToMany() } } - [Fact] - public void OneToManyNonExsting() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void OneToManyNonExsting(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var order = con.Get(2); Assert.NotNull(order); @@ -70,10 +57,11 @@ public void OneToManyNonExsting() } } - [Fact] - public async Task OneToOneAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task OneToOneAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = await con.GetAsync(1); Assert.NotNull(product); @@ -83,10 +71,11 @@ public async Task OneToOneAsync() } } - [Fact] - public async Task OneToOneNotExistingAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task OneToOneNotExistingAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = await con.GetAsync(11); Assert.NotNull(product); @@ -94,10 +83,11 @@ public async Task OneToOneNotExistingAsync() } } - [Fact] - public async Task OneToManyAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task OneToManyAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var order = await con.GetAsync(1); Assert.NotNull(order); @@ -106,10 +96,11 @@ public async Task OneToManyAsync() } } - [Fact] - public async Task OneToManyNonExstingAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task OneToManyNonExstingAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var order = await con.GetAsync(2); Assert.NotNull(order); diff --git a/test/Dommel.IntegrationTests/Databases/Database.cs b/test/Dommel.IntegrationTests/Databases/Database.cs index 0cf019cc..a155020b 100644 --- a/test/Dommel.IntegrationTests/Databases/Database.cs +++ b/test/Dommel.IntegrationTests/Databases/Database.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Data.Common; +using System.Linq; using System.Threading.Tasks; using Dapper; @@ -9,17 +11,74 @@ public abstract class Database { protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; - public virtual string TempDbDatabaseName => "tempdb"; + public abstract string TempDbDatabaseName { get; } public virtual string DefaultDatabaseName => "dommeltests"; public abstract DbConnection GetConnection(string databaseName); - public abstract Task CreateDatabase(); + public DbConnection GetConnection() => GetConnection(DefaultDatabaseName); - public abstract Task CreateTables(); + public virtual async Task InitializeAsync() + { + await CreateDatabase(); + var created = await CreateTables(); + + // Is the table created? If so, insert dummy data + if (created) + { + using (var connection = GetConnection()) + { + await connection.OpenAsync(); + + var categoryId = Convert.ToInt32(await connection.InsertAsync(new Category { Name = "Food" })); + + var products = new List + { + new Product { CategoryId = categoryId, Name = "Chai" }, + new Product { CategoryId = categoryId, Name = "Chang" }, + new Product { CategoryId = categoryId, Name = "Aniseed Syrup" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix" }, + + new Product { CategoryId = categoryId, Name = "Chai 2" }, + new Product { CategoryId = categoryId, Name = "Chang 2" }, + new Product { CategoryId = categoryId, Name = "Aniseed Syrup 2" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning 2" }, + new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix 2" }, + + new Product { Name = "Foo" }, // 11 + new Product { Name = "Bar" }, // 12 + new Product { Name = "Baz" }, // 13 + }; + + await connection.InsertAllAsync(products); - public virtual async Task DropTables() + // Order 1 + var orderId = Convert.ToInt32(await connection.InsertAsync(new Order())); + var orderLines = new List + { + new OrderLine { OrderId = orderId, Line = "Line 1"}, + new OrderLine { OrderId = orderId, Line = "Line 2"}, + new OrderLine { OrderId = orderId, Line = "Line 3"}, + }; + await connection.InsertAllAsync(orderLines); + + // Order 2 + _ = await connection.InsertAsync(new Order()); + + // Foo's and Bar's for delete queries + await connection.InsertAllAsync(Enumerable.Range(0, 5).Select(_ => new Foo())); + await connection.InsertAllAsync(Enumerable.Range(0, 5).Select(_ => new Bar())); + } + } + } + + protected abstract Task CreateDatabase(); + + protected abstract Task CreateTables(); + + protected virtual async Task DropTables() { using (var con = GetConnection(DefaultDatabaseName)) { @@ -27,8 +86,12 @@ await con.ExecuteAsync(@" DROP TABLE Categories; DROP TABLE Products; DROP TABLE Orders; -DROP TABLE OrderLines;"); +DROP TABLE OrderLines; +DROP TABLE Foos; +DROP TABLE Bars;"); } } + + public virtual async Task DisposeAsync() => await DropTables(); } } diff --git a/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs b/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs index 7c9622e3..06818642 100644 --- a/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs @@ -18,7 +18,7 @@ public override DbConnection GetConnection(string databaseName) public override string TempDbDatabaseName => "mysql"; - public override async Task CreateDatabase() + protected override async Task CreateDatabase() { using (var con = GetConnection(TempDbDatabaseName)) { @@ -26,7 +26,7 @@ public override async Task CreateDatabase() } } - public override async Task CreateTables() + protected override async Task CreateTables() { using (var con = GetConnection(DefaultDatabaseName)) { @@ -35,7 +35,9 @@ public override async Task CreateTables() CREATE TABLE IF NOT EXISTS Categories (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(255)); CREATE TABLE IF NOT EXISTS Products (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, CategoryId int, Name VARCHAR(255)); CREATE TABLE IF NOT EXISTS Orders (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Created DATETIME NOT NULL); -CREATE TABLE IF NOT EXISTS OrderLines (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, OrderId int, Line VARCHAR(255));"; +CREATE TABLE IF NOT EXISTS OrderLines (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, OrderId int, Line VARCHAR(255)); +CREATE TABLE IF NOT EXISTS Foos (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(255)); +CREATE TABLE IF NOT EXISTS Bars (Id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Name VARCHAR(255));"; var created = await con.ExecuteScalarAsync(sql); // No result means the tables were just created diff --git a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs b/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs index 7bcba3f8..47a3cd62 100644 --- a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs @@ -21,7 +21,7 @@ public override DbConnection GetConnection(string databaseName) private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - public override async Task CreateDatabase() + protected override async Task CreateDatabase() { using (var con = GetConnection(TempDbDatabaseName)) { @@ -38,7 +38,7 @@ public override async Task CreateDatabase() } } - public override async Task CreateTables() + protected override async Task CreateTables() { using (var con = GetConnection(DefaultDatabaseName)) { @@ -47,7 +47,9 @@ public override async Task CreateTables() CREATE TABLE IF NOT EXISTS ""Categories"" (""Id"" serial primary key, ""Name"" VARCHAR(255)); CREATE TABLE IF NOT EXISTS ""Products"" (""Id"" serial primary key, ""CategoryId"" int, ""Name"" VARCHAR(255)); CREATE TABLE IF NOT EXISTS ""Orders"" (""Id"" serial primary key, ""Created"" TIMESTAMP NOT NULL); -CREATE TABLE IF NOT EXISTS ""OrderLines"" (""Id"" serial primary key, ""OrderId"" int, ""Line"" VARCHAR(255));"; +CREATE TABLE IF NOT EXISTS ""OrderLines"" (""Id"" serial primary key, ""OrderId"" int, ""Line"" VARCHAR(255)); +CREATE TABLE IF NOT EXISTS ""Foos"" (""Id"" serial primary key, ""Name"" VARCHAR(255)); +CREATE TABLE IF NOT EXISTS ""Bars"" (""Id"" serial primary key, ""Name"" VARCHAR(255));"; var created = await con.ExecuteScalarAsync(sql); // No result means the tables were just created @@ -55,7 +57,7 @@ public override async Task CreateTables() } } - public override async Task DropTables() + protected override async Task DropTables() { using (var con = GetConnection(DefaultDatabaseName)) { diff --git a/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs b/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs index aad699a0..51df47da 100644 --- a/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs @@ -16,7 +16,9 @@ public override DbConnection GetConnection(string databaseName) return new SqlConnection(connectionString); } - public override async Task CreateDatabase() + public override string TempDbDatabaseName => "tempdb"; + + protected override async Task CreateDatabase() { using (var con = GetConnection(TempDbDatabaseName)) { @@ -24,7 +26,7 @@ public override async Task CreateDatabase() } } - public override async Task CreateTables() + protected override async Task CreateTables() { using (var con = GetConnection(DefaultDatabaseName)) { @@ -34,6 +36,8 @@ public override async Task CreateTables() CREATE TABLE dbo.Products (Id int IDENTITY(1,1) PRIMARY KEY, CategoryId int, Name VARCHAR(255)); CREATE TABLE dbo.Orders (Id int IDENTITY(1,1) PRIMARY KEY, Created DATETIME NOT NULL); CREATE TABLE dbo.OrderLines (Id int IDENTITY(1,1) PRIMARY KEY, OrderId int, Line VARCHAR(255)); + CREATE TABLE dbo.Foos (Id int IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255)); + CREATE TABLE dbo.Bars (Id int IDENTITY(1,1) PRIMARY KEY, Name VARCHAR(255)); SELECT 1; END"; var created = await con.ExecuteScalarAsync(sql); diff --git a/test/Dommel.IntegrationTests/DeleteTests.cs b/test/Dommel.IntegrationTests/DeleteTests.cs index 8532a6bf..baa26c52 100644 --- a/test/Dommel.IntegrationTests/DeleteTests.cs +++ b/test/Dommel.IntegrationTests/DeleteTests.cs @@ -6,30 +6,14 @@ namespace Dommel.IntegrationTests { - public class DeleteTestsSqlServer : DeleteTests, IClassFixture + [Collection("Database")] + public class DeleteTests { - public DeleteTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Delete(Database database) { - } - } - - public class DeleteTestsMySql : DeleteTests, IClassFixture - { - public DeleteTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class DeleteTests : DommelTestBase - { - public DeleteTests(Database database) : base(database) - { - } - - [Fact] - public void Delete() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var id = Convert.ToInt32(con.Insert(new Product { Name = "blah" })); var product = con.Get(id); @@ -42,10 +26,11 @@ public void Delete() } } - [Fact] - public async Task DeleteAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task DeleteAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var id = Convert.ToInt32(await con.InsertAsync(new Product { Name = "blah" })); var product = await con.GetAsync(id); @@ -58,30 +43,33 @@ public async Task DeleteAsync() } } - [Fact] - public void DeleteAll() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void DeleteAll(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { - Assert.True(con.DeleteAll()); - Assert.Empty(con.GetAll()); + Assert.True(con.DeleteAll()); + Assert.Empty(con.GetAll()); } } - [Fact] - public async Task DeleteAllAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task DeleteAllAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { - Assert.True(await con.DeleteAllAsync()); - Assert.Empty(await con.GetAllAsync()); + Assert.True(await con.DeleteAllAsync()); + Assert.Empty(await con.GetAllAsync()); } } - [Fact] - public void DeleteMultiple() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void DeleteMultiple(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var ps = new List { @@ -98,10 +86,11 @@ public void DeleteMultiple() } } - [Fact] - public async Task DeleteMultipleAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task DeleteMultipleAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var ps = new List { @@ -118,10 +107,11 @@ public async Task DeleteMultipleAsync() } } - [Fact] - public void DeleteMultipleLike() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void DeleteMultipleLike(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var ps = new List { @@ -138,10 +128,11 @@ public void DeleteMultipleLike() } } - [Fact] - public async Task DeleteMultipleAsyncLike() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task DeleteMultipleAsyncLike(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var ps = new List { diff --git a/test/Dommel.IntegrationTests/DommelTestBase.cs b/test/Dommel.IntegrationTests/DommelTestBase.cs deleted file mode 100644 index 00f22344..00000000 --- a/test/Dommel.IntegrationTests/DommelTestBase.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Threading.Tasks; -using Xunit; - -[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] - -namespace Dommel.IntegrationTests -{ - public abstract class DommelTestBase : IAsyncLifetime - { - public DommelTestBase(Database database) - { - Database = database; - } - - protected Database Database { get; } - - protected DbConnection GetConnection() => Database.GetConnection(Database.DefaultDatabaseName); - - public virtual async Task InitializeAsync() - { - using (var connection = Database.GetConnection(Database.TempDbDatabaseName)) - { - await Database.CreateDatabase(); - } - - using (var connection = GetConnection()) - { - await connection.OpenAsync(); - var created = await Database.CreateTables(); - - // Is the table created? If so, insert dummy data - if (created) - { - var categoryId = Convert.ToInt32(await connection.InsertAsync(new Category { Name = "Food" })); - - var products = new List - { - new Product { CategoryId = categoryId, Name = "Chai" }, - new Product { CategoryId = categoryId, Name = "Chang" }, - new Product { CategoryId = categoryId, Name = "Aniseed Syrup" }, - new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning" }, - new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix" }, - - new Product { CategoryId = categoryId, Name = "Chai 2" }, - new Product { CategoryId = categoryId, Name = "Chang 2" }, - new Product { CategoryId = categoryId, Name = "Aniseed Syrup 2" }, - new Product { CategoryId = categoryId, Name = "Chef Anton's Cajun Seasoning 2" }, - new Product { CategoryId = categoryId, Name = "Chef Anton's Gumbo Mix 2" }, - - new Product { Name = "Foo" }, // 11 - new Product { Name = "Bar" }, // 12 - new Product { Name = "Baz" }, // 13 - }; - - await connection.InsertAllAsync(products); - - // Order 1 - var orderId = Convert.ToInt32(await connection.InsertAsync(new Order())); - var orderLines = new List - { - new OrderLine { OrderId = orderId, Line = "Line 1"}, - new OrderLine { OrderId = orderId, Line = "Line 2"}, - new OrderLine { OrderId = orderId, Line = "Line 3"}, - }; - await connection.InsertAllAsync(orderLines); - - // Order 2 - _ = await connection.InsertAsync(new Order()); - } - } - } - - public virtual async Task DisposeAsync() - { - await Database.DropTables(); - DommelMapper.ClearQueryCache(); - } - } -} diff --git a/test/Dommel.IntegrationTests/GetAllTests.cs b/test/Dommel.IntegrationTests/GetAllTests.cs index 85a07761..44d8dc52 100644 --- a/test/Dommel.IntegrationTests/GetAllTests.cs +++ b/test/Dommel.IntegrationTests/GetAllTests.cs @@ -3,30 +3,14 @@ namespace Dommel.IntegrationTests { - public class GetAllTestsSqlServer : GetAllTests, IClassFixture + [Collection("Database")] + public class GetAllTests { - public GetAllTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void GetAll(Database database) { - } - } - - public class GetAllTestsMySql : GetAllTests, IClassFixture - { - public GetAllTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class GetAllTests : DommelTestBase - { - public GetAllTests(Database database) : base(database) - { - } - - [Fact] - public void GetAll() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var products = con.GetAll(); Assert.NotEmpty(products); @@ -34,10 +18,11 @@ public void GetAll() } } - [Fact] - public async Task GetAllAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task GetAllAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var products = await con.GetAllAsync(); Assert.NotEmpty(products); diff --git a/test/Dommel.IntegrationTests/GetTests.cs b/test/Dommel.IntegrationTests/GetTests.cs index 278644b9..e7af4ffb 100644 --- a/test/Dommel.IntegrationTests/GetTests.cs +++ b/test/Dommel.IntegrationTests/GetTests.cs @@ -3,30 +3,14 @@ namespace Dommel.IntegrationTests { - public class GetTestsSqlServer : GetTests, IClassFixture + [Collection("Database")] + public class GetTests { - public GetTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Get(Database database) { - } - } - - public class GetTestsMySql : GetTests, IClassFixture - { - public GetTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class GetTests : DommelTestBase - { - public GetTests(Database database) : base(database) - { - } - - [Fact] - public void Get() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = con.Get(1); Assert.NotNull(product); @@ -34,10 +18,11 @@ public void Get() } } - [Fact] - public async Task GetAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task GetAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = await con.GetAsync(1); Assert.NotNull(product); diff --git a/test/Dommel.IntegrationTests/Infrastructure/DatabaseCollection.cs b/test/Dommel.IntegrationTests/Infrastructure/DatabaseCollection.cs new file mode 100644 index 00000000..1976ec2d --- /dev/null +++ b/test/Dommel.IntegrationTests/Infrastructure/DatabaseCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Dommel.IntegrationTests +{ + // Apply the text fixture to all tests in the "Database" collection + [CollectionDefinition("Database")] + public class DatabaseCollection : ICollectionFixture + { + } +} diff --git a/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs b/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs new file mode 100644 index 00000000..5ba19145 --- /dev/null +++ b/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class DatabaseFixture : IAsyncLifetime + { + private readonly Database[] _databases; + + public DatabaseFixture() + { + // Extract the + _databases = new DatabaseTestData() + .Select(x => x[0]) + .OfType() + .OfType() + .ToArray(); + + if (_databases.Length == 0) + { + throw new InvalidOperationException($"No databases defined in {nameof(DatabaseTestData)} theory data."); + } + } + + public async Task InitializeAsync() + { + foreach (var database in _databases) + { + await database.InitializeAsync(); + } + } + + public async Task DisposeAsync() + { + foreach (var database in _databases) + { + await database.DisposeAsync(); + } + } + } +} diff --git a/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs b/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs new file mode 100644 index 00000000..401f8795 --- /dev/null +++ b/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs @@ -0,0 +1,17 @@ +using Xunit; + +namespace Dommel.IntegrationTests +{ + public class DatabaseTestData : TheoryData + { + public DatabaseTestData() + { + // Defines the database providers to use for each test method. + // These providers are used to initialize the databases in the + // DatabaseFixture as well. + Add(new SqlServerDatabase()); + Add(new MySqlDatabase()); + Add(new PostgresDatabase()); + } + } +} diff --git a/test/Dommel.IntegrationTests/InsertTests.cs b/test/Dommel.IntegrationTests/InsertTests.cs index 5484ad81..e401d2f8 100644 --- a/test/Dommel.IntegrationTests/InsertTests.cs +++ b/test/Dommel.IntegrationTests/InsertTests.cs @@ -6,30 +6,14 @@ namespace Dommel.IntegrationTests { - public class InsertTestsSqlServer : InsertTests, IClassFixture + [Collection("Database")] + public class InsertTests { - public InsertTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Insert(Database database) { - } - } - - public class InsertTestsMySql : InsertTests, IClassFixture - { - public InsertTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class InsertTests : DommelTestBase - { - public InsertTests(Database database) : base(database) - { - } - - [Fact] - public void Insert() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var id = Convert.ToInt32(con.Insert(new Product { Name = "blah" })); var product = con.Get(id); @@ -39,10 +23,11 @@ public void Insert() } } - [Fact] - public async Task InsertAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task InsertAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var id = Convert.ToInt32(await con.InsertAsync(new Product { Name = "blah" })); var product = await con.GetAsync(id); @@ -52,58 +37,62 @@ public async Task InsertAsync() } } - [Fact] - public void InsertAll() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void InsertAll(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { - var ps = new List + var ps = new List { - new Product { Name = "blah"}, - new Product { Name = "blah"}, - new Product { Name = "blah"}, + new Foo { Name = "blah" }, + new Foo { Name = "blah" }, + new Foo { Name = "blah" }, }; con.InsertAll(ps); - var blahs = con.Select(p => p.Name == "blah"); + var blahs = con.Select(p => p.Name == "blah"); Assert.Equal(3, blahs.Count()); } } - [Fact] - public async Task InsertAllAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task InsertAllAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { - var ps = new List + var ps = new List { - new Product { Name = "blah"}, - new Product { Name = "blah"}, - new Product { Name = "blah"}, + new Bar { Name = "blah" }, + new Bar { Name = "blah" }, + new Bar { Name = "blah" }, }; await con.InsertAllAsync(ps); - var blahs = await con.SelectAsync(p => p.Name == "blah"); + var blahs = await con.SelectAsync(p => p.Name == "blah"); Assert.Equal(3, blahs.Count()); } } - [Fact] - public void InsertAllEmtyList() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void InsertAllEmtyList(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var ps = new List(); con.InsertAll(ps); } } - [Fact] - public async Task InsertAllAsyncEmtyList() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task InsertAllAsyncEmtyList(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var ps = new List(); await con.InsertAllAsync(ps); diff --git a/test/Dommel.IntegrationTests/Models.cs b/test/Dommel.IntegrationTests/Models.cs index d5a41ac2..0ee1d045 100644 --- a/test/Dommel.IntegrationTests/Models.cs +++ b/test/Dommel.IntegrationTests/Models.cs @@ -42,4 +42,18 @@ public class OrderLine public string Line { get; set; } } + + public class Foo + { + public int Id { get; set; } + + public string Name { get; set; } = nameof(Foo); + } + + public class Bar + { + public int Id { get; set; } + + public string Name { get; set; } = nameof(Bar); + } } diff --git a/test/Dommel.IntegrationTests/PagingTests.cs b/test/Dommel.IntegrationTests/PagingTests.cs index 33176b74..a4295e2e 100644 --- a/test/Dommel.IntegrationTests/PagingTests.cs +++ b/test/Dommel.IntegrationTests/PagingTests.cs @@ -4,30 +4,14 @@ namespace Dommel.IntegrationTests { - public class PagingTestsSqlServer : PagingTests, IClassFixture + [Collection("Database")] + public class PagingTests { - public PagingTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Fetches_FirstPage(Database database) { - } - } - - public class PagingTestsMySql : PagingTests, IClassFixture - { - public PagingTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class PagingTests : DommelTestBase - { - public PagingTests(Database database) : base(database) - { - } - - [Fact] - public void Fetches_FirstPage() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var paged = con.GetPaged(1, 5).ToArray(); Assert.Equal(5, paged.Length); @@ -40,30 +24,33 @@ public void Fetches_FirstPage() } } - [Fact] - public void Fetches_SecondPage() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Fetches_SecondPage(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var paged = con.GetPaged(2, 5).ToArray(); Assert.Equal(5, paged.Length); } } - [Fact] - public async Task Fetches_ThirdPartialPage() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task Fetches_ThirdPartialPage(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var paged = (await con.GetPagedAsync(3, 5)).ToArray(); - Assert.Equal(3, paged.Length); + Assert.True(paged.Length >= 3, "Should contain at least 3 items"); } } - [Fact] - public async Task SelectPaged_FetchesFirstPage() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task SelectPaged_FetchesFirstPage(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var paged = (await con.SelectPagedAsync(p => p.Name == "Chai", 1, 5)).ToArray(); Assert.Single(paged); diff --git a/test/Dommel.IntegrationTests/SelectTests.cs b/test/Dommel.IntegrationTests/SelectTests.cs index b57c7c30..5fe0a0a3 100644 --- a/test/Dommel.IntegrationTests/SelectTests.cs +++ b/test/Dommel.IntegrationTests/SelectTests.cs @@ -3,60 +3,47 @@ namespace Dommel.IntegrationTests { - public class SelectTestsSqlServer : SelectTests, IClassFixture + [Collection("Database")] + public class SelectTests { - public SelectTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void SelectEqual(Database database) { - } - } - - public class SelectTestsMySql : SelectTests, IClassFixture - { - public SelectTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class SelectTests : DommelTestBase - { - public SelectTests(Database database) : base(database) - { - } - - [Fact] - public void SelectEqual() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var products = con.Select(p => p.CategoryId == 1); Assert.NotEmpty(products); } } - [Fact] - public async Task SelectAsyncEqual() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task SelectAsyncEqual(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var products = await con.SelectAsync(p => p.CategoryId == 1); Assert.NotEmpty(products); } } - [Fact] - public void SelectContains() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void SelectContains(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var products = con.Select(p => p.Name.Contains("Chai")); Assert.NotEmpty(products); } } - [Fact] - public async Task SelectAsyncContains() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task SelectAsyncContains(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var products = await con.SelectAsync(p => p.Name.Contains("Chai")); Assert.NotEmpty(products); diff --git a/test/Dommel.IntegrationTests/TestSample.cs b/test/Dommel.IntegrationTests/TestSample.cs index 27b494e1..26b01f90 100644 --- a/test/Dommel.IntegrationTests/TestSample.cs +++ b/test/Dommel.IntegrationTests/TestSample.cs @@ -3,39 +3,24 @@ namespace Dommel.IntegrationTests { - public class SampleTestsSqlServer : SampleTests, IClassFixture + [Collection("Database")] + public class SampleTests { - public SampleTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Sample(Database database) { - } - } - - public class SampleTestsMySql : SampleTests, IClassFixture - { - public SampleTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class SampleTests : DommelTestBase - { - public SampleTests(Database database) : base(database) - { - } - - [Fact] - public void Sample() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { _ = con.GetAll(); } } - [Fact] - public async Task SampleAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task SampleAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { _ = await con.GetAllAsync(); } diff --git a/test/Dommel.IntegrationTests/UpdateTests.cs b/test/Dommel.IntegrationTests/UpdateTests.cs index 0fc1ec80..77956bc5 100644 --- a/test/Dommel.IntegrationTests/UpdateTests.cs +++ b/test/Dommel.IntegrationTests/UpdateTests.cs @@ -3,30 +3,14 @@ namespace Dommel.IntegrationTests { - public class UpdateTestsSqlServer : UpdateTests, IClassFixture + [Collection("Database")] + public class UpdateTests { - public UpdateTestsSqlServer(SqlServerDatabase database) : base(database) + [Theory] + [ClassData(typeof(DatabaseTestData))] + public void Update(Database database) { - } - } - - public class UpdateTestsMySql : UpdateTests, IClassFixture - { - public UpdateTestsMySql(MySqlDatabase database) : base(database) - { - } - } - - public abstract class UpdateTests : DommelTestBase - { - public UpdateTests(Database database) : base(database) - { - } - - [Fact] - public void Update() - { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = con.Get(1); Assert.NotNull(product); @@ -39,10 +23,11 @@ public void Update() } } - [Fact] - public async Task UpdateAsync() + [Theory] + [ClassData(typeof(DatabaseTestData))] + public async Task UpdateAsync(Database database) { - using (var con = GetConnection()) + using (var con = database.GetConnection()) { var product = await con.GetAsync(1); Assert.NotNull(product); From 4f03c6ec448799fa284ac00fc23ff065714041dd Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:39:27 +0100 Subject: [PATCH 13/18] Rename Database to DatabaseDriver --- .../Dommel.IntegrationTests/AutoMultiMapTests.cs | 16 ++++++++-------- .../Databases/{Database.cs => DatabaseDriver.cs} | 5 ++++- .../{MySqlDatabase.cs => MySqlDatabaseDriver.cs} | 2 +- ...gresDatabase.cs => PostgresDatabaseDriver.cs} | 2 +- ...verDatabase.cs => SqlServerDatabaseDriver.cs} | 2 +- test/Dommel.IntegrationTests/DeleteTests.cs | 16 ++++++++-------- test/Dommel.IntegrationTests/GetAllTests.cs | 4 ++-- test/Dommel.IntegrationTests/GetTests.cs | 4 ++-- .../Infrastructure/DatabaseFixture.cs | 6 +++--- .../Infrastructure/DatabaseTestData.cs | 8 ++++---- test/Dommel.IntegrationTests/InsertTests.cs | 12 ++++++------ test/Dommel.IntegrationTests/PagingTests.cs | 8 ++++---- test/Dommel.IntegrationTests/SelectTests.cs | 8 ++++---- test/Dommel.IntegrationTests/TestSample.cs | 4 ++-- test/Dommel.IntegrationTests/UpdateTests.cs | 4 ++-- 15 files changed, 52 insertions(+), 49 deletions(-) rename test/Dommel.IntegrationTests/Databases/{Database.cs => DatabaseDriver.cs} (96%) rename test/Dommel.IntegrationTests/Databases/{MySqlDatabase.cs => MySqlDatabaseDriver.cs} (97%) rename test/Dommel.IntegrationTests/Databases/{PostgresDatabase.cs => PostgresDatabaseDriver.cs} (97%) rename test/Dommel.IntegrationTests/Databases/{SqlServerDatabase.cs => SqlServerDatabaseDriver.cs} (97%) diff --git a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs index 0fa92b1d..7d040fd9 100644 --- a/test/Dommel.IntegrationTests/AutoMultiMapTests.cs +++ b/test/Dommel.IntegrationTests/AutoMultiMapTests.cs @@ -8,7 +8,7 @@ public class AutoMultiMapTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void OneToOne(Database database) + public void OneToOne(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -22,7 +22,7 @@ public void OneToOne(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void OneToOneNotExisting(Database database) + public void OneToOneNotExisting(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -34,7 +34,7 @@ public void OneToOneNotExisting(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void OneToMany(Database database) + public void OneToMany(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -47,7 +47,7 @@ public void OneToMany(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void OneToManyNonExsting(Database database) + public void OneToManyNonExsting(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -59,7 +59,7 @@ public void OneToManyNonExsting(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task OneToOneAsync(Database database) + public async Task OneToOneAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -73,7 +73,7 @@ public async Task OneToOneAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task OneToOneNotExistingAsync(Database database) + public async Task OneToOneNotExistingAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -85,7 +85,7 @@ public async Task OneToOneNotExistingAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task OneToManyAsync(Database database) + public async Task OneToManyAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -98,7 +98,7 @@ public async Task OneToManyAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task OneToManyNonExstingAsync(Database database) + public async Task OneToManyNonExstingAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/Databases/Database.cs b/test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs similarity index 96% rename from test/Dommel.IntegrationTests/Databases/Database.cs rename to test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs index a155020b..f483090c 100644 --- a/test/Dommel.IntegrationTests/Databases/Database.cs +++ b/test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs @@ -7,7 +7,10 @@ namespace Dommel.IntegrationTests { - public abstract class Database + /// + /// Provides a driver to interact with a specific database system. + /// + public abstract class DatabaseDriver { protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; diff --git a/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs b/test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs similarity index 97% rename from test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs rename to test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs index 06818642..4daf2792 100644 --- a/test/Dommel.IntegrationTests/Databases/MySqlDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs @@ -5,7 +5,7 @@ namespace Dommel.IntegrationTests { - public class MySqlDatabase : Database + public class MySqlDatabaseDriver : DatabaseDriver { public override DbConnection GetConnection(string databaseName) { diff --git a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs b/test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs similarity index 97% rename from test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs rename to test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs index 47a3cd62..7d6fc4ce 100644 --- a/test/Dommel.IntegrationTests/Databases/PostgresDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs @@ -6,7 +6,7 @@ namespace Dommel.IntegrationTests { - public class PostgresDatabase : Database + public class PostgresDatabaseDriver : DatabaseDriver { public override DbConnection GetConnection(string databaseName) { diff --git a/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs b/test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs similarity index 97% rename from test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs rename to test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs index 51df47da..55b95f5b 100644 --- a/test/Dommel.IntegrationTests/Databases/SqlServerDatabase.cs +++ b/test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs @@ -5,7 +5,7 @@ namespace Dommel.IntegrationTests { - public class SqlServerDatabase : Database + public class SqlServerDatabaseDriver : DatabaseDriver { public override DbConnection GetConnection(string databaseName) { diff --git a/test/Dommel.IntegrationTests/DeleteTests.cs b/test/Dommel.IntegrationTests/DeleteTests.cs index baa26c52..a34a9944 100644 --- a/test/Dommel.IntegrationTests/DeleteTests.cs +++ b/test/Dommel.IntegrationTests/DeleteTests.cs @@ -11,7 +11,7 @@ public class DeleteTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void Delete(Database database) + public void Delete(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -28,7 +28,7 @@ public void Delete(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task DeleteAsync(Database database) + public async Task DeleteAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -45,7 +45,7 @@ public async Task DeleteAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void DeleteAll(Database database) + public void DeleteAll(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -56,7 +56,7 @@ public void DeleteAll(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task DeleteAllAsync(Database database) + public async Task DeleteAllAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -67,7 +67,7 @@ public async Task DeleteAllAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void DeleteMultiple(Database database) + public void DeleteMultiple(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -88,7 +88,7 @@ public void DeleteMultiple(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task DeleteMultipleAsync(Database database) + public async Task DeleteMultipleAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -109,7 +109,7 @@ public async Task DeleteMultipleAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void DeleteMultipleLike(Database database) + public void DeleteMultipleLike(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -130,7 +130,7 @@ public void DeleteMultipleLike(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task DeleteMultipleAsyncLike(Database database) + public async Task DeleteMultipleAsyncLike(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/GetAllTests.cs b/test/Dommel.IntegrationTests/GetAllTests.cs index 44d8dc52..c986d981 100644 --- a/test/Dommel.IntegrationTests/GetAllTests.cs +++ b/test/Dommel.IntegrationTests/GetAllTests.cs @@ -8,7 +8,7 @@ public class GetAllTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void GetAll(Database database) + public void GetAll(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -20,7 +20,7 @@ public void GetAll(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task GetAllAsync(Database database) + public async Task GetAllAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/GetTests.cs b/test/Dommel.IntegrationTests/GetTests.cs index e7af4ffb..bad6c856 100644 --- a/test/Dommel.IntegrationTests/GetTests.cs +++ b/test/Dommel.IntegrationTests/GetTests.cs @@ -8,7 +8,7 @@ public class GetTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void Get(Database database) + public void Get(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -20,7 +20,7 @@ public void Get(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task GetAsync(Database database) + public async Task GetAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs b/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs index 5ba19145..c286f7fe 100644 --- a/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs +++ b/test/Dommel.IntegrationTests/Infrastructure/DatabaseFixture.cs @@ -7,15 +7,15 @@ namespace Dommel.IntegrationTests { public class DatabaseFixture : IAsyncLifetime { - private readonly Database[] _databases; + private readonly DatabaseDriver[] _databases; public DatabaseFixture() { // Extract the _databases = new DatabaseTestData() .Select(x => x[0]) - .OfType() - .OfType() + .OfType() + .OfType() .ToArray(); if (_databases.Length == 0) diff --git a/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs b/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs index 401f8795..4e9125ea 100644 --- a/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs +++ b/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs @@ -2,16 +2,16 @@ namespace Dommel.IntegrationTests { - public class DatabaseTestData : TheoryData + public class DatabaseTestData : TheoryData { public DatabaseTestData() { // Defines the database providers to use for each test method. // These providers are used to initialize the databases in the // DatabaseFixture as well. - Add(new SqlServerDatabase()); - Add(new MySqlDatabase()); - Add(new PostgresDatabase()); + Add(new SqlServerDatabaseDriver()); + Add(new MySqlDatabaseDriver()); + Add(new PostgresDatabaseDriver()); } } } diff --git a/test/Dommel.IntegrationTests/InsertTests.cs b/test/Dommel.IntegrationTests/InsertTests.cs index e401d2f8..b80072a3 100644 --- a/test/Dommel.IntegrationTests/InsertTests.cs +++ b/test/Dommel.IntegrationTests/InsertTests.cs @@ -11,7 +11,7 @@ public class InsertTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void Insert(Database database) + public void Insert(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -25,7 +25,7 @@ public void Insert(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task InsertAsync(Database database) + public async Task InsertAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -39,7 +39,7 @@ public async Task InsertAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void InsertAll(Database database) + public void InsertAll(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -59,7 +59,7 @@ public void InsertAll(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task InsertAllAsync(Database database) + public async Task InsertAllAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -79,7 +79,7 @@ public async Task InsertAllAsync(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void InsertAllEmtyList(Database database) + public void InsertAllEmtyList(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -90,7 +90,7 @@ public void InsertAllEmtyList(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task InsertAllAsyncEmtyList(Database database) + public async Task InsertAllAsyncEmtyList(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/PagingTests.cs b/test/Dommel.IntegrationTests/PagingTests.cs index a4295e2e..e86f10d0 100644 --- a/test/Dommel.IntegrationTests/PagingTests.cs +++ b/test/Dommel.IntegrationTests/PagingTests.cs @@ -9,7 +9,7 @@ public class PagingTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void Fetches_FirstPage(Database database) + public void Fetches_FirstPage(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -26,7 +26,7 @@ public void Fetches_FirstPage(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void Fetches_SecondPage(Database database) + public void Fetches_SecondPage(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -37,7 +37,7 @@ public void Fetches_SecondPage(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task Fetches_ThirdPartialPage(Database database) + public async Task Fetches_ThirdPartialPage(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -48,7 +48,7 @@ public async Task Fetches_ThirdPartialPage(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task SelectPaged_FetchesFirstPage(Database database) + public async Task SelectPaged_FetchesFirstPage(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/SelectTests.cs b/test/Dommel.IntegrationTests/SelectTests.cs index 5fe0a0a3..a63be6ed 100644 --- a/test/Dommel.IntegrationTests/SelectTests.cs +++ b/test/Dommel.IntegrationTests/SelectTests.cs @@ -8,7 +8,7 @@ public class SelectTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void SelectEqual(Database database) + public void SelectEqual(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -19,7 +19,7 @@ public void SelectEqual(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task SelectAsyncEqual(Database database) + public async Task SelectAsyncEqual(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -30,7 +30,7 @@ public async Task SelectAsyncEqual(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public void SelectContains(Database database) + public void SelectContains(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -41,7 +41,7 @@ public void SelectContains(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task SelectAsyncContains(Database database) + public async Task SelectAsyncContains(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/TestSample.cs b/test/Dommel.IntegrationTests/TestSample.cs index 26b01f90..62ad08e4 100644 --- a/test/Dommel.IntegrationTests/TestSample.cs +++ b/test/Dommel.IntegrationTests/TestSample.cs @@ -8,7 +8,7 @@ public class SampleTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void Sample(Database database) + public void Sample(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -18,7 +18,7 @@ public void Sample(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task SampleAsync(Database database) + public async Task SampleAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { diff --git a/test/Dommel.IntegrationTests/UpdateTests.cs b/test/Dommel.IntegrationTests/UpdateTests.cs index 77956bc5..42112cbe 100644 --- a/test/Dommel.IntegrationTests/UpdateTests.cs +++ b/test/Dommel.IntegrationTests/UpdateTests.cs @@ -8,7 +8,7 @@ public class UpdateTests { [Theory] [ClassData(typeof(DatabaseTestData))] - public void Update(Database database) + public void Update(DatabaseDriver database) { using (var con = database.GetConnection()) { @@ -25,7 +25,7 @@ public void Update(Database database) [Theory] [ClassData(typeof(DatabaseTestData))] - public async Task UpdateAsync(Database database) + public async Task UpdateAsync(DatabaseDriver database) { using (var con = database.GetConnection()) { From 8a4940aef0885fdf2e3cc179f4c6f101f543f6e2 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:39:42 +0100 Subject: [PATCH 14/18] Bump .NET 4.5.1 to 4.5.2 and multi-target tests --- src/Dommel/Dommel.csproj | 2 +- test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj | 6 +++--- test/Dommel.Tests/Dommel.Tests.csproj | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Dommel/Dommel.csproj b/src/Dommel/Dommel.csproj index 1ca56054..ca2d2abe 100644 --- a/src/Dommel/Dommel.csproj +++ b/src/Dommel/Dommel.csproj @@ -5,7 +5,7 @@ Dommel 2.0.0-beta3 Henk Mollema - net451;netstandard1.3;netstandard2.0 + net452;netstandard1.3;netstandard2.0 netstandard1.3;netstandard2.0 latest true diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index 9cc59604..033b0f9c 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -1,6 +1,6 @@  - netcoreapp2.1 + net452;netcoreapp2.0;netcoreapp2.1 false latest @@ -9,7 +9,7 @@ - - + + diff --git a/test/Dommel.Tests/Dommel.Tests.csproj b/test/Dommel.Tests/Dommel.Tests.csproj index 095f1dad..e784469f 100644 --- a/test/Dommel.Tests/Dommel.Tests.csproj +++ b/test/Dommel.Tests/Dommel.Tests.csproj @@ -1,6 +1,6 @@  - netcoreapp2.1 + netcoreapp2.0;netcoreapp2.1 false @@ -8,7 +8,7 @@ - - + + From 58fa70325cc2b2b3a14436962b126daa2ee13903 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:40:59 +0100 Subject: [PATCH 15/18] Let's not test .NET 4.5.2 --- test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index 033b0f9c..36f196d9 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -1,6 +1,6 @@  - net452;netcoreapp2.0;netcoreapp2.1 + netcoreapp2.0;netcoreapp2.1 false latest From 2722b53c52f8f9dbc9ac54c931dd5ba55ca45e2f Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:41:39 +0100 Subject: [PATCH 16/18] Add PostgreSQL to AppVeyor --- .appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.appveyor.yml b/.appveyor.yml index b6a9df44..9b9b97ef 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,6 +17,7 @@ environment: services: - mssql2016 - mysql + - postgresql101 artifacts: - path: .\src\Dommel\artifacts\**\*.nupkg name: NuGet From 8434ed496ad96d8daa408e8a4faf2bc8b36d7904 Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:44:14 +0100 Subject: [PATCH 17/18] Don't test net452 on non-Windows --- test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index 36f196d9..f111591c 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -1,6 +1,7 @@  - netcoreapp2.0;netcoreapp2.1 + net452;netcoreapp2.0;netcoreapp2.1 + netcoreapp2.0;netcoreapp2.1 false latest From 4c5769eecaeb0670ac5667331e7da501f9de74db Mon Sep 17 00:00:00 2001 From: Henk Mollema Date: Thu, 15 Nov 2018 20:59:52 +0100 Subject: [PATCH 18/18] Add MySQL and PostgreSQL tests for Travis --- .appveyor.yml | 5 ++++- .travis.yml | 13 ++++++++----- build.sh | 1 + .../Databases/DatabaseDriver.cs | 2 -- .../Databases/MySqlDatabaseDriver.cs | 12 +++++++++--- .../Databases/PostgresDatabaseDriver.cs | 12 +++++++++--- .../Databases/SqlServerDatabaseDriver.cs | 2 +- .../Dommel.IntegrationTests.csproj | 3 +-- test/Dommel.IntegrationTests/Infrastructure/CI.cs | 13 +++++++++++++ .../Infrastructure/DatabaseTestData.cs | 6 +++++- 10 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 test/Dommel.IntegrationTests/Infrastructure/CI.cs diff --git a/.appveyor.yml b/.appveyor.yml index 9b9b97ef..c75197ac 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -13,11 +13,14 @@ environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true CI: true - AppVeyor: true + APPVEYOR: true services: - mssql2016 - mysql - postgresql101 +branches: + only: + - master artifacts: - path: .\src\Dommel\artifacts\**\*.nupkg name: NuGet diff --git a/.travis.yml b/.travis.yml index a796b5e1..29668b9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,14 @@ dist: trusty env: global: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - - DOTNET_CLI_TELEMETRY_OPTOUT: 1 -os: - - linux - - osx -osx_image: xcode8.2 + - DOTNET_CLI_TELEMETRY_OPTOUT: true + - TRAVIS: true +services: + - mysql + - postgresql +branches: + only: + - master before_script: - chmod +x ./build.sh script: diff --git a/build.sh b/build.sh index 458eaf13..d13da2cf 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,3 @@ dotnet build dotnet test test/Dommel.Tests +dotnet test test/Dommel.IntegrationTests diff --git a/test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs b/test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs index f483090c..f1944f8a 100644 --- a/test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs +++ b/test/Dommel.IntegrationTests/Databases/DatabaseDriver.cs @@ -12,8 +12,6 @@ namespace Dommel.IntegrationTests /// public abstract class DatabaseDriver { - protected static readonly bool IsAppVeyor = bool.TryParse(Environment.GetEnvironmentVariable("AppVeyor"), out var appVeyor) ? appVeyor : false; - public abstract string TempDbDatabaseName { get; } public virtual string DefaultDatabaseName => "dommeltests"; diff --git a/test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs b/test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs index 4daf2792..856c6467 100644 --- a/test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs +++ b/test/Dommel.IntegrationTests/Databases/MySqlDatabaseDriver.cs @@ -9,9 +9,15 @@ public class MySqlDatabaseDriver : DatabaseDriver { public override DbConnection GetConnection(string databaseName) { - var connectionString = IsAppVeyor - ? $"Server=localhost;Database={databaseName};Uid=root;Pwd=Password12!;" - : $"Server=localhost;Database={databaseName};Uid=dommeltest;Pwd=test;"; + var connectionString = $"Server=localhost;Database={databaseName};Uid=dommeltest;Pwd=test;"; + if (CI.IsAppVeyor) + { + connectionString = $"Server=localhost;Database={databaseName};Uid=root;Pwd=Password12!;"; + } + else if (CI.IsTravis) + { + connectionString = $"Server=localhost;Database={databaseName};Uid=root;Pwd=;"; + } return new MySqlConnection(connectionString); } diff --git a/test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs b/test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs index 7d6fc4ce..6777aa19 100644 --- a/test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs +++ b/test/Dommel.IntegrationTests/Databases/PostgresDatabaseDriver.cs @@ -10,9 +10,15 @@ public class PostgresDatabaseDriver : DatabaseDriver { public override DbConnection GetConnection(string databaseName) { - var connectionString = IsAppVeyor - ? $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=Password12!;" - : $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=root;"; + var connectionString = $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=root;"; + if (CI.IsAppVeyor) + { + connectionString = $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=Password12!;"; + } + else if (CI.IsTravis) + { + connectionString = $"Server=localhost;Port=5432;Database={databaseName};Uid=postgres;Pwd=;"; + } return new NpgsqlConnection(connectionString); } diff --git a/test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs b/test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs index 55b95f5b..f68cf755 100644 --- a/test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs +++ b/test/Dommel.IntegrationTests/Databases/SqlServerDatabaseDriver.cs @@ -9,7 +9,7 @@ public class SqlServerDatabaseDriver : DatabaseDriver { public override DbConnection GetConnection(string databaseName) { - var connectionString = IsAppVeyor + var connectionString = CI.IsAppVeyor ? $"Server=(local)\\SQL2016;Database={databaseName};User ID=sa;Password=Password12!" : $"Server=(LocalDb)\\mssqllocaldb;Database={databaseName};Integrated Security=True"; diff --git a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj index f111591c..36f196d9 100644 --- a/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj +++ b/test/Dommel.IntegrationTests/Dommel.IntegrationTests.csproj @@ -1,7 +1,6 @@  - net452;netcoreapp2.0;netcoreapp2.1 - netcoreapp2.0;netcoreapp2.1 + netcoreapp2.0;netcoreapp2.1 false latest diff --git a/test/Dommel.IntegrationTests/Infrastructure/CI.cs b/test/Dommel.IntegrationTests/Infrastructure/CI.cs new file mode 100644 index 00000000..596eed59 --- /dev/null +++ b/test/Dommel.IntegrationTests/Infrastructure/CI.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dommel.IntegrationTests +{ + internal static class CI + { + public static bool IsAppVeyor => EnvBool("APPVEYOR"); + + public static bool IsTravis => EnvBool("TRAVIS"); + + private static bool EnvBool(string env) => bool.TryParse(Environment.GetEnvironmentVariable(env), out var b) ? b : false; + } +} diff --git a/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs b/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs index 4e9125ea..5336606f 100644 --- a/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs +++ b/test/Dommel.IntegrationTests/Infrastructure/DatabaseTestData.cs @@ -9,7 +9,11 @@ public DatabaseTestData() // Defines the database providers to use for each test method. // These providers are used to initialize the databases in the // DatabaseFixture as well. - Add(new SqlServerDatabaseDriver()); + if (!CI.IsTravis) + { + Add(new SqlServerDatabaseDriver()); + } + Add(new MySqlDatabaseDriver()); Add(new PostgresDatabaseDriver()); }