From d13e1d253518c5ab0e13b11518dfafeae647009d Mon Sep 17 00:00:00 2001 From: Eben Date: Thu, 9 Feb 2023 14:35:40 +0200 Subject: [PATCH 01/37] - async --- README.md | 4 +- .../embedded-script.sql | 0 .../DataRepositoryFixture.cs | 26 +- .../DatabaseContextCacheFixture.cs | 7 +- .../DatabaseContextFactoryFixture.cs | 7 +- .../DatabaseContextFixture.cs | 39 +-- .../DatabaseGatewayFixture.cs | 171 ----------- .../DbCommandFactoryFixture.cs | 2 +- .../DbConnectionFactoryFixture.cs | 2 +- Shuttle.Core.Data.Tests/Fixture.cs | 10 +- .../MappedColumnFixture.cs | 274 ++++++++++-------- .../Mapping/DataRowMapperFixture.cs | 26 +- .../Mapping/QueryMapperFixture.cs | 25 +- .../ProcedureQueryFixture.cs | 2 +- Shuttle.Core.Data.Tests/RawQueryFixture.cs | 2 +- .../ScriptProviderFixture.cs | 12 +- .../ScriptProviderNegativeFixture.cs | 2 +- .../Shuttle.Core.Data.Tests.csproj | 4 +- Shuttle.Core.Data/ActiveDatabaseContext.cs | 8 +- .../ServiceCollectionExtensions.cs | 2 +- Shuttle.Core.Data/DataRepository.cs | 26 +- Shuttle.Core.Data/DatabaseContext.cs | 23 +- Shuttle.Core.Data/DatabaseContextCache.cs | 4 +- Shuttle.Core.Data/DatabaseContextFactory.cs | 24 +- Shuttle.Core.Data/DatabaseGateway.cs | 130 +++++---- Shuttle.Core.Data/DbCommandFactory.cs | 3 +- Shuttle.Core.Data/DbConnectionFactory.cs | 2 +- .../Extensions/DataRepositoryExtensions.cs | 127 +++++++- .../DatabaseContextCacheExtensions.cs | 42 +-- .../DatabaseContextFactoryExtensions.cs | 3 +- .../Extensions/DatabaseGatewayExtensions.cs | 153 +++++++++- .../Extensions/MappingExtensions.cs | 6 +- .../Extensions/QueryMapperExtensions.cs | 202 ++++++++++++- Shuttle.Core.Data/IDataRepository.cs | 12 +- Shuttle.Core.Data/IDatabaseContext.cs | 3 +- Shuttle.Core.Data/IDatabaseContextFactory.cs | 5 +- ...extCache.cs => IDatabaseContextService.cs} | 2 +- Shuttle.Core.Data/IDatabaseGateway.cs | 15 +- Shuttle.Core.Data/IDbCommandFactory.cs | 2 +- Shuttle.Core.Data/IDbConnectionFactory.cs | 3 +- Shuttle.Core.Data/IMappedColumn.cs | 4 + Shuttle.Core.Data/IQueryMapper.cs | 18 +- Shuttle.Core.Data/MappedColumn.cs | 91 +++++- Shuttle.Core.Data/QueryMapper.cs | 102 ++++--- Shuttle.Core.Data/ScriptProvider.cs | 10 +- Shuttle.Core.Data/Shuttle.Core.Data.csproj | 2 +- .../ThreadStaticDatabaseContextCache.cs | 8 +- 47 files changed, 997 insertions(+), 650 deletions(-) rename Shuttle.Core.Data.Tests/.scripts/{System.Data.SqlClient => Microsoft.Data.SqlClient}/embedded-script.sql (100%) delete mode 100644 Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs rename Shuttle.Core.Data/{IDatabaseContextCache.cs => IDatabaseContextService.cs} (88%) diff --git a/README.md b/README.md index 1cef5b9..ce6caa0 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,8 @@ using (var context = databaseContextFactory.Create("connection-name")) } using (var context = databaseContextFactory - .Create("System.Data.SqlClient", - "Data Source=.\sqlexpress;Initial Catalog=Shuttle;Integrated Security=SSPI")) + .Create("Microsoft.Data.SqlClient", + "Data Source=.\sqlexpress;Initial Catalog=Shuttle;Integrated Security=SSPI;TrustServerCertificate=true")) { // database interaction } diff --git a/Shuttle.Core.Data.Tests/.scripts/System.Data.SqlClient/embedded-script.sql b/Shuttle.Core.Data.Tests/.scripts/Microsoft.Data.SqlClient/embedded-script.sql similarity index 100% rename from Shuttle.Core.Data.Tests/.scripts/System.Data.SqlClient/embedded-script.sql rename to Shuttle.Core.Data.Tests/.scripts/Microsoft.Data.SqlClient/embedded-script.sql diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index bae5551..705f6b5 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Moq; using NUnit.Framework; @@ -18,12 +20,12 @@ public void Should_be_able_to_fetch_all_items() var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRows(query.Object)).Returns(new List {dataRow}); + gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchItems(query.Object).ToList(); + var result = repository.FetchItems(query.Object).Result.ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -39,12 +41,12 @@ public void Should_be_able_to_fetch_a_single_item() var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRow(query.Object)).Returns(dataRow); + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchItem(query.Object); + var result = repository.FetchItem(query.Object).Result; Assert.IsNotNull(result); Assert.AreSame(anObject, result); @@ -56,11 +58,11 @@ public void Should_be_able_to_get_default_when_fetching_a_single_item_that_is_no var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetRow(query.Object)).Returns((DataRow) null); + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); var repository = new DataRepository(gateway.Object, new Mock>().Object); - var result = repository.FetchItem(query.Object); + var result = repository.FetchItem(query.Object).Result; Assert.IsNull(result); } @@ -71,11 +73,11 @@ public void Should_be_able_to_call_contains() var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetScalar(query.Object)).Returns(1); + gateway.Setup(m => m.GetScalar(query.Object, CancellationToken.None)).ReturnsAsync(1); var repository = new DataRepository(gateway.Object, new Mock>().Object); - Assert.IsTrue(repository.Contains(query.Object)); + Assert.IsTrue(repository.Contains(query.Object).Result); } [Test] @@ -88,12 +90,12 @@ public void Should_be_able_to_fetch_mapped_rows() var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRows(query.Object)).Returns(new List {dataRow}); + gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchMappedRows(query.Object).ToList(); + var result = repository.FetchMappedRows(query.Object).Result.ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -111,12 +113,12 @@ public void Should_be_able_to_fetch_a_single_row() var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRow(query.Object)).Returns(dataRow); + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchMappedRow(query.Object); + var result = repository.FetchMappedRow(query.Object).Result; Assert.IsNotNull(result); Assert.AreSame(dataRow, result.Row); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs index 1b99dda..19e6f61 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Data.Common; using Moq; using NUnit.Framework; @@ -9,13 +10,13 @@ public class DatabaseContextCacheFixture : Fixture [Test] public void Should_be_able_to_use_different_contexts() { - var cache = new DatabaseContextCache(); + var cache = new DatabaseContextService(); - var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, cache).WithName("mock-1"); + var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, cache).WithName("mock-1"); Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, cache).WithName("mock-2"); + var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, cache).WithName("mock-2"); Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 61e05b3..5ad690d 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Data.Common; using System.Threading; using Microsoft.Extensions.Options; using Moq; @@ -41,17 +42,17 @@ public void Should_be_able_to_check_connection_availability() Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.True); Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.True); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.True); Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.True); databaseContextFactory.Setup(m => m.Create()).Throws(new Exception()); databaseContextFactory.Setup(m => m.Create(It.IsAny())).Throws(new Exception()); - databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.False); Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.False); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.False); Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.False); } } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index b9ad381..e4e8bca 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -1,6 +1,7 @@ using System; using System.Data; -using System.Data.SqlClient; +using System.Data.Common; +using Microsoft.Data.SqlClient; using Moq; using NUnit.Framework; @@ -14,7 +15,7 @@ public void Should_not_be_able_to_create_an_invalid_connection() { Assert.Throws(() => { - using (new DatabaseContext("System.Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextCache())) + using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) { } }); @@ -26,8 +27,8 @@ public void Should_not_be_able_to_create_a_non_existent_connection() Assert.Throws(() => { using ( - new DatabaseContext("System.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object, new DatabaseContextCache())) + new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), + new Mock().Object, new DatabaseContextService())) { } }); @@ -37,8 +38,8 @@ public void Should_not_be_able_to_create_a_non_existent_connection() public void Should_be_able_to_create_a_valid_connection() { using ( - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextCache())) + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) { } } @@ -48,8 +49,8 @@ public void Should_be_able_to_begin_and_commit_a_transaction() { using ( var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextCache())) + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) { connection.BeginTransaction(); connection.CommitTransaction(); @@ -61,8 +62,8 @@ public void Should_be_able_to_begin_and_rollback_a_transaction() { using ( var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextCache())) + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) { connection.BeginTransaction(); } @@ -73,8 +74,8 @@ public void Should_be_able_to_call_commit_without_a_transaction() { using ( var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextCache())) + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) { connection.CommitTransaction(); } @@ -85,8 +86,8 @@ public void Should_be_able_to_call_dispose_more_than_once() { using ( var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextCache())) + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) { connection.Dispose(); connection.Dispose(); @@ -97,16 +98,16 @@ public void Should_be_able_to_call_dispose_more_than_once() public void Should_be_able_to_create_a_command() { var dbCommandFactory = new Mock(); - var dbConnection = GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString); + var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); var query = new Mock(); - var dbCommand = new Mock(); + var dbCommand = new Mock(); - dbCommandFactory.Setup(m => m.CreateCommandUsing(dbConnection, query.Object)).Returns(dbCommand.Object); + dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); using ( - var connection = new DatabaseContext("System.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextCache())) + var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) { - connection.CreateCommandToExecute(query.Object); + connection.CreateCommand(query.Object); } dbCommandFactory.VerifyAll(); diff --git a/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs b/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs deleted file mode 100644 index fb89cb5..0000000 --- a/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System.Collections.Generic; -using System.Data; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; - -namespace Shuttle.Core.Data.Tests -{ - [TestFixture] - public class DatabaseGatewayFixture : Fixture - { - protected IDatabaseContext GetDatabaseContext(Mock command) - { - var commandFactory = new Mock(); - - commandFactory.Setup(m => m.CreateCommandUsing(It.IsAny(), It.IsAny())).Returns(command.Object); - - return new DatabaseContextFactory(Provider.GetRequiredService>(), Options.Create(new DataAccessOptions()), GetDbConnectionFactory(), commandFactory.Object, new ThreadStaticDatabaseContextCache()).Create(DefaultConnectionStringName); - } - - [Test] - public void Should_be_able_to_get_a_data_table() - { - var query = new Mock(); - var command = CommandMock(); - - command.Setup(m => m.ExecuteReader()).Returns(DataTableReader(2)); - - var gateway = GetDatabaseGateway(); - - using (GetDatabaseContext(command)) - { - var table = gateway.GetDataTable(query.Object); - - Assert.IsNotNull(table); - Assert.AreEqual(2, table.Rows.Count); - Assert.AreEqual("row-1", table.Rows[0][0]); - Assert.AreEqual("row-2", table.Rows[1][0]); - } - } - - [Test] - public void Should_be_able_to_get_rows() - { - var query = new Mock(); - - var command = CommandMock(); - - command.Setup(m => m.ExecuteReader()).Returns(DataTableReader(2)); - - var gateway = GetDatabaseGateway(); - - using (GetDatabaseContext(command)) - { - var rows = gateway.GetRows(query.Object).ToList(); - - Assert.IsNotNull(rows); - Assert.AreEqual(2, rows.Count); - Assert.AreEqual("row-1", rows[0][0]); - Assert.AreEqual("row-2", rows[1][0]); - } - } - - [Test] - public void Should_be_able_to_get_single_row() - { - var query = new Mock(); - var command = CommandMock(); - - command.Setup(m => m.ExecuteReader()).Returns(DataTableReader(2)); - - var gateway = GetDatabaseGateway(); - - using (GetDatabaseContext(command)) - { - var row = gateway.GetRow(query.Object); - - Assert.IsNotNull(row); - Assert.AreEqual("row-1", row[0]); - } - } - - [Test] - public void Should_be_able_to_get_null_for_single_row_if_there_is_no_data() - { - var query = new Mock(); - var command = CommandMock(); - - command.Setup(m => m.ExecuteReader()).Returns(DataTableReader(0)); - - var gateway = GetDatabaseGateway(); - - using (GetDatabaseContext(command)) - { - Assert.IsNull(gateway.GetRow(query.Object)); - } - } - - [Test] - public void Should_be_able_to_execute_a_query() - { - var query = new Mock(); - var command = CommandMock(); - - command.Setup(m => m.ExecuteNonQuery()).Returns(1); - - var gateway = GetDatabaseGateway(); - - using (GetDatabaseContext(command)) - { - var result = gateway.Execute(query.Object); - - Assert.IsNotNull(result); - Assert.AreEqual(1, result); - } - } - - [Test] - public void Should_be_able_to_get_a_scalar() - { - var query = new Mock(); - var command = CommandMock(); - - command.Setup(m => m.ExecuteScalar()).Returns(10); - - var gateway = GetDatabaseGateway(); - - using (GetDatabaseContext(command)) - { - var result = gateway.GetScalar(query.Object); - - Assert.IsNotNull(result); - Assert.AreEqual(10, result); - } - } - - private DataTableReader DataTableReader(int rowCount) - { - var table = new DataTable(); - - table.Columns.Add("object"); - - for (var i = 0; i < rowCount; i++) - { - table.Rows.Add(string.Concat("row-", i + 1)); - } - - return new DataTableReader(table); - } - - private Mock CommandMock() - { - var dbCommand = new Mock(); - var dataParameterCollection = new Mock(); - var dataParameter = new Mock(); - - dataParameter.Setup(m => m.ParameterName).Returns("some-parameter"); - dataParameter.Setup(m => m.Value).Returns("some-value"); - dataParameterCollection.Setup(m => m.GetEnumerator()) - .Returns(new List {dataParameter.Object}.GetEnumerator()); - - dbCommand.Setup(m => m.CommandType).Returns(CommandType.Text); - dbCommand.Setup(m => m.CommandText).Returns("some-sql"); - dbCommand.Setup(m => m.Parameters).Returns(dataParameterCollection.Object); - - return dbCommand; - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DbCommandFactoryFixture.cs b/Shuttle.Core.Data.Tests/DbCommandFactoryFixture.cs index ddd516c..ddf458a 100644 --- a/Shuttle.Core.Data.Tests/DbCommandFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DbCommandFactoryFixture.cs @@ -21,7 +21,7 @@ public void Should_be_able_to_create_a_command() connection.Setup(m => m.CreateCommand()).Returns(command.Object); query.Setup(m => m.Prepare(command.Object)); - var result = factory.CreateCommandUsing(connection.Object, query.Object); + var result = factory.Create(connection.Object, query.Object); connection.VerifyAll(); query.VerifyAll(); diff --git a/Shuttle.Core.Data.Tests/DbConnectionFactoryFixture.cs b/Shuttle.Core.Data.Tests/DbConnectionFactoryFixture.cs index c896d39..5edca77 100644 --- a/Shuttle.Core.Data.Tests/DbConnectionFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DbConnectionFactoryFixture.cs @@ -8,7 +8,7 @@ public class DbConnectionFactoryFixture : Fixture [Test] public void Should_be_able_to_create_a_valid_connection() { - using (var connection = GetDbConnectionFactory().CreateConnection(DefaultProviderName, DefaultConnectionString)) + using (var connection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString)) { Assert.IsNotNull(connection); } diff --git a/Shuttle.Core.Data.Tests/Fixture.cs b/Shuttle.Core.Data.Tests/Fixture.cs index 22a0a80..dcae96f 100644 --- a/Shuttle.Core.Data.Tests/Fixture.cs +++ b/Shuttle.Core.Data.Tests/Fixture.cs @@ -1,10 +1,8 @@ using System; -using System.Data; using System.Data.Common; -using System.Data.SqlClient; +using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Moq; using NUnit.Framework; namespace Shuttle.Core.Data.Tests @@ -16,12 +14,12 @@ public abstract class Fixture protected static IServiceProvider Provider; protected static string DefaultConnectionStringName = "Shuttle"; - protected static string DefaultProviderName = "System.Data.SqlClient"; - protected static string DefaultConnectionString = "Server=.;Database=Shuttle;User ID=sa;Password=Pass!000"; + protected static string DefaultProviderName = "Microsoft.Data.SqlClient"; + protected static string DefaultConnectionString = "Server=.;Database=Shuttle;User ID=sa;Password=Pass!000;TrustServerCertificate=true"; protected Fixture() { - DbProviderFactories.RegisterFactory("System.Data.SqlClient", SqlClientFactory.Instance); + DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", SqlClientFactory.Instance); Services.AddDataAccess(builder => { diff --git a/Shuttle.Core.Data.Tests/MappedColumnFixture.cs b/Shuttle.Core.Data.Tests/MappedColumnFixture.cs index 8ad1246..214846a 100644 --- a/Shuttle.Core.Data.Tests/MappedColumnFixture.cs +++ b/Shuttle.Core.Data.Tests/MappedColumnFixture.cs @@ -1,188 +1,208 @@ using System; using System.Data; using System.Data.SqlTypes; +using Microsoft.Data.SqlClient.Server; using Microsoft.SqlServer.Server; using Moq; using NUnit.Framework; namespace Shuttle.Core.Data.Tests { - [TestFixture] - public class MappedColumnFixture : Fixture - { - [Test] - public void Should_be_able_to_create_mapped_columns() - { - const string columnName = "name"; + [TestFixture] + public class MappedColumnFixture : Fixture + { + [Test] + public void Should_be_able_to_create_mapped_columns() + { + const string columnName = "name"; - var mcGuid = new MappedColumn(columnName, DbType.Guid); + var mcGuid = new MappedColumn(columnName, DbType.Guid); - Assert.AreEqual(columnName, mcGuid.ColumnName); - Assert.AreEqual(DbType.Guid, mcGuid.DbType); - Assert.IsNull(mcGuid.Size); - Assert.IsNull(mcGuid.Precision); - Assert.IsNull(mcGuid.Scale); + Assert.AreEqual(columnName, mcGuid.ColumnName); + Assert.AreEqual(DbType.Guid, mcGuid.DbType); + Assert.IsNull(mcGuid.Size); + Assert.IsNull(mcGuid.Precision); + Assert.IsNull(mcGuid.Scale); - var mcString = new MappedColumn(columnName, DbType.AnsiString, 65); + var mcString = new MappedColumn(columnName, DbType.AnsiString, 65); - Assert.AreEqual(columnName, mcString.ColumnName); - Assert.AreEqual(DbType.AnsiString, mcString.DbType); - Assert.AreEqual(65, mcString.Size); - Assert.IsNull(mcString.Precision); - Assert.IsNull(mcString.Scale); + Assert.AreEqual(columnName, mcString.ColumnName); + Assert.AreEqual(DbType.AnsiString, mcString.DbType); + Assert.AreEqual(65, mcString.Size); + Assert.IsNull(mcString.Precision); + Assert.IsNull(mcString.Scale); - var mcDouble = new MappedColumn(columnName, DbType.Decimal, 10, 2); + var mcDouble = new MappedColumn(columnName, DbType.Decimal, 10, 2); - Assert.AreEqual(columnName, mcDouble.ColumnName); - Assert.AreEqual(DbType.Decimal, mcDouble.DbType); - Assert.IsNull(mcDouble.Size); - Assert.AreEqual((byte)10, mcDouble.Precision); - Assert.AreEqual((byte)2, mcDouble.Scale); - } + Assert.AreEqual(columnName, mcDouble.ColumnName); + Assert.AreEqual(DbType.Decimal, mcDouble.DbType); + Assert.IsNull(mcDouble.Size); + Assert.AreEqual((byte)10, mcDouble.Precision); + Assert.AreEqual((byte)2, mcDouble.Scale); + } - [Test] - public void Should_be_able_to_create_data_parameters() - { - const string columnName = "name"; + [Test] + public void Should_be_able_to_create_data_parameters() + { + const string columnName = "name"; - var parameter = new Mock(); + var parameter = new Mock(); - var mcGuid = new MappedColumn(columnName, DbType.Guid); + var mcGuid = new MappedColumn(columnName, DbType.Guid); - var guid = Guid.NewGuid(); + var guid = Guid.NewGuid(); - var factory = new Mock(); + var factory = new Mock(); - factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); + factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); - var result = mcGuid.CreateDataParameter(factory.Object, guid); + var result = mcGuid.CreateDataParameter(factory.Object, guid); - factory.VerifyAll(); - Assert.AreSame(result, parameter.Object); + factory.VerifyAll(); + Assert.AreSame(result, parameter.Object); - var mcString = new MappedColumn(columnName, DbType.AnsiString, 65); + var mcString = new MappedColumn(columnName, DbType.AnsiString, 65); - factory = new Mock(); - factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); + factory = new Mock(); + factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); - result = mcString.CreateDataParameter(factory.Object, "a-string"); + result = mcString.CreateDataParameter(factory.Object, "a-string"); - factory.VerifyAll(); - Assert.AreSame(result, parameter.Object); + factory.VerifyAll(); + Assert.AreSame(result, parameter.Object); - var mcDouble = new MappedColumn(columnName, DbType.Decimal, 10, 2); + var mcDouble = new MappedColumn(columnName, DbType.Decimal, 10, 2); - factory = new Mock(); - factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); + factory = new Mock(); + factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); - result = mcDouble.CreateDataParameter(factory.Object, 150.15d); + result = mcDouble.CreateDataParameter(factory.Object, 150.15d); - factory.VerifyAll(); - Assert.AreSame(result, parameter.Object); - } + factory.VerifyAll(); + Assert.AreSame(result, parameter.Object); + } - [Test] - public void Should_be_able_to_determine_if_column_value_is_null() - { - var table = new DataTable(); + [Test] + public void Should_be_able_to_determine_if_column_value_is_null() + { + var table = new DataTable(); - table.Columns.Add("column-1", typeof(string)); + table.Columns.Add("column-1", typeof(string)); - var row = table.Rows.Add(DBNull.Value); + var row = table.Rows.Add(DBNull.Value); - var mc = new MappedColumn("column-1", DbType.AnsiString, 65); + var mc = new MappedColumn("column-1", DbType.AnsiString, 65); - Assert.IsTrue(mc.IsNullFor(row)); + Assert.IsTrue(mc.IsNullFor(row)); - row["column-1"] = "value-1"; + row["column-1"] = "value-1"; - Assert.IsFalse(mc.IsNullFor(row)); - } + Assert.IsFalse(mc.IsNullFor(row)); + } - [Test] - public void Should_be_able_to_map_from_a_data_row() - { - var table = new DataTable(); + [Test] + public void Should_be_able_to_map_from_a_dynamic_instance() + { + var instanceA = new { column1 = (string)null }; - table.Columns.Add("column-1", typeof(string)); + var mc = new MappedColumn("column1", DbType.AnsiString, 65); + var missing = new MappedColumn("missing", DbType.AnsiString, 65); - var row = table.Rows.Add(DBNull.Value); + Assert.AreEqual(default(string), mc.MapFrom(instanceA)); + Assert.AreEqual(default(string), missing.MapFrom(instanceA)); + Assert.AreEqual(null, mc.RetrieveRawValueFrom(instanceA)); - var mc = new MappedColumn("column-1", DbType.AnsiString, 65); - var missing = new MappedColumn("missing", DbType.AnsiString, 65); + var instanceB = new { column1 = "value-1" }; - Assert.AreEqual(default(string), mc.MapFrom(row)); - Assert.AreEqual(default(string), missing.MapFrom(row)); - Assert.AreEqual(DBNull.Value, mc.RetrieveRawValueFrom(row)); + Assert.AreEqual("value-1", mc.MapFrom(instanceB)); + Assert.AreEqual(default(string), missing.MapFrom(instanceB)); + Assert.AreEqual("value-1", mc.RetrieveRawValueFrom(instanceB)); + } - row["column-1"] = "value-1"; + [Test] + public void Should_be_able_to_map_from_a_data_row() + { + var table = new DataTable(); - Assert.AreEqual("value-1", mc.MapFrom(row)); - Assert.AreEqual(default(string), missing.MapFrom(row)); - Assert.AreEqual("value-1", mc.RetrieveRawValueFrom(row)); - } + table.Columns.Add("column-1", typeof(string)); - [Test] - public void Should_be_able_to_map_from_a_data_record() - { - var record = new SqlDataRecord(new[] { new SqlMetaData("column-1", SqlDbType.VarChar, 65) }); + var row = table.Rows.Add(DBNull.Value); - record.SetSqlString(0, new SqlString(null)); + var mc = new MappedColumn("column-1", DbType.AnsiString, 65); + var missing = new MappedColumn("missing", DbType.AnsiString, 65); - var column1 = new MappedColumn("column-1", DbType.AnsiString, 65); - var column2 = new MappedColumn("column-2", DbType.AnsiString, 65); + Assert.AreEqual(default(string), mc.MapFrom(row)); + Assert.AreEqual(default(string), missing.MapFrom(row)); + Assert.AreEqual(DBNull.Value, mc.RetrieveRawValueFrom(row)); - Assert.AreEqual(default(string), column1.MapFrom(record)); - Assert.AreEqual(default(string), column2.MapFrom(record)); + row["column-1"] = "value-1"; - record.SetSqlString(0, new SqlString("value-1")); + Assert.AreEqual("value-1", mc.MapFrom(row)); + Assert.AreEqual(default(string), missing.MapFrom(row)); + Assert.AreEqual("value-1", mc.RetrieveRawValueFrom(row)); + } - Assert.AreEqual("value-1", column1.MapFrom(record)); - Assert.AreEqual(default(string), column2.MapFrom(record)); - } + [Test] + public void Should_be_able_to_map_from_a_data_record() + { + var record = new SqlDataRecord(new SqlMetaData("column-1", SqlDbType.VarChar, 65)); - [Test] - public void Should_be_able_to_rename_a_mapped_column() - { - var column1Guid = new MappedColumn("column-1", DbType.Guid); - var column2Guid = column1Guid.Rename("column-2"); + record.SetSqlString(0, new SqlString(null)); - Assert.AreEqual("column-1", column1Guid.ColumnName); - Assert.AreEqual(DbType.Guid, column1Guid.DbType); - Assert.AreEqual("column-2", column2Guid.ColumnName); - Assert.AreEqual(DbType.Guid, column2Guid.DbType); + var column1 = new MappedColumn("column-1", DbType.AnsiString, 65); + var column2 = new MappedColumn("column-2", DbType.AnsiString, 65); - var column1String = new MappedColumn("column-1", DbType.AnsiString, 65); - var column2String = column1String.Rename("column-2"); + Assert.AreEqual(default(string), column1.MapFrom(record)); + Assert.AreEqual(default(string), column2.MapFrom(record)); - Assert.AreEqual("column-1", column1String.ColumnName); - Assert.AreEqual(DbType.AnsiString, column1String.DbType); - Assert.AreEqual(65, column1String.Size); - Assert.AreEqual("column-2", column2String.ColumnName); - Assert.AreEqual(DbType.AnsiString, column2String.DbType); - Assert.AreEqual(65, column2String.Size); + record.SetSqlString(0, new SqlString("value-1")); - var column1Double = new MappedColumn("column-1", DbType.Double, 10, 2); - var column2Double = column1Double.Rename("column-2"); + Assert.AreEqual("value-1", column1.MapFrom(record)); + Assert.AreEqual(default(string), column2.MapFrom(record)); + } - Assert.AreEqual("column-1", column1Double.ColumnName); - Assert.AreEqual(DbType.Double, column1Double.DbType); - Assert.AreEqual((byte)10, column1Double.Precision); - Assert.AreEqual((byte)2, column1Double.Scale); - Assert.AreEqual("column-2", column2Double.ColumnName); - Assert.AreEqual(DbType.Double, column2Double.DbType); - Assert.AreEqual((byte)10, column2Double.Precision); - Assert.AreEqual((byte)2, column2Double.Scale); - } + [Test] + public void Should_be_able_to_rename_a_mapped_column() + { + var column1Guid = new MappedColumn("column-1", DbType.Guid); + var column2Guid = column1Guid.Rename("column-2"); - [Test] - public void Should_be_able_to_implicitly_convert_to_string() - { - var mc = new MappedColumn("column-1", DbType.AnsiString, 65); + Assert.AreEqual("column-1", column1Guid.ColumnName); + Assert.AreEqual(DbType.Guid, column1Guid.DbType); + Assert.AreEqual("column-2", column2Guid.ColumnName); + Assert.AreEqual(DbType.Guid, column2Guid.DbType); - string name = mc; + var column1String = new MappedColumn("column-1", DbType.AnsiString, 65); + var column2String = column1String.Rename("column-2"); - Assert.AreEqual("column-1", name); - } - } + Assert.AreEqual("column-1", column1String.ColumnName); + Assert.AreEqual(DbType.AnsiString, column1String.DbType); + Assert.AreEqual(65, column1String.Size); + Assert.AreEqual("column-2", column2String.ColumnName); + Assert.AreEqual(DbType.AnsiString, column2String.DbType); + Assert.AreEqual(65, column2String.Size); + + var column1Double = new MappedColumn("column-1", DbType.Double, 10, 2); + var column2Double = column1Double.Rename("column-2"); + + Assert.AreEqual("column-1", column1Double.ColumnName); + Assert.AreEqual(DbType.Double, column1Double.DbType); + Assert.AreEqual((byte)10, column1Double.Precision); + Assert.AreEqual((byte)2, column1Double.Scale); + Assert.AreEqual("column-2", column2Double.ColumnName); + Assert.AreEqual(DbType.Double, column2Double.DbType); + Assert.AreEqual((byte)10, column2Double.Precision); + Assert.AreEqual((byte)2, column2Double.Scale); + } + + [Test] + public void Should_be_able_to_implicitly_convert_to_string() + { + var mc = new MappedColumn("column-1", DbType.AnsiString, 65); + + string name = mc; + + Assert.AreEqual("column-1", name); + } + } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 36ae62c..27fa112 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -33,14 +33,14 @@ select top 1 using (GetDatabaseContext()) { - var item = dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery)); - var items = dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery)); + var item = dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery).Result); + var items = dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery).Result); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery)); - var mappedRows = dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery)); + var mappedRow = dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery).Result); + var mappedRows = dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery).Result); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -73,14 +73,14 @@ Age as TheAge using (GetDatabaseContext()) { - var item = dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery)); - var items = dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery)); + var item = dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery).Result); + var items = dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery).Result); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery)); - var mappedRows = dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery)); + var mappedRow = dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery).Result); + var mappedRows = dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery).Result); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -109,8 +109,8 @@ select top 1 using (GetDatabaseContext()) { - var value = dataRowMapper.MapValue(databaseGateway.GetRow(rowQuery)); - var values = dataRowMapper.MapValues(databaseGateway.GetRows(rowsQuery)); + var value = dataRowMapper.MapValue(databaseGateway.GetRow(rowQuery).Result); + var values = dataRowMapper.MapValues(databaseGateway.GetRows(rowsQuery).Result); Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -148,15 +148,15 @@ public void Should_be_able_to_perform_dynamic_mapping() using (GetDatabaseContext()) { - var item = dataRowMapper.MapItem(databaseGateway.GetRow(rowQuery)); + var item = dataRowMapper.MapItem(databaseGateway.GetRow(rowQuery).Result); Assert.IsNotNull(item); - var items = dataRowMapper.MapItems(databaseGateway.GetRows(rowsQuery)); + var items = dataRowMapper.MapItems(databaseGateway.GetRows(rowsQuery).Result); Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id }))); + item = dataRowMapper.MapItem(databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id })).Result); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index 9782d2f..f468a4e 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading; using NUnit.Framework; namespace Shuttle.Core.Data.Tests @@ -32,14 +33,14 @@ select top 1 using (GetDatabaseContext()) { - var item = mapper.MapObject(queryRow); - var items = mapper.MapObjects(queryRows); + var item = mapper.MapObject(queryRow).Result; + var items = mapper.MapObjects(queryRows).Result; Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRow(queryRow); - var mappedRows = mapper.MapRows(queryRows); + var mappedRow = mapper.MapRow(queryRow).Result; + var mappedRows = mapper.MapRows(queryRows).Result; Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -71,14 +72,14 @@ Age as TheAge using (GetDatabaseContext()) { - var item = mapper.MapObject(queryRow); - var items = mapper.MapObjects(queryRows); + var item = mapper.MapObject(queryRow).Result; + var items = mapper.MapObjects(queryRows).Result; Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRow(queryRow); - var mappedRows = mapper.MapRows(queryRows); + var mappedRow = mapper.MapRow(queryRow).Result; + var mappedRows = mapper.MapRows(queryRows).Result; Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -106,8 +107,8 @@ select top 1 using (GetDatabaseContext()) { - var value = mapper.MapValue(queryRow); - var values = mapper.MapValues(queryRows); + var value = mapper.MapValue(queryRow).Result; + var values = mapper.MapValues(queryRows).Result; Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -140,8 +141,8 @@ select top 1 using (GetDatabaseContext()) { - var item = queryMapper.MapItem(queryRow); - var items = queryMapper.MapItems(queryRows); + var item = queryMapper.MapItem(queryRow).Result; + var items = queryMapper.MapItems(queryRows).Result; Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); diff --git a/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs b/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs index d227d90..29e3880 100644 --- a/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs @@ -1,6 +1,6 @@ using System; using System.Data; -using System.Data.SqlClient; +using Microsoft.Data.SqlClient; using Moq; using NUnit.Framework; diff --git a/Shuttle.Core.Data.Tests/RawQueryFixture.cs b/Shuttle.Core.Data.Tests/RawQueryFixture.cs index 679ea3e..0ed0824 100644 --- a/Shuttle.Core.Data.Tests/RawQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/RawQueryFixture.cs @@ -1,6 +1,6 @@ using System; using System.Data; -using System.Data.SqlClient; +using Microsoft.Data.SqlClient; using Moq; using NUnit.Framework; diff --git a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs index 73591ee..acd7422 100644 --- a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs +++ b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs @@ -11,7 +11,7 @@ public class ScriptProviderFixture : Fixture [SetUp] public void SetupContext() { - var cache = new Mock(); + var cache = new Mock(); cache.Setup(m => m.Current).Returns(new Mock().Object); } @@ -20,7 +20,7 @@ public void SetupContext() public void Should_fail_when_there_is_no_ambient_database_context() { Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextCache()).Get("throw")); + () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); } [Test] @@ -38,9 +38,9 @@ public void Should_eb_able_to_retrieve_script_from_file() Assert.AreEqual("select 'file-script'", script); } - private static IDatabaseContextCache MockDatabaseContextCache() + private static IDatabaseContextService MockDatabaseContextCache() { - var databaseContextCache = new Mock(); + var databaseContextCache = new Mock(); databaseContextCache.Setup(m => m.Current).Returns(new Mock().Object); @@ -53,7 +53,7 @@ public void Should_eb_able_to_retrieve_script_from_resource() var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly, - ResourceNameFormat = "Shuttle.Core.Data.Tests..scripts.System.Data.SqlClient.{ScriptName}.sql" + ResourceNameFormat = "Shuttle.Core.Data.Tests..scripts.Microsoft.Data.SqlClient.{ScriptName}.sql" }), MockDatabaseContextCache()); var script = provider.Get("embedded-script"); @@ -70,7 +70,7 @@ public void Should_throw_exception_when_no_resource_or_file_found() ResourceAssembly = GetType().Assembly }), MockDatabaseContextCache()); - Assert.Throws(() => provider.Get("System.Data.SqlClient", "missing-script")); + Assert.Throws(() => provider.Get("Microsoft.Data.SqlClient", "missing-script")); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs index 1cfbc9f..0be7b18 100644 --- a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs +++ b/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs @@ -11,7 +11,7 @@ public class ScriptProviderNegativeFixture : Fixture public void Should_fail_when_there_is_no_ambient_database_context() { Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextCache()).Get("throw")); + () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj index 709a4e7..fe04e5b 100644 --- a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj +++ b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj @@ -11,17 +11,17 @@ Always - + Never + - diff --git a/Shuttle.Core.Data/ActiveDatabaseContext.cs b/Shuttle.Core.Data/ActiveDatabaseContext.cs index 8826358..b80b230 100644 --- a/Shuttle.Core.Data/ActiveDatabaseContext.cs +++ b/Shuttle.Core.Data/ActiveDatabaseContext.cs @@ -5,12 +5,12 @@ namespace Shuttle.Core.Data { public class ActiveDatabaseContext : IDisposable { - private readonly IDatabaseContextCache _databaseContextCache; + private readonly IDatabaseContextService _databaseContextService; private readonly IDatabaseContext _current; - public ActiveDatabaseContext(IDatabaseContextCache databaseContextCache, IDatabaseContext current) + public ActiveDatabaseContext(IDatabaseContextService databaseContextService, IDatabaseContext current) { - _databaseContextCache = Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); _current = current; } @@ -21,7 +21,7 @@ public void Dispose() return; } - _databaseContextCache.Use(_current); + _databaseContextService.Use(_current); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs index 25c838a..2d8d81c 100644 --- a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs @@ -25,7 +25,7 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, options.DatabaseContextFactory = dataAccessBuilder.Options.DatabaseContextFactory; }); - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index c6e4512..3146b2e 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data @@ -15,33 +17,37 @@ public DataRepository(IDatabaseGateway databaseGateway, IDataRowMapper dataRo _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public IEnumerable FetchItems(IQuery query) + public async Task> FetchItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - return _databaseGateway.GetRows(query).MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList(); + var rows = await _databaseGateway.GetRows(query, cancellationToken); + + return await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList()); } - public T FetchItem(IQuery query) + public async Task FetchItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - var row = _databaseGateway.GetRow(query); + var row = await _databaseGateway.GetRow(query, cancellationToken); return row == null ? default : _dataRowMapper.Map(row).Result; } - public MappedRow FetchMappedRow(IQuery query) + public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - var row = _databaseGateway.GetRow(query); + var row = await _databaseGateway.GetRow(query, cancellationToken); return row == null ? null : _dataRowMapper.Map(row); } - public IEnumerable> FetchMappedRows(IQuery query) + public async Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - return _databaseGateway.GetRows(query).MappedRowsUsing(_dataRowMapper); + var rows = await _databaseGateway.GetRows(query, cancellationToken); + + return rows.MappedRowsUsing(_dataRowMapper); } - public bool Contains(IQuery query) + public async Task Contains(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - return _databaseGateway.GetScalar(query) == 1; + return await _databaseGateway.GetScalar(query, cancellationToken) == 1; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 6ea98aa..8d2b350 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -1,22 +1,23 @@ using System; using System.Data; +using System.Data.Common; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public class DatabaseContext : IDatabaseContext { - private readonly IDatabaseContextCache _databaseContextCache; + private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; private bool _dispose; private bool _disposed; private readonly IDatabaseContext _activeContext; public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, - IDatabaseContextCache databaseContextCache) + IDatabaseContextService databaseContextService) { _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); - _databaseContextCache = Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); _dispose = true; Key = Guid.NewGuid(); @@ -29,9 +30,9 @@ public DatabaseContext(string providerName, IDbConnection dbConnection, IDbComma Connection.Open(); } - _activeContext = databaseContextCache.HasCurrent ? databaseContextCache.Current : null; + _activeContext = databaseContextService.HasCurrent ? databaseContextService.Current : null; - _databaseContextCache.Add(this); + _databaseContextService.Add(this); } public Guid Key { get; } @@ -49,7 +50,7 @@ public IDatabaseContext WithName(string name) public IDatabaseContext Suppressed() { - return new DatabaseContext(ProviderName, Connection, _dbCommandFactory, _databaseContextCache) + return new DatabaseContext(ProviderName, Connection, _dbCommandFactory, _databaseContextService) { Transaction = Transaction, _dispose = false @@ -63,9 +64,9 @@ public IDatabaseContext SuppressDispose() return this; } - public IDbCommand CreateCommandToExecute(IQuery query) + public IDbCommand CreateCommand(IQuery query) { - var command = _dbCommandFactory.CreateCommandUsing(Connection, Guard.AgainstNull(query, nameof(query))); + var command = _dbCommandFactory.Create(Connection, Guard.AgainstNull(query, nameof(query))); command.Transaction = Transaction; return command; } @@ -100,11 +101,11 @@ public void CommitTransaction() public void Dispose() { - _databaseContextCache.Remove(this); + _databaseContextService.Remove(this); - if (_activeContext != null && _databaseContextCache.Contains(_activeContext)) + if (_activeContext != null && _databaseContextService.Contains(_activeContext)) { - _databaseContextCache.Use(_activeContext); + _databaseContextService.Use(_activeContext); } Dispose(true); diff --git a/Shuttle.Core.Data/DatabaseContextCache.cs b/Shuttle.Core.Data/DatabaseContextCache.cs index e8ddc54..d9d2a9c 100644 --- a/Shuttle.Core.Data/DatabaseContextCache.cs +++ b/Shuttle.Core.Data/DatabaseContextCache.cs @@ -4,12 +4,12 @@ namespace Shuttle.Core.Data { - public class DatabaseContextCache : IDatabaseContextCache + public class DatabaseContextService : IDatabaseContextService { private readonly List _databaseContexts = new List(); private IDatabaseContext _current; - public DatabaseContextCache() + public DatabaseContextService() { _current = null; } diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index f091552..8d8153c 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -1,5 +1,5 @@ using System; -using System.Data; +using System.Data.Common; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; @@ -12,7 +12,7 @@ public class DatabaseContextFactory : IDatabaseContextFactory public DatabaseContextFactory(IOptionsMonitor connectionStringOptions, IOptions dataAccessOptions, IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory, - IDatabaseContextCache databaseContextCache) + IDatabaseContextService databaseContextService) { Guard.AgainstNull(dataAccessOptions, nameof(dataAccessOptions)); @@ -21,7 +21,7 @@ public DatabaseContextFactory(IOptionsMonitor connectio DbConnectionFactory = Guard.AgainstNull(dbConnectionFactory, nameof(dbConnectionFactory)); DbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); - DatabaseContextCache = Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } public IDatabaseContext Create(string name) @@ -38,24 +38,24 @@ public IDatabaseContext Create(string name) public IDatabaseContext Create(string providerName, string connectionString) { - return DatabaseContextCache.ContainsConnectionString(connectionString) - ? DatabaseContextCache.GetConnectionString(connectionString).Suppressed() - : new DatabaseContext(providerName, DbConnectionFactory.CreateConnection(providerName, connectionString), - DbCommandFactory, DatabaseContextCache); + return DatabaseContextService.ContainsConnectionString(connectionString) + ? DatabaseContextService.GetConnectionString(connectionString).Suppressed() + : new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), + DbCommandFactory, DatabaseContextService); } - public IDatabaseContext Create(string providerName, IDbConnection dbConnection) + public IDatabaseContext Create(string providerName, DbConnection dbConnection) { Guard.AgainstNull(dbConnection, nameof(dbConnection)); - return DatabaseContextCache.ContainsConnectionString(dbConnection.ConnectionString) - ? DatabaseContextCache.GetConnectionString(dbConnection.ConnectionString).Suppressed() - : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextCache); + return DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) + ? DatabaseContextService.GetConnectionString(dbConnection.ConnectionString).Suppressed() + : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService); } public IDbConnectionFactory DbConnectionFactory { get; } public IDbCommandFactory DbCommandFactory { get; } - public IDatabaseContextCache DatabaseContextCache { get; } + public IDatabaseContextService DatabaseContextService { get; } public IDatabaseContext Create() { diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index f92ed28..212195a 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -1,94 +1,96 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { - public class DatabaseGateway : IDatabaseGateway - { - private readonly IDatabaseContextCache _databaseContextCache; - - public DatabaseGateway(IDatabaseContextCache databaseContextCache) - { - _databaseContextCache = Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + public class DatabaseGateway : IDatabaseGateway + { + private readonly IDatabaseContextService _databaseContextService; + + public DatabaseGateway(IDatabaseContextService databaseContextService) + { + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public DataTable GetDataTable(IQuery query) - { + public async Task GetDataTable(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + { Guard.AgainstNull(query, nameof(query)); - using (var reader = GetReader(query)) - { - var results = new DataTable(); + using var reader = GetReader(query, cancellationToken); - if (reader != null) - { - results.Load(reader); - } + var results = new DataTable(); - return results; - } - } + if (reader != null) + { + results.Load(await reader); + } - public IEnumerable GetRows(IQuery query) - { - return GetDataTable(query).Rows.Cast(); - } + return results; + } - public DataRow GetRow(IQuery query) - { - var table = GetDataTable(query); + public Task> GetRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + { + return Task.FromResult(GetDataTable(query, cancellationToken).Result.Rows.Cast()); + } - if ((table == null) || (table.Rows.Count == 0)) - { - return null; - } + public async Task GetRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + { + var table = await GetDataTable(query, cancellationToken); - return table.Rows[0]; - } + if (table == null || table.Rows.Count == 0) + { + return null; + } - public event EventHandler DbCommandCreated = delegate - { - }; + return table.Rows[0]; + } - public IDataReader GetReader(IQuery query) - { - Guard.AgainstNull(query, nameof(query)); + public event EventHandler DbCommandCreated = delegate + { + }; - using (var command = _databaseContextCache.Current.CreateCommandToExecute(query)) - { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + public async Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + { + Guard.AgainstNull(query, nameof(query)); - return command.ExecuteReader(); - } - } + await using var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - public int Execute(IQuery query) - { - Guard.AgainstNull(query, nameof(query)); + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); - using (var command = _databaseContextCache.Current.CreateCommandToExecute(query)) - { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + return await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + } - return command.ExecuteNonQuery(); - } - } + public async Task Execute(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + { + Guard.AgainstNull(query, nameof(query)); + + await using var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - public T GetScalar(IQuery query) - { - Guard.AgainstNull(query, nameof(query)); + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); - using (var command = _databaseContextCache.Current.CreateCommandToExecute(query)) - { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + return await command.ExecuteNonQueryAsync(cancellationToken); + } + + public async Task GetScalar(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + { + Guard.AgainstNull(query, nameof(query)); - var scalar = command.ExecuteScalar(); + var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - return scalar != null && scalar != DBNull.Value ? (T)scalar : default(T); - } - } - } + await using (command.ConfigureAwait(false)) + { + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + + var scalar = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + + return scalar != null && scalar != DBNull.Value ? (T)scalar : default; + } + } + } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DbCommandFactory.cs b/Shuttle.Core.Data/DbCommandFactory.cs index cbc84d4..983f05b 100644 --- a/Shuttle.Core.Data/DbCommandFactory.cs +++ b/Shuttle.Core.Data/DbCommandFactory.cs @@ -15,11 +15,12 @@ public DbCommandFactory(IOptions options) _commandTimeout = Guard.AgainstNull(options.Value, nameof(options.Value)).CommandTimeout; } - public IDbCommand CreateCommandUsing(IDbConnection connection, IQuery query) + public IDbCommand Create(IDbConnection connection, IQuery query) { var command = Guard.AgainstNull(connection, nameof(connection)).CreateCommand(); command.CommandTimeout = _commandTimeout; + Guard.AgainstNull(query, nameof(query)).Prepare(command); return command; diff --git a/Shuttle.Core.Data/DbConnectionFactory.cs b/Shuttle.Core.Data/DbConnectionFactory.cs index 0ef8f61..11f4361 100644 --- a/Shuttle.Core.Data/DbConnectionFactory.cs +++ b/Shuttle.Core.Data/DbConnectionFactory.cs @@ -22,7 +22,7 @@ public DbConnectionFactory(IDbProviderFactories providerFactories) { }; - public IDbConnection CreateConnection(string providerName, string connectionString) + public DbConnection Create(string providerName, string connectionString) { Guard.AgainstNullOrEmptyString(providerName, nameof(providerName)); Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index 4a6e5af..1faef8a 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -1,43 +1,150 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public static class DataRepositoryExtensions { - public static bool Contains(this IDataRepository dataRepository, string sql, dynamic parameters = null) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.Contains(RawQuery.Create(sql, parameters)); + return await dataRepository.Contains(RawQuery.Create(sql), CancellationToken.None); } - public static T FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters = null) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchItem(RawQuery.Create(sql, parameters)); + return await dataRepository.Contains(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters = null) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchItems(RawQuery.Create(sql, parameters)); + return await dataRepository.Contains(RawQuery.Create(sql), cancellationToken); } - public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters = null) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters)); + return await dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); } - public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters = null) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters)); + return await dataRepository.FetchItem(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); + } + + public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItems(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); + } + + public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRow(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); + } + + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRows(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); + } + + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + { + Guard.AgainstNull(dataRepository, nameof(dataRepository)); + + return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs index d608968..e05ea5f 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs @@ -6,57 +6,57 @@ namespace Shuttle.Core.Data { public static class DatabaseContextCacheExtensions { - public static bool Contains(this IDatabaseContextCache databaseContextCache, IDatabaseContext context) + public static bool Contains(this IDatabaseContextService databaseContextService, IDatabaseContext context) { - Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNull(context, nameof(context)); - return databaseContextCache.Find(databaseContext => databaseContext.Key.Equals(context.Key)) != null; + return databaseContextService.Find(databaseContext => databaseContext.Key.Equals(context.Key)) != null; } - public static bool Contains(this IDatabaseContextCache databaseContextCache, string name) + public static bool Contains(this IDatabaseContextService databaseContextService, string name) { - Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextCache.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) != null; + return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) != null; } - public static bool ContainsConnectionString(this IDatabaseContextCache databaseContextCache, string connectionString) + public static bool ContainsConnectionString(this IDatabaseContextService databaseContextService, string connectionString) { - Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); - return databaseContextCache.FindConnectionString(connectionString) != null; + return databaseContextService.FindConnectionString(connectionString) != null; } - public static IDatabaseContext Get(this IDatabaseContextCache databaseContextCache, string name) + public static IDatabaseContext Get(this IDatabaseContextService databaseContextService, string name) { - Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextCache.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); + return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); } - public static IDatabaseContext GetConnectionString(this IDatabaseContextCache databaseContextCache, string connectionString) + public static IDatabaseContext GetConnectionString(this IDatabaseContextService databaseContextService, string connectionString) { - return databaseContextCache.FindConnectionString(connectionString) ?? throw new Exception(Resources.DatabaseContextNotFoundException); + return databaseContextService.FindConnectionString(connectionString) ?? throw new Exception(Resources.DatabaseContextNotFoundException); } - public static ActiveDatabaseContext Use(this IDatabaseContextCache databaseContextCache, string name) + public static ActiveDatabaseContext Use(this IDatabaseContextService databaseContextService, string name) { - Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextCache.Use(databaseContextCache.Get(name)); + return databaseContextService.Use(databaseContextService.Get(name)); } - public static IDatabaseContext FindConnectionString(this IDatabaseContextCache databaseContextCache, string connectionString) + public static IDatabaseContext FindConnectionString(this IDatabaseContextService databaseContextService, string connectionString) { - Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); - var result = databaseContextCache.Find(candidate => + var result = databaseContextService.Find(candidate => candidate.Connection.ConnectionString.Equals(connectionString, StringComparison.OrdinalIgnoreCase)); @@ -70,7 +70,7 @@ public static IDatabaseContext FindConnectionString(this IDatabaseContextCache d matchDbConnectionStringBuilder.Remove("password"); matchDbConnectionStringBuilder.Remove("pwd"); - result = databaseContextCache.Find(candidate => + result = databaseContextService.Find(candidate => { var candidateDbConnectionStringBuilder = new DbConnectionStringBuilder { diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs index c400bf4..b31767c 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Data.Common; using System.Threading; using Shuttle.Core.Contract; @@ -40,7 +41,7 @@ public static bool IsAvailable(this IDatabaseContextFactory databaseContextFacto return IsAvailable(() => { - using (databaseContextFactory.Create(providerName, dbConnection)) + using (databaseContextFactory.Create(providerName, (DbConnection)dbConnection)) { } }, cancellationToken, retries, secondsBetweenRetries); diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index 9497dd3..a2ec2f2 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -1,51 +1,180 @@ using System.Collections.Generic; using System.Data; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public static class DatabaseGatewayExtensions { - public static int Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters = null) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.Execute(RawQuery.Create(sql, parameters)); + return await databaseGateway.Execute(RawQuery.Create(sql), CancellationToken.None); } - public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters = null) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetDataTable(RawQuery.Create(sql, parameters)); + return await databaseGateway.Execute(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static IDataReader GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters = null) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetReader(RawQuery.Create(sql, parameters)); + return await databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); } - public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters = null) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetRow(RawQuery.Create(sql, parameters)); + return await databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); } - public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters = null) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetRows(RawQuery.Create(sql, parameters)); + return await databaseGateway.GetDataTable(RawQuery.Create(sql), CancellationToken.None); } - public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters = null) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetScalar(RawQuery.Create(sql, parameters)); + return await databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); + } + + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetReader(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); + } + + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRow(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); + } + + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRows(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); + } + + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetScalar(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); + } + + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); + + return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/MappingExtensions.cs b/Shuttle.Core.Data/Extensions/MappingExtensions.cs index 90a3fc8..2b207a2 100644 --- a/Shuttle.Core.Data/Extensions/MappingExtensions.cs +++ b/Shuttle.Core.Data/Extensions/MappingExtensions.cs @@ -10,10 +10,8 @@ public static class MappingExtensions public static IEnumerable> MappedRowsUsing(this IEnumerable rows, IDataRowMapper mapper) where T : class { - Guard.AgainstNull(rows, nameof(rows)); - Guard.AgainstNull(mapper, nameof(mapper)); - - return rows.Select(mapper.Map).ToList(); + return Guard.AgainstNull(rows, nameof(rows)) + .Select(row => Guard.AgainstNull(mapper, nameof(mapper)).Map(row)).ToList(); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index ebd4348..68f7791 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -1,64 +1,234 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public static class QueryMapperExtensions { - public static dynamic MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters = null) + public static async Task MapItem(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapItem(RawQuery.Create(sql, parameters)); + return await queryMapper.MapItem(RawQuery.Create(sql), CancellationToken.None); } - public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters = null) + public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapItems(RawQuery.Create(sql, parameters)); + return await queryMapper.MapItem(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); + } + + public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapItem(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task> MapItems(this IQueryMapper queryMapper, string sql) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapItems(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapItems(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); + } + + public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObject(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObject(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); + } + + public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObjects(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); + } + + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRow(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRow(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); + } + + public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRows(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRows(RawQuery.Create(sql, parameters), CancellationToken.None); + } + + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); + } + + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); + } + + public static async Task MapValue(this IQueryMapper queryMapper, string sql) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapValue(RawQuery.Create(sql), CancellationToken.None); + } + + public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) + { + Guard.AgainstNull(queryMapper, nameof(queryMapper)); + + return await queryMapper.MapValue(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static T MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters = null) where T : new() + public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapObject(RawQuery.Create(sql, parameters)); + return await queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); } - public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters = null) where T : new() + public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapObjects(RawQuery.Create(sql, parameters)); + return await queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); } - public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters = null) where T : new() + public static async Task> MapValues(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapRow(RawQuery.Create(sql, parameters)); + return await queryMapper.MapValues(RawQuery.Create(sql), CancellationToken.None); } - public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters = null) where T : new() + public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapRows(RawQuery.Create(sql, parameters)); + return await queryMapper.MapValues(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static T MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters = null) + public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapValue(RawQuery.Create(sql, parameters)); + return await queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); } - public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters = null) + public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapValues(RawQuery.Create(sql, parameters)); + return await queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDataRepository.cs b/Shuttle.Core.Data/IDataRepository.cs index 9677841..993a328 100644 --- a/Shuttle.Core.Data/IDataRepository.cs +++ b/Shuttle.Core.Data/IDataRepository.cs @@ -1,13 +1,15 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Shuttle.Core.Data { public interface IDataRepository where T : class { - IEnumerable FetchItems(IQuery query); - T FetchItem(IQuery query); - MappedRow FetchMappedRow(IQuery query); - IEnumerable> FetchMappedRows(IQuery query); - bool Contains(IQuery query); + Task> FetchItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task FetchItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task Contains(IQuery query, CancellationToken cancellationToken = new CancellationToken()); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index 16d7c3d..72d4756 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Data.Common; namespace Shuttle.Core.Data { @@ -10,7 +11,7 @@ public interface IDatabaseContext : IDisposable IDbTransaction Transaction { get; } IDbConnection Connection { get; } - IDbCommand CreateCommandToExecute(IQuery query); + IDbCommand CreateCommand(IQuery query); bool HasTransaction { get; } string ProviderName { get; } diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index 0a699e4..d2bf9ed 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Data.Common; namespace Shuttle.Core.Data { @@ -6,11 +7,11 @@ public interface IDatabaseContextFactory { IDatabaseContext Create(string name); IDatabaseContext Create(string providerName, string connectionString); - IDatabaseContext Create(string providerName, IDbConnection dbConnection); + IDatabaseContext Create(string providerName, DbConnection dbConnection); IDatabaseContext Create(); IDbConnectionFactory DbConnectionFactory { get; } IDbCommandFactory DbCommandFactory { get; } - IDatabaseContextCache DatabaseContextCache { get; } + IDatabaseContextService DatabaseContextService { get; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextCache.cs b/Shuttle.Core.Data/IDatabaseContextService.cs similarity index 88% rename from Shuttle.Core.Data/IDatabaseContextCache.cs rename to Shuttle.Core.Data/IDatabaseContextService.cs index 250ae6d..792df3f 100644 --- a/Shuttle.Core.Data/IDatabaseContextCache.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -2,7 +2,7 @@ namespace Shuttle.Core.Data { - public interface IDatabaseContextCache + public interface IDatabaseContextService { bool HasCurrent { get; } IDatabaseContext Current { get; } diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index 06c8e1a..fcca7ba 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; namespace Shuttle.Core.Data { @@ -8,11 +11,11 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - IDataReader GetReader(IQuery query); - int Execute(IQuery query); - T GetScalar(IQuery query); - DataTable GetDataTable(IQuery query); - IEnumerable GetRows(IQuery query); - DataRow GetRow(IQuery query); + Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task Execute(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task GetScalar(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task GetDataTable(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task> GetRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task GetRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDbCommandFactory.cs b/Shuttle.Core.Data/IDbCommandFactory.cs index b80b0fe..43dcc97 100644 --- a/Shuttle.Core.Data/IDbCommandFactory.cs +++ b/Shuttle.Core.Data/IDbCommandFactory.cs @@ -4,6 +4,6 @@ namespace Shuttle.Core.Data { public interface IDbCommandFactory { - IDbCommand CreateCommandUsing(IDbConnection connection, IQuery query); + IDbCommand Create(IDbConnection connection, IQuery query); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDbConnectionFactory.cs b/Shuttle.Core.Data/IDbConnectionFactory.cs index f2ce01f..2c644e9 100644 --- a/Shuttle.Core.Data/IDbConnectionFactory.cs +++ b/Shuttle.Core.Data/IDbConnectionFactory.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Data.Common; namespace Shuttle.Core.Data { @@ -7,6 +8,6 @@ public interface IDbConnectionFactory { event EventHandler DbConnectionCreated; - IDbConnection CreateConnection(string providerName, string connectionString); + DbConnection Create(string providerName, string connectionString); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IMappedColumn.cs b/Shuttle.Core.Data/IMappedColumn.cs index 47a7781..3d989e4 100644 --- a/Shuttle.Core.Data/IMappedColumn.cs +++ b/Shuttle.Core.Data/IMappedColumn.cs @@ -10,8 +10,12 @@ public interface IMappedColumn byte? Precision { get; } byte? Scale { get; } string FlattenedColumnName(); + object RetrieveRawValueFrom(dynamic instance); object RetrieveRawValueFrom(DataRow row); + object RetrieveRawValueFrom(IDataRecord record); + bool IsNullFor(dynamic instance); bool IsNullFor(DataRow row); + bool IsNullFor(IDataRecord record); IDbDataParameter CreateDataParameter(IDbCommand command, object value); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryMapper.cs b/Shuttle.Core.Data/IQueryMapper.cs index d28ce1f..01ba26b 100644 --- a/Shuttle.Core.Data/IQueryMapper.cs +++ b/Shuttle.Core.Data/IQueryMapper.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Shuttle.Core.Data { public interface IQueryMapper { - MappedRow MapRow(IQuery query) where T : new(); - IEnumerable> MapRows(IQuery query) where T : new(); - T MapObject(IQuery query) where T : new(); - IEnumerable MapObjects(IQuery query) where T : new(); - T MapValue(IQuery query); - IEnumerable MapValues(IQuery query); - dynamic MapItem(IQuery query); - IEnumerable MapItems(IQuery query); + Task> MapRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); + Task>> MapRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); + Task MapObject(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); + Task> MapObjects(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); + Task MapValue(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task> MapValues(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task MapItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task> MapItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/MappedColumn.cs b/Shuttle.Core.Data/MappedColumn.cs index b874569..c8f5cf6 100644 --- a/Shuttle.Core.Data/MappedColumn.cs +++ b/Shuttle.Core.Data/MappedColumn.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Data; +using System.Dynamic; +using System.Resources; using Shuttle.Core.Contract; namespace Shuttle.Core.Data @@ -96,14 +98,43 @@ public string FlattenedColumnName() return ColumnName.Replace(".", "_"); } + public object RetrieveRawValueFrom(dynamic instance) + { + Guard.AgainstNull(instance, nameof(instance)); + + var property = instance.GetType().GetProperty(ColumnName); + + if (property == null) + { + return null; + } + + return property.GetValue(instance, null); + } + public object RetrieveRawValueFrom(DataRow row) { - return row[ColumnName]; + return Guard.AgainstNull(row, nameof(row))[ColumnName]; + } + + public object RetrieveRawValueFrom(IDataRecord record) + { + return Guard.AgainstNull(record, nameof(record))[ColumnName]; + } + + public bool IsNullFor(dynamic instance) + { + return Guard.AgainstNull(instance, nameof(instance))[ColumnName] == DBNull.Value; } public bool IsNullFor(DataRow row) { - return row.IsNull(ColumnName); + return Guard.AgainstNull(row, nameof(row)).IsNull(ColumnName); + } + + public bool IsNullFor(IDataRecord record) + { + return Guard.AgainstNull(record, nameof(record)).IsDBNull(Ordinal(record)); } public IDbDataParameter CreateDataParameter(IDbCommand command, object value) @@ -129,6 +160,34 @@ public IDbDataParameter CreateDataParameter(IDbCommand command, object value) return result; } + protected bool HasProperty(dynamic instance, string name) + { + return instance != null + && + ( + instance is ExpandoObject + ? ((IDictionary)instance).ContainsKey(name) + : (bool)(instance.GetType().GetProperty(name) != null) + ); + } + + public T MapFrom(dynamic instance) + { + Guard.AgainstNull(instance, nameof(instance)); + + if (HasProperty(instance, ColumnName)) + { + var type = typeof(T); + var value = instance.GetType().GetProperty(ColumnName).GetValue(instance, null); + + return value == null || DBNull.Value.Equals(value) + ? default + : (T)Convert.ChangeType(RetrieveRawValueFrom(value), Nullable.GetUnderlyingType(type) ?? type); + } + + return default; + } + public T MapFrom(DataRow row) { Guard.AgainstNull(row, nameof(row)); @@ -170,11 +229,11 @@ public static implicit operator string(MappedColumn column) return column.ColumnName; } - protected int Ordinal(IDataRecord reader) + protected int Ordinal(IDataRecord record) { try { - return reader.GetOrdinal(ColumnName); + return record.GetOrdinal(ColumnName); } catch { @@ -192,13 +251,6 @@ public MappedColumn Rename(string name) ? new MappedColumn(name, Type, DbType, Precision.Value, Scale ?? 0) : new MappedColumn(name, Type, DbType); } - - public object RetrieveRawValueFrom(IDataRecord record) - { - return record[ColumnName]; - } - - } public class MappedColumn : MappedColumn @@ -225,6 +277,23 @@ private void GetUnderlyingSystemType() _underlyingSystemType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); } + public T MapFrom(dynamic instance) + { + Guard.AgainstNull(instance, nameof(instance)); + + if (HasProperty(instance, ColumnName)) + { + var type = typeof(T); + var value = instance.GetType().GetProperty(ColumnName).GetValue(instance, null); + + return value == null + ? default + : (T)Convert.ChangeType(RetrieveRawValueFrom(instance), Nullable.GetUnderlyingType(type) ?? type); + } + + return default; + } + public T MapFrom(DataRow row) { Guard.AgainstNull(row, nameof(row)); diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index 5593ecc..8c2383a 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -4,6 +4,8 @@ using System.Dynamic; using System.Linq; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data @@ -22,87 +24,83 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public MappedRow MapRow(IQuery query) where T : new() + public async Task> MapRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRow(_databaseGateway.GetRow(query)); + return _dataRowMapper.MapRow(await _databaseGateway.GetRow(query, cancellationToken)); } - public IEnumerable> MapRows(IQuery query) where T : new() + public async Task>> MapRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRows(_databaseGateway.GetRows(query)); + return _dataRowMapper.MapRows(await _databaseGateway.GetRows(query, cancellationToken)); } - public T MapObject(IQuery query) where T : new() + public async Task MapObject(IQuery query, CancellationToken cancellationToken= new CancellationToken()) where T : new() { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query)) - { - var columns = GetColumns(reader); + await using var reader = await _databaseGateway.GetReader(query, cancellationToken); - if (reader.Read()) - { - return Map(GetPropertyInfo(), reader, columns); - } + var columns = GetColumns(reader); + + if (await reader.ReadAsync(cancellationToken)) + { + return Map(GetPropertyInfo(), reader, columns); } return default; } - public IEnumerable MapObjects(IQuery query) where T : new() + public async Task> MapObjects(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new() { Guard.AgainstNull(query, nameof(query)); var result = new List(); - using (var reader = _databaseGateway.GetReader(query)) - { - var columns = GetColumns(reader); + await using var reader = await _databaseGateway.GetReader(query, cancellationToken); - while (reader.Read()) - { - result.Add(Map(GetPropertyInfo(), reader, columns)); - } + var columns = GetColumns(reader); + + while (await reader.ReadAsync(cancellationToken)) + { + result.Add(Map(GetPropertyInfo(), reader, columns)); } return result; } - public T MapValue(IQuery query) + public async Task MapValue(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query)) + await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + + while (await reader.ReadAsync(cancellationToken)) { - while (reader.Read()) - { - return MapRowValue(reader); - } + return MapRowValue(reader); } return default; } - public IEnumerable MapValues(IQuery query) + public async Task> MapValues(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - using (var reader = _databaseGateway.GetReader(query)) + await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + + while (await reader.ReadAsync(cancellationToken)) { - while (reader.Read()) - { - var value = MapRowValue(reader); + var value = MapRowValue(reader); - if (value != null) - { - result.Add(value); - } + if (value != null) + { + result.Add(value); } } @@ -121,43 +119,41 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return result; } - public dynamic MapItem(IQuery query) + public async Task MapItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query)) - { - var columns = GetColumns(reader); + await using var reader = await _databaseGateway.GetReader(query, cancellationToken + ); + var columns = GetColumns(reader); - if (reader.Read()) - { - return DynamicMap(reader, columns); - } + if (await reader.ReadAsync(cancellationToken)) + { + return DynamicMap(reader, columns); } return default; } - public IEnumerable MapItems(IQuery query) + public async Task> MapItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - using (var reader = _databaseGateway.GetReader(query)) - { - var columns = GetColumns(reader); + await using var reader = await _databaseGateway.GetReader(query, cancellationToken); - while (reader.Read()) - { - result.Add(DynamicMap(reader, columns)); - } + var columns = GetColumns(reader); + + while (await reader.ReadAsync(cancellationToken)) + { + result.Add(DynamicMap(reader, columns)); } return result; } - private PropertyInfo[] GetPropertyInfo() + private IEnumerable GetPropertyInfo() { lock (_lock) { @@ -172,7 +168,7 @@ private PropertyInfo[] GetPropertyInfo() } } - private T Map(IEnumerable properties, IDataRecord record, Dictionary columns) where T : new() + private static T Map(IEnumerable properties, IDataRecord record, Dictionary columns) where T : new() { var result = new T(); diff --git a/Shuttle.Core.Data/ScriptProvider.cs b/Shuttle.Core.Data/ScriptProvider.cs index 6b89aef..631d2f7 100644 --- a/Shuttle.Core.Data/ScriptProvider.cs +++ b/Shuttle.Core.Data/ScriptProvider.cs @@ -10,17 +10,17 @@ public class ScriptProvider : IScriptProvider { private static readonly object Padlock = new object(); private readonly ScriptProviderOptions _options; - private readonly IDatabaseContextCache _databaseContextCache; + private readonly IDatabaseContextService _databaseContextService; private readonly string[] _emptyFiles = Array.Empty(); private readonly Dictionary _scripts = new Dictionary(); - public ScriptProvider(IOptions options, IDatabaseContextCache databaseContextCache) + public ScriptProvider(IOptions options, IDatabaseContextService databaseContextService) { Guard.AgainstNull(options, nameof(options)); _options = Guard.AgainstNull(options.Value, nameof(options.Value)); - _databaseContextCache = Guard.AgainstNull(databaseContextCache, nameof(databaseContextCache)); + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } public string Get(string scriptName) @@ -51,7 +51,7 @@ private string Key(string scriptName) { lock (Padlock) { - return $"[{_databaseContextCache.Current.ProviderName}]-{scriptName}"; + return $"[{_databaseContextService.Current.ProviderName}]-{scriptName}"; } } @@ -102,7 +102,7 @@ private string FormattedResourceName(string scriptName) private string FormattedScriptPath(string format, string scriptName) { - return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", _databaseContextCache.Current.ProviderName); + return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", _databaseContextService.Current.ProviderName); } private void AddEmbeddedScript(string scriptName) diff --git a/Shuttle.Core.Data/Shuttle.Core.Data.csproj b/Shuttle.Core.Data/Shuttle.Core.Data.csproj index 013faac..58de6ff 100644 --- a/Shuttle.Core.Data/Shuttle.Core.Data.csproj +++ b/Shuttle.Core.Data/Shuttle.Core.Data.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1 + netstandard2.1 false diff --git a/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs b/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs index 40cd92e..9c6d185 100644 --- a/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs +++ b/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs @@ -2,9 +2,9 @@ namespace Shuttle.Core.Data { - public class ThreadStaticDatabaseContextCache : IDatabaseContextCache + public class ThreadStaticDatabaseContextService : IDatabaseContextService { - [ThreadStatic] private static DatabaseContextCache _cache; + [ThreadStatic] private static DatabaseContextService _service; public bool HasCurrent => GuardedCache().HasCurrent; @@ -40,9 +40,9 @@ public IDatabaseContext Get(string name) return GuardedCache().Get(name); } - private static DatabaseContextCache GuardedCache() + private static DatabaseContextService GuardedCache() { - return _cache ?? (_cache = new DatabaseContextCache()); + return _service ?? (_service = new DatabaseContextService()); } } } \ No newline at end of file From 2a191e26cbd9b33165b946063e8da84041047efe Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 10 Feb 2023 17:38:31 +0200 Subject: [PATCH 02/37] - async --- README.md | 2 +- .../DatabaseContextCacheFixture.cs | 38 ---- .../DatabaseContextServiceFixture.cs | 38 ++++ .../MappedColumnFixture.cs | 67 ++++--- Shuttle.Core.Data.Tests/Mapping/Columns.cs | 2 +- .../Mapping/DataRowMapperFixture.cs | 18 +- .../Mapping/MappingFixture.cs | 2 +- .../Mapping/QueryMapperFixture.cs | 16 +- .../ProcedureQueryFixture.cs | 2 +- Shuttle.Core.Data.Tests/RawQueryFixture.cs | 10 +- .../ScriptProviderFixture.cs | 8 +- Shuttle.Core.Data/.package/package.nuspec | 5 +- .../{MappedColumn.cs => Column.cs} | 155 ++++++++------- .../ServiceCollectionExtensions.cs | 2 +- Shuttle.Core.Data/DatabaseContextCache.cs | 109 ----------- Shuttle.Core.Data/DatabaseContextService.cs | 179 ++++++++++++++++++ Shuttle.Core.Data/DatabaseGateway.cs | 36 ++-- .../Extensions/DataRepositoryExtensions.cs | 40 ++-- ...cs => DatabaseContextServiceExtensions.cs} | 2 +- .../Extensions/DatabaseGatewayExtensions.cs | 48 ++--- .../Extensions/QueryMapperExtensions.cs | 64 +++---- Shuttle.Core.Data/IDatabaseGateway.cs | 2 +- Shuttle.Core.Data/IMappedColumn.cs | 18 +- Shuttle.Core.Data/IQueryParameter.cs | 2 +- Shuttle.Core.Data/ProcedureQuery.cs | 6 +- Shuttle.Core.Data/Properties/AssemblyInfo.cs | 6 +- Shuttle.Core.Data/{RawQuery.cs => Query.cs} | 17 +- Shuttle.Core.Data/QueryMapper.cs | 14 +- Shuttle.Core.Data/ScriptProvider.cs | 8 +- Shuttle.Core.Data/Shuttle.Core.Data.csproj | 1 + .../ThreadStaticDatabaseContextCache.cs | 48 ----- 31 files changed, 495 insertions(+), 470 deletions(-) delete mode 100644 Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs create mode 100644 Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs rename Shuttle.Core.Data/{MappedColumn.cs => Column.cs} (62%) delete mode 100644 Shuttle.Core.Data/DatabaseContextCache.cs create mode 100644 Shuttle.Core.Data/DatabaseContextService.cs rename Shuttle.Core.Data/Extensions/{DatabaseContextCacheExtensions.cs => DatabaseContextServiceExtensions.cs} (98%) rename Shuttle.Core.Data/{RawQuery.cs => Query.cs} (69%) delete mode 100644 Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs diff --git a/README.md b/README.md index ce6caa0..af2a9e6 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ The default JSON settings structure is as follows: In order to access a database we need a database connection. A database connection is represented by an `IDatabaseContext` instance that may be obtained by using an instance of an `IDatabaseContextFactory` implementation. -The `DatabaseContextFactory` implementation makes use of an `IDbConnectionFactory` implementation which creates a `System.Data.IDbConnection` by using the provider name and connection string. An `IDbCommandFactory` creates a `System.Data.IDbCommand` by using an `IDbConnection` instance. The `DatabaseContextFactory` also requires an instance of an `IDatabaseContextCache` that stores connections and is assigned to the `DatabaseContext` in order to obtain the active connection. +The `DatabaseContextFactory` implementation makes use of an `IDbConnectionFactory` implementation which creates a `System.Data.IDbConnection` by using the provider name and connection string. An `IDbCommandFactory` creates a `System.Data.IDbCommand` by using an `IDbConnection` instance. The `DatabaseContextFactory` also requires an instance of an `IDatabaseContextService` that stores connections and is assigned to the `DatabaseContext` in order to obtain the active connection. ``` c# var databaseContextFactory = provider.GetRequiredService(); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs deleted file mode 100644 index 19e6f61..0000000 --- a/Shuttle.Core.Data.Tests/DatabaseContextCacheFixture.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Data; -using System.Data.Common; -using Moq; -using NUnit.Framework; - -namespace Shuttle.Core.Data.Tests -{ - public class DatabaseContextCacheFixture : Fixture - { - [Test] - public void Should_be_able_to_use_different_contexts() - { - var cache = new DatabaseContextService(); - - var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, cache).WithName("mock-1"); - - Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - - var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, cache).WithName("mock-2"); - - Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - - using (cache.Use("mock-1")) - { - Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - } - - Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - - using (cache.Use(context1)) - { - Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - } - - Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs new file mode 100644 index 0000000..df0a002 --- /dev/null +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -0,0 +1,38 @@ +using System.Data; +using System.Data.Common; +using Moq; +using NUnit.Framework; + +namespace Shuttle.Core.Data.Tests +{ + public class DatabaseContextServiceFixture : Fixture + { + [Test] + public void Should_be_able_to_use_different_contexts() + { + var service = new DatabaseContextService(); + + var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, service).WithName("mock-1"); + + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + + var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, service).WithName("mock-2"); + + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + + using (service.Use("mock-1")) + { + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + } + + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + + using (service.Use(context1)) + { + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + } + + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/MappedColumnFixture.cs b/Shuttle.Core.Data.Tests/MappedColumnFixture.cs index 214846a..07d605e 100644 --- a/Shuttle.Core.Data.Tests/MappedColumnFixture.cs +++ b/Shuttle.Core.Data.Tests/MappedColumnFixture.cs @@ -2,39 +2,38 @@ using System.Data; using System.Data.SqlTypes; using Microsoft.Data.SqlClient.Server; -using Microsoft.SqlServer.Server; using Moq; using NUnit.Framework; namespace Shuttle.Core.Data.Tests { [TestFixture] - public class MappedColumnFixture : Fixture + public class ColumnFixture : Fixture { [Test] public void Should_be_able_to_create_mapped_columns() { const string columnName = "name"; - var mcGuid = new MappedColumn(columnName, DbType.Guid); + var mcGuid = new Column(columnName, DbType.Guid); - Assert.AreEqual(columnName, mcGuid.ColumnName); + Assert.AreEqual(columnName, mcGuid.Name); Assert.AreEqual(DbType.Guid, mcGuid.DbType); Assert.IsNull(mcGuid.Size); Assert.IsNull(mcGuid.Precision); Assert.IsNull(mcGuid.Scale); - var mcString = new MappedColumn(columnName, DbType.AnsiString, 65); + var mcString = new Column(columnName, DbType.AnsiString, 65); - Assert.AreEqual(columnName, mcString.ColumnName); + Assert.AreEqual(columnName, mcString.Name); Assert.AreEqual(DbType.AnsiString, mcString.DbType); Assert.AreEqual(65, mcString.Size); Assert.IsNull(mcString.Precision); Assert.IsNull(mcString.Scale); - var mcDouble = new MappedColumn(columnName, DbType.Decimal, 10, 2); + var mcDouble = new Column(columnName, DbType.Decimal, 10, 2); - Assert.AreEqual(columnName, mcDouble.ColumnName); + Assert.AreEqual(columnName, mcDouble.Name); Assert.AreEqual(DbType.Decimal, mcDouble.DbType); Assert.IsNull(mcDouble.Size); Assert.AreEqual((byte)10, mcDouble.Precision); @@ -48,7 +47,7 @@ public void Should_be_able_to_create_data_parameters() var parameter = new Mock(); - var mcGuid = new MappedColumn(columnName, DbType.Guid); + var mcGuid = new Column(columnName, DbType.Guid); var guid = Guid.NewGuid(); @@ -61,7 +60,7 @@ public void Should_be_able_to_create_data_parameters() factory.VerifyAll(); Assert.AreSame(result, parameter.Object); - var mcString = new MappedColumn(columnName, DbType.AnsiString, 65); + var mcString = new Column(columnName, DbType.AnsiString, 65); factory = new Mock(); factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); @@ -71,7 +70,7 @@ public void Should_be_able_to_create_data_parameters() factory.VerifyAll(); Assert.AreSame(result, parameter.Object); - var mcDouble = new MappedColumn(columnName, DbType.Decimal, 10, 2); + var mcDouble = new Column(columnName, DbType.Decimal, 10, 2); factory = new Mock(); factory.Setup(m => m.CreateParameter()).Returns(parameter.Object); @@ -91,13 +90,13 @@ public void Should_be_able_to_determine_if_column_value_is_null() var row = table.Rows.Add(DBNull.Value); - var mc = new MappedColumn("column-1", DbType.AnsiString, 65); + var mc = new Column("column-1", DbType.AnsiString, 65); - Assert.IsTrue(mc.IsNullFor(row)); + Assert.IsTrue(mc.IsNull(row)); row["column-1"] = "value-1"; - Assert.IsFalse(mc.IsNullFor(row)); + Assert.IsFalse(mc.IsNull(row)); } [Test] @@ -105,18 +104,18 @@ public void Should_be_able_to_map_from_a_dynamic_instance() { var instanceA = new { column1 = (string)null }; - var mc = new MappedColumn("column1", DbType.AnsiString, 65); - var missing = new MappedColumn("missing", DbType.AnsiString, 65); + var mc = new Column("column1", DbType.AnsiString, 65); + var missing = new Column("missing", DbType.AnsiString, 65); Assert.AreEqual(default(string), mc.MapFrom(instanceA)); Assert.AreEqual(default(string), missing.MapFrom(instanceA)); - Assert.AreEqual(null, mc.RetrieveRawValueFrom(instanceA)); + Assert.AreEqual(null, mc.RawValue(instanceA)); var instanceB = new { column1 = "value-1" }; Assert.AreEqual("value-1", mc.MapFrom(instanceB)); Assert.AreEqual(default(string), missing.MapFrom(instanceB)); - Assert.AreEqual("value-1", mc.RetrieveRawValueFrom(instanceB)); + Assert.AreEqual("value-1", mc.RawValue(instanceB)); } [Test] @@ -128,18 +127,18 @@ public void Should_be_able_to_map_from_a_data_row() var row = table.Rows.Add(DBNull.Value); - var mc = new MappedColumn("column-1", DbType.AnsiString, 65); - var missing = new MappedColumn("missing", DbType.AnsiString, 65); + var mc = new Column("column-1", DbType.AnsiString, 65); + var missing = new Column("missing", DbType.AnsiString, 65); Assert.AreEqual(default(string), mc.MapFrom(row)); Assert.AreEqual(default(string), missing.MapFrom(row)); - Assert.AreEqual(DBNull.Value, mc.RetrieveRawValueFrom(row)); + Assert.AreEqual(DBNull.Value, mc.RawValue(row)); row["column-1"] = "value-1"; Assert.AreEqual("value-1", mc.MapFrom(row)); Assert.AreEqual(default(string), missing.MapFrom(row)); - Assert.AreEqual("value-1", mc.RetrieveRawValueFrom(row)); + Assert.AreEqual("value-1", mc.RawValue(row)); } [Test] @@ -149,8 +148,8 @@ public void Should_be_able_to_map_from_a_data_record() record.SetSqlString(0, new SqlString(null)); - var column1 = new MappedColumn("column-1", DbType.AnsiString, 65); - var column2 = new MappedColumn("column-2", DbType.AnsiString, 65); + var column1 = new Column("column-1", DbType.AnsiString, 65); + var column2 = new Column("column-2", DbType.AnsiString, 65); Assert.AreEqual(default(string), column1.MapFrom(record)); Assert.AreEqual(default(string), column2.MapFrom(record)); @@ -164,32 +163,32 @@ public void Should_be_able_to_map_from_a_data_record() [Test] public void Should_be_able_to_rename_a_mapped_column() { - var column1Guid = new MappedColumn("column-1", DbType.Guid); + var column1Guid = new Column("column-1", DbType.Guid); var column2Guid = column1Guid.Rename("column-2"); - Assert.AreEqual("column-1", column1Guid.ColumnName); + Assert.AreEqual("column-1", column1Guid.Name); Assert.AreEqual(DbType.Guid, column1Guid.DbType); - Assert.AreEqual("column-2", column2Guid.ColumnName); + Assert.AreEqual("column-2", column2Guid.Name); Assert.AreEqual(DbType.Guid, column2Guid.DbType); - var column1String = new MappedColumn("column-1", DbType.AnsiString, 65); + var column1String = new Column("column-1", DbType.AnsiString, 65); var column2String = column1String.Rename("column-2"); - Assert.AreEqual("column-1", column1String.ColumnName); + Assert.AreEqual("column-1", column1String.Name); Assert.AreEqual(DbType.AnsiString, column1String.DbType); Assert.AreEqual(65, column1String.Size); - Assert.AreEqual("column-2", column2String.ColumnName); + Assert.AreEqual("column-2", column2String.Name); Assert.AreEqual(DbType.AnsiString, column2String.DbType); Assert.AreEqual(65, column2String.Size); - var column1Double = new MappedColumn("column-1", DbType.Double, 10, 2); + var column1Double = new Column("column-1", DbType.Double, 10, 2); var column2Double = column1Double.Rename("column-2"); - Assert.AreEqual("column-1", column1Double.ColumnName); + Assert.AreEqual("column-1", column1Double.Name); Assert.AreEqual(DbType.Double, column1Double.DbType); Assert.AreEqual((byte)10, column1Double.Precision); Assert.AreEqual((byte)2, column1Double.Scale); - Assert.AreEqual("column-2", column2Double.ColumnName); + Assert.AreEqual("column-2", column2Double.Name); Assert.AreEqual(DbType.Double, column2Double.DbType); Assert.AreEqual((byte)10, column2Double.Precision); Assert.AreEqual((byte)2, column2Double.Scale); @@ -198,7 +197,7 @@ public void Should_be_able_to_rename_a_mapped_column() [Test] public void Should_be_able_to_implicitly_convert_to_string() { - var mc = new MappedColumn("column-1", DbType.AnsiString, 65); + var mc = new Column("column-1", DbType.AnsiString, 65); string name = mc; diff --git a/Shuttle.Core.Data.Tests/Mapping/Columns.cs b/Shuttle.Core.Data.Tests/Mapping/Columns.cs index 5883ac1..0431fbb 100644 --- a/Shuttle.Core.Data.Tests/Mapping/Columns.cs +++ b/Shuttle.Core.Data.Tests/Mapping/Columns.cs @@ -5,5 +5,5 @@ namespace Shuttle.Core.Data.Tests; public class Columns { - public static readonly MappedColumn Id = new("Id", DbType.Guid); + public static readonly Column Id = new("Id", DbType.Guid); } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 27fa112..0a779b9 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -13,7 +13,7 @@ public void Should_be_able_to_perform_basic_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = RawQuery.Create(@" + var rowQuery = Query.Create(@" select top 1 Id, Name, @@ -22,7 +22,7 @@ select top 1 BasicMapping "); - var rowsQuery = RawQuery.Create(@" + var rowsQuery = Query.Create(@" select Id, Name, @@ -53,7 +53,7 @@ public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_miss var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = RawQuery.Create(@" + var rowQuery = Query.Create(@" select top 1 Id, Name as NotMapped, @@ -62,7 +62,7 @@ Age as TheAge BasicMapping "); - var rowsQuery = RawQuery.Create(@" + var rowsQuery = Query.Create(@" select Id, Name, @@ -93,14 +93,14 @@ public void Should_be_able_to_perform_value_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = RawQuery.Create(@" + var rowQuery = Query.Create(@" select top 1 Id from BasicMapping "); - var rowsQuery = RawQuery.Create(@" + var rowsQuery = Query.Create(@" select Id from @@ -136,8 +136,8 @@ public void Should_be_able_to_perform_dynamic_mapping() Id = @Id "; - var rowQuery = RawQuery.Create(rowSql).AddParameterValue(Columns.Id, id); - var rowsQuery = RawQuery.Create(@" + var rowQuery = Query.Create(rowSql).AddParameterValue(Columns.Id, id); + var rowsQuery = Query.Create(@" select Id, Name, @@ -156,7 +156,7 @@ public void Should_be_able_to_perform_dynamic_mapping() Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id })).Result); + item = dataRowMapper.MapItem(databaseGateway.GetRow(Query.Create(rowSql, new { Id = id })).Result); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index bd34cc8..0338b7e 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -9,7 +9,7 @@ public void SetUp() { using (GetDatabaseContext()) { - GetDatabaseGateway().Execute(RawQuery.Create(@" + GetDatabaseGateway().Execute(Query.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index f468a4e..39abb3b 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -13,7 +13,7 @@ public void Should_be_able_to_perform_basic_mapping() { var mapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = Query.Create(@" select top 1 Id, Name, @@ -22,7 +22,7 @@ select top 1 BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = Query.Create(@" select Id, Name, @@ -52,7 +52,7 @@ public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_miss { var mapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = Query.Create(@" select top 1 Id, Name as NotMapped, @@ -61,7 +61,7 @@ Age as TheAge BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = Query.Create(@" select Id, Name, @@ -91,14 +91,14 @@ public void Should_be_able_to_perform_value_mapping() { var mapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = Query.Create(@" select top 1 Id from BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = Query.Create(@" select Id from @@ -121,7 +121,7 @@ public void Should_be_able_to_perform_dynamic_mapping() var databaseGateway = GetDatabaseGateway(); var queryMapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = Query.Create(@" select top 1 Id, Name, @@ -130,7 +130,7 @@ select top 1 BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = Query.Create(@" select Id, Name, diff --git a/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs b/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs index 29e3880..5acb7f7 100644 --- a/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs @@ -24,7 +24,7 @@ public void Should_be_able_prepare_a_query() const string sql = "uspDoSomething"; var guid = Guid.NewGuid(); - var mc = new MappedColumn("Id", DbType.Guid); + var mc = new Column("Id", DbType.Guid); var query = new ProcedureQuery(sql).AddParameterValue(mc, guid); var dataParameterCollection = new Mock(); diff --git a/Shuttle.Core.Data.Tests/RawQueryFixture.cs b/Shuttle.Core.Data.Tests/RawQueryFixture.cs index 0ed0824..d33e213 100644 --- a/Shuttle.Core.Data.Tests/RawQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/RawQueryFixture.cs @@ -15,8 +15,8 @@ public void Should_be_able_prepare_a_query() const string sql = "select @Id"; var guid = Guid.NewGuid(); - var mc = new MappedColumn("Id", DbType.Guid); - var query = new RawQuery(sql).AddParameterValue(mc, guid); + var mc = new Column("Id", DbType.Guid); + var query = new Query(sql).AddParameterValue(mc, guid); var dataParameterCollection = new Mock(); var command = new Mock(); @@ -38,9 +38,9 @@ public void Should_be_able_to_create_a_query() { const string sql = "select 1"; - var query1 = new RawQuery(sql); - var query2 = RawQuery.Create(sql); - var query3 = RawQuery.Create("select {0}", 1); + var query1 = new Query(sql); + var query2 = Query.Create(sql); + var query3 = Query.Create("select {0}", 1); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs index acd7422..e5c389e 100644 --- a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs +++ b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs @@ -30,7 +30,7 @@ public void Should_eb_able_to_retrieve_script_from_file() { FileNameFormat = "{ScriptName}.sql", ScriptFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @".\.scripts\") - }), MockDatabaseContextCache()); + }), MockDatabaseContextService()); var script = provider.Get("file-script"); @@ -38,7 +38,7 @@ public void Should_eb_able_to_retrieve_script_from_file() Assert.AreEqual("select 'file-script'", script); } - private static IDatabaseContextService MockDatabaseContextCache() + private static IDatabaseContextService MockDatabaseContextService() { var databaseContextCache = new Mock(); @@ -54,7 +54,7 @@ public void Should_eb_able_to_retrieve_script_from_resource() { ResourceAssembly = GetType().Assembly, ResourceNameFormat = "Shuttle.Core.Data.Tests..scripts.Microsoft.Data.SqlClient.{ScriptName}.sql" - }), MockDatabaseContextCache()); + }), MockDatabaseContextService()); var script = provider.Get("embedded-script"); @@ -68,7 +68,7 @@ public void Should_throw_exception_when_no_resource_or_file_found() var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly - }), MockDatabaseContextCache()); + }), MockDatabaseContextService()); Assert.Throws(() => provider.Get("Microsoft.Data.SqlClient", "missing-script")); } diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index 5600d4f..50167e2 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -3,7 +3,7 @@ Shuttle.Core.Data - 14.0.2 + 15.0.0 Eben Roux Eben Roux BSD-3-Clause @@ -13,7 +13,7 @@ https://github.com/Shuttle/Shuttle.Core.Data Provides an abstraction over ADO.NET. - Copyright (c) 2022, Eben Roux + Copyright (c) 2023, Eben Roux orm micro-orm microorm data-access @@ -22,6 +22,7 @@ + diff --git a/Shuttle.Core.Data/MappedColumn.cs b/Shuttle.Core.Data/Column.cs similarity index 62% rename from Shuttle.Core.Data/MappedColumn.cs rename to Shuttle.Core.Data/Column.cs index c8f5cf6..66961ee 100644 --- a/Shuttle.Core.Data/MappedColumn.cs +++ b/Shuttle.Core.Data/Column.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Data; using System.Dynamic; -using System.Resources; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { - public class MappedColumn : IMappedColumn + public class Column : IColumn { private static readonly Dictionary DbTypes = new Dictionary { @@ -47,29 +46,15 @@ public class MappedColumn : IMappedColumn [typeof(DateTimeOffset?)] = DbType.DateTimeOffset }; - public static DbType GetDbType(Type type) - { - Guard.AgainstNull(type, nameof(type)); - - if (!DbTypes.ContainsKey(type)) - { - throw new ArgumentException(string.Format(Resources.DbTypeMappingException, type.FullName)); - } - - return DbTypes[type]; - } - - public Type Type { get; } - - public MappedColumn(string columnName, Type type, DbType dbType) - : this(columnName, type, dbType, null) + public Column(string name, Type type, DbType dbType) + : this(name, type, dbType, null) { Type = type; } - public MappedColumn(string columnName, Type type, DbType dbType, int? size) + public Column(string name, Type type, DbType dbType, int? size) { - ColumnName = columnName; + Name = name; Type = type; DbType = dbType; Size = size; @@ -77,9 +62,9 @@ public MappedColumn(string columnName, Type type, DbType dbType, int? size) Scale = null; } - public MappedColumn(string columnName, Type type, DbType dbType, byte precision, byte scale) + public Column(string name, Type type, DbType dbType, byte precision, byte scale) { - ColumnName = columnName; + Name = name; Type = type; DbType = dbType; Precision = precision; @@ -87,22 +72,24 @@ public MappedColumn(string columnName, Type type, DbType dbType, byte precision, Size = null; } - public string ColumnName { get; protected set; } + public Type Type { get; } + + public string Name { get; protected set; } public DbType DbType { get; } public int? Size { get; protected set; } public byte? Precision { get; protected set; } public byte? Scale { get; protected set; } - public string FlattenedColumnName() + public string FlattenedName() { - return ColumnName.Replace(".", "_"); + return Name.Replace(".", "_"); } - public object RetrieveRawValueFrom(dynamic instance) + public object RawValue(dynamic instance) { Guard.AgainstNull(instance, nameof(instance)); - var property = instance.GetType().GetProperty(ColumnName); + var property = instance.GetType().GetProperty(Name); if (property == null) { @@ -112,27 +99,27 @@ public object RetrieveRawValueFrom(dynamic instance) return property.GetValue(instance, null); } - public object RetrieveRawValueFrom(DataRow row) + public object RawValue(DataRow row) { - return Guard.AgainstNull(row, nameof(row))[ColumnName]; + return Guard.AgainstNull(row, nameof(row))[Name]; } - public object RetrieveRawValueFrom(IDataRecord record) + public object RawValue(IDataRecord record) { - return Guard.AgainstNull(record, nameof(record))[ColumnName]; + return Guard.AgainstNull(record, nameof(record))[Name]; } - public bool IsNullFor(dynamic instance) + public bool IsNull(dynamic instance) { - return Guard.AgainstNull(instance, nameof(instance))[ColumnName] == DBNull.Value; + return Guard.AgainstNull(instance, nameof(instance))[Name] == DBNull.Value; } - public bool IsNullFor(DataRow row) + public bool IsNull(DataRow row) { - return Guard.AgainstNull(row, nameof(row)).IsNull(ColumnName); + return Guard.AgainstNull(row, nameof(row)).IsNull(Name); } - public bool IsNullFor(IDataRecord record) + public bool IsNull(IDataRecord record) { return Guard.AgainstNull(record, nameof(record)).IsDBNull(Ordinal(record)); } @@ -141,7 +128,7 @@ public IDbDataParameter CreateDataParameter(IDbCommand command, object value) { var result = command.CreateParameter(); - result.ParameterName = string.Concat("@", FlattenedColumnName()); + result.ParameterName = string.Concat("@", FlattenedName()); result.DbType = DbType; result.Value = value ?? DBNull.Value; @@ -160,51 +147,63 @@ public IDbDataParameter CreateDataParameter(IDbCommand command, object value) return result; } + public static DbType GetDbType(Type type) + { + Guard.AgainstNull(type, nameof(type)); + + if (!DbTypes.ContainsKey(type)) + { + throw new ArgumentException(string.Format(Resources.DbTypeMappingException, type.FullName)); + } + + return DbTypes[type]; + } + protected bool HasProperty(dynamic instance, string name) { - return instance != null - && + return instance != null + && ( - instance is ExpandoObject - ? ((IDictionary)instance).ContainsKey(name) + instance is ExpandoObject + ? ((IDictionary)instance).ContainsKey(name) : (bool)(instance.GetType().GetProperty(name) != null) - ); + ); } - public T MapFrom(dynamic instance) + public T Value(dynamic instance) { Guard.AgainstNull(instance, nameof(instance)); - if (HasProperty(instance, ColumnName)) + if (HasProperty(instance, Name)) { var type = typeof(T); - var value = instance.GetType().GetProperty(ColumnName).GetValue(instance, null); + var value = instance.GetType().GetProperty(Name).GetValue(instance, null); return value == null || DBNull.Value.Equals(value) ? default - : (T)Convert.ChangeType(RetrieveRawValueFrom(value), Nullable.GetUnderlyingType(type) ?? type); + : (T)Convert.ChangeType(RawValue(value), Nullable.GetUnderlyingType(type) ?? type); } return default; } - public T MapFrom(DataRow row) + public T Value(DataRow row) { Guard.AgainstNull(row, nameof(row)); - if (row.Table.Columns.Contains(ColumnName)) + if (row.Table.Columns.Contains(Name)) { var type = typeof(T); - return row.IsNull(ColumnName) + return row.IsNull(Name) ? default - : (T)Convert.ChangeType(RetrieveRawValueFrom(row), Nullable.GetUnderlyingType(type) ?? type); + : (T)Convert.ChangeType(RawValue(row), Nullable.GetUnderlyingType(type) ?? type); } return default; } - public T MapFrom(IDataRecord record) + public T Value(IDataRecord record) { Guard.AgainstNull(record, nameof(record)); @@ -216,24 +215,24 @@ public T MapFrom(IDataRecord record) return record.IsDBNull(ordinal) ? default - : (T)Convert.ChangeType(RetrieveRawValueFrom(record), Nullable.GetUnderlyingType(type) ?? type); + : (T)Convert.ChangeType(RawValue(record), Nullable.GetUnderlyingType(type) ?? type); } return default; } - public static implicit operator string(MappedColumn column) + public static implicit operator string(Column column) { Guard.AgainstNull(column, nameof(column)); - return column.ColumnName; + return column.Name; } protected int Ordinal(IDataRecord record) { try { - return record.GetOrdinal(ColumnName); + return record.GetOrdinal(Name); } catch { @@ -241,33 +240,33 @@ protected int Ordinal(IDataRecord record) } } - public MappedColumn Rename(string name) + public Column Rename(string name) { Guard.AgainstNullOrEmptyString(name, nameof(name)); return Size.HasValue - ? new MappedColumn(name, Type, DbType, Size.Value) + ? new Column(name, Type, DbType, Size.Value) : Precision.HasValue - ? new MappedColumn(name, Type, DbType, Precision.Value, Scale ?? 0) - : new MappedColumn(name, Type, DbType); + ? new Column(name, Type, DbType, Precision.Value, Scale ?? 0) + : new Column(name, Type, DbType); } } - public class MappedColumn : MappedColumn + public class Column : Column { private Type _underlyingSystemType; - public MappedColumn(string columnName, DbType dbType) - : this(columnName, dbType, null) + public Column(string name, DbType dbType) + : this(name, dbType, null) { } - public MappedColumn(string columnName, DbType dbType, int? size) : base(columnName, typeof(T), dbType, size) + public Column(string name, DbType dbType, int? size) : base(name, typeof(T), dbType, size) { GetUnderlyingSystemType(); } - public MappedColumn(string columnName, DbType dbType, byte precision, byte scale) : base(columnName, typeof(T), dbType, precision, scale) + public Column(string name, DbType dbType, byte precision, byte scale) : base(name, typeof(T), dbType, precision, scale) { GetUnderlyingSystemType(); } @@ -281,14 +280,14 @@ public T MapFrom(dynamic instance) { Guard.AgainstNull(instance, nameof(instance)); - if (HasProperty(instance, ColumnName)) + if (HasProperty(instance, Name)) { var type = typeof(T); - var value = instance.GetType().GetProperty(ColumnName).GetValue(instance, null); + var value = instance.GetType().GetProperty(Name).GetValue(instance, null); - return value == null + return value == null ? default - : (T)Convert.ChangeType(RetrieveRawValueFrom(instance), Nullable.GetUnderlyingType(type) ?? type); + : (T)Convert.ChangeType(RawValue(instance), Nullable.GetUnderlyingType(type) ?? type); } return default; @@ -298,11 +297,11 @@ public T MapFrom(DataRow row) { Guard.AgainstNull(row, nameof(row)); - if (row.Table.Columns.Contains(ColumnName)) + if (row.Table.Columns.Contains(Name)) { - return row.IsNull(ColumnName) + return row.IsNull(Name) ? default - : (T)Convert.ChangeType(RetrieveRawValueFrom(row), _underlyingSystemType); + : (T)Convert.ChangeType(RawValue(row), _underlyingSystemType); } return default; @@ -318,28 +317,28 @@ public T MapFrom(IDataRecord record) { return record.IsDBNull(ordinal) ? default - : (T)Convert.ChangeType(RetrieveRawValueFrom(record), _underlyingSystemType); + : (T)Convert.ChangeType(RawValue(record), _underlyingSystemType); } return default; } - public static implicit operator string(MappedColumn column) + public static implicit operator string(Column column) { Guard.AgainstNull(column, nameof(column)); - return column.ColumnName; + return column.Name; } - public new MappedColumn Rename(string name) + public new Column Rename(string name) { Guard.AgainstNullOrEmptyString(name, nameof(name)); return Size.HasValue - ? new MappedColumn(name, DbType, Size.Value) + ? new Column(name, DbType, Size.Value) : Precision.HasValue - ? new MappedColumn(name, DbType, Precision.Value, Scale ?? 0) - : new MappedColumn(name, DbType); + ? new Column(name, DbType, Precision.Value, Scale ?? 0) + : new Column(name, DbType); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs index 2d8d81c..1c638f9 100644 --- a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs @@ -25,7 +25,7 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, options.DatabaseContextFactory = dataAccessBuilder.Options.DatabaseContextFactory; }); - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/Shuttle.Core.Data/DatabaseContextCache.cs b/Shuttle.Core.Data/DatabaseContextCache.cs deleted file mode 100644 index d9d2a9c..0000000 --- a/Shuttle.Core.Data/DatabaseContextCache.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public class DatabaseContextService : IDatabaseContextService - { - private readonly List _databaseContexts = new List(); - private IDatabaseContext _current; - - public DatabaseContextService() - { - _current = null; - } - - public bool HasCurrent => _current != null; - - public IDatabaseContext Current - { - get - { - if (_current == null) - { - throw new InvalidOperationException(Resources.DatabaseContextMissing); - } - - return _current; - } - } - - public ActiveDatabaseContext Use(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - var current = _current; - - _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); - - if (_current == null) - { - throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); - } - - return new ActiveDatabaseContext(this, current); - } - - public IDatabaseContext Find(Predicate match) - { - Guard.AgainstNull(match, nameof(match)); - - return _databaseContexts.Find(match); - } - - public void Add(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - if (Find(context) != null) - { - throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); - } - - _databaseContexts.Add(context); - Use(context); - } - - public void Remove(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - var candidate = Find(context); - - if (candidate == null) - { - return; - } - - if (_current != null && candidate.Key.Equals(_current.Key)) - { - _current = null; - } - - _databaseContexts.Remove(candidate); - } - - private IDatabaseContext Find(IDatabaseContext context) - { - return Find(candidate => candidate.Key.Equals(context.Key)); - } - - public ActiveDatabaseContext Use(string name) - { - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - var current = _current; - - _current = _databaseContexts.Find(candidate => - candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - if (_current == null) - { - throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); - } - - return new ActiveDatabaseContext(this, current); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs new file mode 100644 index 0000000..1ad4899 --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -0,0 +1,179 @@ +using Shuttle.Core.Contract; +using System; +using System.Collections.Generic; + +namespace Shuttle.Core.Data +{ + public class DatabaseContextService : IDatabaseContextService + { + private class CallContextService : IDatabaseContextService + { + private readonly List _databaseContexts = new List(); + private IDatabaseContext _current; + + public CallContextService() + { + _current = null; + } + + public bool HasCurrent => _current != null; + + public IDatabaseContext Current + { + get + { + if (_current == null) + { + throw new InvalidOperationException(Resources.DatabaseContextMissing); + } + + return _current; + } + } + + public ActiveDatabaseContext Use(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); + + var current = _current; + + _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); + + if (_current == null) + { + throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); + } + + return new ActiveDatabaseContext(this, current); + } + + public IDatabaseContext Find(Predicate match) + { + Guard.AgainstNull(match, nameof(match)); + + return _databaseContexts.Find(match); + } + + public void Add(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); + + if (Find(context) != null) + { + throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); + } + + _databaseContexts.Add(context); + + Use(context); + } + + public void Remove(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); + + var candidate = Find(context); + + if (candidate == null) + { + return; + } + + if (_current != null && candidate.Key.Equals(_current.Key)) + { + _current = null; + } + + _databaseContexts.Remove(candidate); + } + + private IDatabaseContext Find(IDatabaseContext context) + { + return Find(candidate => candidate.Key.Equals(context.Key)); + } + + public ActiveDatabaseContext Use(string name) + { + Guard.AgainstNullOrEmptyString(name, nameof(name)); + + var current = _current; + + _current = _databaseContexts.Find(candidate => + candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + if (_current == null) + { + throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); + } + + return new ActiveDatabaseContext(this, current); + } + } + + public IDatabaseContext Current => Guarded().Current; + + public ActiveDatabaseContext Use(string name) + { + return Guarded().Use(name); + } + + public ActiveDatabaseContext Use(IDatabaseContext context) + { + return Guarded().Use(context); + } + + public IDatabaseContext Find(Predicate match) + { + return Guarded().Find(match); + } + + public bool Contains(string connectionString) + { + return Guarded().Contains(connectionString); + } + + public bool ContainsConnectionString(string connectionString) + { + return Guarded().ContainsConnectionString(connectionString); + } + + public IDatabaseContext GetConnectionString(string connectionString) + { + return Guarded().GetConnectionString(connectionString); + } + + public void Add(IDatabaseContext context) + { + Guarded().Add(context); + Guarded().Use(context); + } + + public void Remove(IDatabaseContext context) + { + Guarded().Remove(context); + } + + public bool HasCurrent => Guarded().HasCurrent; + + public IDatabaseContext Get(string connectionString) + { + return Guarded().Get(connectionString); + } + + private IDatabaseContextService Guarded() + { + const string key = "__database-context-service-item__"; + + var result = (IDatabaseContextService)Threading.CallContext.GetData(key); + + if (result != null) + { + return result; + } + + Threading.CallContext.SetData(key, new CallContextService()); + + return (IDatabaseContextService)Threading.CallContext.GetData(key); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 212195a..3d68359 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -22,26 +22,26 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) { Guard.AgainstNull(query, nameof(query)); - using var reader = GetReader(query, cancellationToken); - var results = new DataTable(); - if (reader != null) + using (var reader = await GetReader(query, cancellationToken).ConfigureAwait(false)) { - results.Load(await reader); + results.Load(reader); } return results; } - public Task> GetRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task> GetRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - return Task.FromResult(GetDataTable(query, cancellationToken).Result.Rows.Cast()); + var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); + + return table.Rows.Cast(); } public async Task GetRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { - var table = await GetDataTable(query, cancellationToken); + var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); if (table == null || table.Rows.Count == 0) { @@ -55,26 +55,30 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) { }; - public async Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { Guard.AgainstNull(query, nameof(query)); - await using var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); + + await using var _ = command.ConfigureAwait(false); + { + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); - return await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + return await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + } } public async Task Execute(IQuery query, CancellationToken cancellationToken = new CancellationToken()) { Guard.AgainstNull(query, nameof(query)); - await using var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + await using (var command = (DbCommand)_databaseContextService.Current.CreateCommand(query)) + { + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); - return await command.ExecuteNonQueryAsync(cancellationToken); + return await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + } } public async Task GetScalar(IQuery query, CancellationToken cancellationToken = new CancellationToken()) diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index 1faef8a..65d33ac 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -11,140 +11,140 @@ public static async Task Contains(this IDataRepository dataRepositor { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.Contains(Query.Create(sql), CancellationToken.None); } public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.Contains(Query.Create(sql, parameters), CancellationToken.None); } public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql), cancellationToken); + return await dataRepository.Contains(Query.Create(sql), cancellationToken); } public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.Contains(Query.Create(sql, parameters), cancellationToken); } public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItem(Query.Create(sql), CancellationToken.None); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItem(Query.Create(sql, parameters), CancellationToken.None); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItem(Query.Create(sql), cancellationToken); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItem(Query.Create(sql, parameters), cancellationToken); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItems(Query.Create(sql), CancellationToken.None); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItems(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItems(Query.Create(sql), cancellationToken); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItems(Query.Create(sql, parameters), cancellationToken); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRow(Query.Create(sql), CancellationToken.None); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRow(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRow(Query.Create(sql), cancellationToken); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRow(Query.Create(sql, parameters), cancellationToken); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRows(Query.Create(sql), CancellationToken.None); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRows(Query.Create(sql, parameters), CancellationToken.None); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRows(Query.Create(sql), cancellationToken); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRows(Query.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs similarity index 98% rename from Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs rename to Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs index e05ea5f..b4d7185 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextCacheExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -4,7 +4,7 @@ namespace Shuttle.Core.Data { - public static class DatabaseContextCacheExtensions + public static class DatabaseContextServiceExtensions { public static bool Contains(this IDatabaseContextService databaseContextService, IDatabaseContext context) { diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index a2ec2f2..24f54a1 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -13,168 +13,168 @@ public static async Task Execute(this IDatabaseGateway databaseGateway, str { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.Execute(Query.Create(sql), CancellationToken.None); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.Execute(Query.Create(sql, parameters), CancellationToken.None); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.Execute(Query.Create(sql), cancellationToken); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.Execute(Query.Create(sql, parameters), cancellationToken); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetDataTable(Query.Create(sql), CancellationToken.None); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetDataTable(Query.Create(sql), cancellationToken); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetDataTable(Query.Create(sql, parameters), CancellationToken.None); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetDataTable(Query.Create(sql, parameters), cancellationToken); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetReader(Query.Create(sql), CancellationToken.None); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetReader(Query.Create(sql, parameters), CancellationToken.None); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetReader(Query.Create(sql), cancellationToken); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReader(Query.Create(sql, parameters), cancellationToken); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRow(Query.Create(sql), CancellationToken.None); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRow(Query.Create(sql, parameters), CancellationToken.None); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRow(Query.Create(sql), cancellationToken); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRow(Query.Create(sql, parameters), cancellationToken); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRows(Query.Create(sql), CancellationToken.None); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRows(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRows(Query.Create(sql), cancellationToken); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRows(Query.Create(sql, parameters), cancellationToken); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetScalar(Query.Create(sql), CancellationToken.None); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetScalar(Query.Create(sql, parameters), CancellationToken.None); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetScalar(Query.Create(sql), cancellationToken); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetScalar(Query.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 68f7791..62c22f9 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -11,224 +11,224 @@ public static async Task MapItem(this IQueryMapper queryMapper, string { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItem(Query.Create(sql), CancellationToken.None); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItem(Query.Create(sql, parameters), CancellationToken.None); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItem(Query.Create(sql), cancellationToken); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItem(Query.Create(sql, parameters), cancellationToken); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItems(Query.Create(sql), CancellationToken.None); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItems(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItems(Query.Create(sql), cancellationToken); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItems(Query.Create(sql, parameters), cancellationToken); } public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObject(Query.Create(sql), CancellationToken.None); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObject(Query.Create(sql, parameters), CancellationToken.None); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObject(Query.Create(sql), cancellationToken); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObject(Query.Create(sql, parameters), cancellationToken); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObjects(Query.Create(sql), CancellationToken.None); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjects(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObjects(Query.Create(sql), cancellationToken); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjects(Query.Create(sql, parameters), cancellationToken); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRow(Query.Create(sql), CancellationToken.None); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRow(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRow(Query.Create(sql), cancellationToken); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRow(Query.Create(sql, parameters), cancellationToken); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRows(Query.Create(sql), CancellationToken.None); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRows(Query.Create(sql, parameters), CancellationToken.None); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRows(Query.Create(sql), cancellationToken); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRows(Query.Create(sql, parameters), cancellationToken); } public static async Task MapValue(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValue(Query.Create(sql), CancellationToken.None); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValue(Query.Create(sql, parameters), CancellationToken.None); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValue(Query.Create(sql), cancellationToken); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValue(Query.Create(sql, parameters), cancellationToken); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValues(Query.Create(sql), CancellationToken.None); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValues(Query.Create(sql, parameters), CancellationToken.None); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValues(Query.Create(sql), cancellationToken); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValues(Query.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index fcca7ba..915f475 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -11,7 +11,7 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()); Task Execute(IQuery query, CancellationToken cancellationToken = new CancellationToken()); Task GetScalar(IQuery query, CancellationToken cancellationToken = new CancellationToken()); Task GetDataTable(IQuery query, CancellationToken cancellationToken = new CancellationToken()); diff --git a/Shuttle.Core.Data/IMappedColumn.cs b/Shuttle.Core.Data/IMappedColumn.cs index 3d989e4..b22e89f 100644 --- a/Shuttle.Core.Data/IMappedColumn.cs +++ b/Shuttle.Core.Data/IMappedColumn.cs @@ -2,20 +2,20 @@ namespace Shuttle.Core.Data { - public interface IMappedColumn + public interface IColumn { - string ColumnName { get; } + string Name { get; } DbType DbType { get; } int? Size { get; } byte? Precision { get; } byte? Scale { get; } - string FlattenedColumnName(); - object RetrieveRawValueFrom(dynamic instance); - object RetrieveRawValueFrom(DataRow row); - object RetrieveRawValueFrom(IDataRecord record); - bool IsNullFor(dynamic instance); - bool IsNullFor(DataRow row); - bool IsNullFor(IDataRecord record); + string FlattenedName(); + object RawValue(dynamic instance); + object RawValue(DataRow row); + object RawValue(IDataRecord record); + bool IsNull(dynamic instance); + bool IsNull(DataRow row); + bool IsNull(IDataRecord record); IDbDataParameter CreateDataParameter(IDbCommand command, object value); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryParameter.cs b/Shuttle.Core.Data/IQueryParameter.cs index fcc0c01..84ebcde 100644 --- a/Shuttle.Core.Data/IQueryParameter.cs +++ b/Shuttle.Core.Data/IQueryParameter.cs @@ -2,6 +2,6 @@ { public interface IQueryParameter : IQuery { - IQueryParameter AddParameterValue(IMappedColumn column, object value); + IQueryParameter AddParameterValue(IColumn column, object value); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/ProcedureQuery.cs b/Shuttle.Core.Data/ProcedureQuery.cs index 0fe4e03..e416c17 100644 --- a/Shuttle.Core.Data/ProcedureQuery.cs +++ b/Shuttle.Core.Data/ProcedureQuery.cs @@ -6,7 +6,7 @@ namespace Shuttle.Core.Data { public class ProcedureQuery : IQueryParameter { - private readonly Dictionary _parameterValues; + private readonly Dictionary _parameterValues; private readonly string _procedure; public ProcedureQuery(string procedure) @@ -14,7 +14,7 @@ public ProcedureQuery(string procedure) Guard.AgainstNullOrEmptyString(procedure, nameof(procedure)); _procedure = procedure; - _parameterValues = new Dictionary(); + _parameterValues = new Dictionary(); } public void Prepare(IDbCommand command) @@ -30,7 +30,7 @@ public void Prepare(IDbCommand command) } } - public IQueryParameter AddParameterValue(IMappedColumn column, object value) + public IQueryParameter AddParameterValue(IColumn column, object value) { Guard.AgainstNull(column, nameof(column)); diff --git a/Shuttle.Core.Data/Properties/AssemblyInfo.cs b/Shuttle.Core.Data/Properties/AssemblyInfo.cs index 45be909..bd08bee 100644 --- a/Shuttle.Core.Data/Properties/AssemblyInfo.cs +++ b/Shuttle.Core.Data/Properties/AssemblyInfo.cs @@ -13,10 +13,10 @@ [assembly: AssemblyTitle(".NET Standard")] #endif -[assembly: AssemblyVersion("14.0.2.0")] -[assembly: AssemblyCopyright("Copyright (c) 2022, Eben Roux")] +[assembly: AssemblyVersion("15.0.0.0")] +[assembly: AssemblyCopyright("Copyright (c) 2023, Eben Roux")] [assembly: AssemblyProduct("Shuttle.Core.Data")] [assembly: AssemblyCompany("Eben Roux")] [assembly: AssemblyConfiguration("Release")] -[assembly: AssemblyInformationalVersion("14.0.2")] +[assembly: AssemblyInformationalVersion("15.0.0")] [assembly: ComVisible(false)] \ No newline at end of file diff --git a/Shuttle.Core.Data/RawQuery.cs b/Shuttle.Core.Data/Query.cs similarity index 69% rename from Shuttle.Core.Data/RawQuery.cs rename to Shuttle.Core.Data/Query.cs index a3973e4..a8d3cfd 100644 --- a/Shuttle.Core.Data/RawQuery.cs +++ b/Shuttle.Core.Data/Query.cs @@ -1,20 +1,19 @@ using System; using System.Collections.Generic; using System.Data; -using System.Dynamic; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { - public class RawQuery : IQueryParameter + public class Query : IQueryParameter { - private readonly Dictionary _parameterValues; + private readonly Dictionary _parameterValues; private readonly string _sql; - public RawQuery(string sql) + public Query(string sql) { _sql = Guard.AgainstNullOrEmptyString(sql, nameof(sql)); - _parameterValues = new Dictionary(); + _parameterValues = new Dictionary(); } public void Prepare(IDbCommand command) @@ -30,7 +29,7 @@ public void Prepare(IDbCommand command) } } - public IQueryParameter AddParameterValue(IMappedColumn column, object value) + public IQueryParameter AddParameterValue(IColumn column, object value) { Guard.AgainstNull(column, nameof(column)); @@ -41,12 +40,12 @@ public IQueryParameter AddParameterValue(IMappedColumn column, object value) public static IQueryParameter Create(string sql, params object[] args) { - return new RawQuery(args != null && args.Length > 0 ? string.Format(sql, args) : sql); + return new Query(args != null && args.Length > 0 ? string.Format(sql, args) : sql); } public static IQueryParameter Create(string sql, dynamic parameters) { - var result = new RawQuery(sql); + var result = new Query(sql); if (parameters != null) { @@ -54,7 +53,7 @@ public static IQueryParameter Create(string sql, dynamic parameters) { try { - result.AddParameterValue(new MappedColumn(pi.Name, pi.PropertyType, MappedColumn.GetDbType(pi.PropertyType)), pi.GetValue(parameters)); + result.AddParameterValue(new Column(pi.Name, pi.PropertyType, Column.GetDbType(pi.PropertyType)), pi.GetValue(parameters)); } catch { diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index 8c2383a..ed5e6b0 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Dynamic; using System.Linq; using System.Reflection; @@ -42,7 +43,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); @@ -60,7 +61,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe var result = new List(); - await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); @@ -76,7 +77,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -92,7 +93,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe var result = new List(); - await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -123,8 +124,7 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co { Guard.AgainstNull(query, nameof(query)); - await using var reader = await _databaseGateway.GetReader(query, cancellationToken - ); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); if (await reader.ReadAsync(cancellationToken)) @@ -141,7 +141,7 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co var result = new List(); - await using var reader = await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); diff --git a/Shuttle.Core.Data/ScriptProvider.cs b/Shuttle.Core.Data/ScriptProvider.cs index 631d2f7..0e2f87b 100644 --- a/Shuttle.Core.Data/ScriptProvider.cs +++ b/Shuttle.Core.Data/ScriptProvider.cs @@ -8,7 +8,7 @@ namespace Shuttle.Core.Data { public class ScriptProvider : IScriptProvider { - private static readonly object Padlock = new object(); + private static readonly object Lock = new object(); private readonly ScriptProviderOptions _options; private readonly IDatabaseContextService _databaseContextService; private readonly string[] _emptyFiles = Array.Empty(); @@ -34,7 +34,7 @@ public string Get(string scriptName, params object[] parameters) var key = Key(scriptName); - lock (Padlock) + lock (Lock) { if (!_scripts.ContainsKey(key)) { @@ -49,7 +49,7 @@ public string Get(string scriptName, params object[] parameters) private string Key(string scriptName) { - lock (Padlock) + lock (Lock) { return $"[{_databaseContextService.Current.ProviderName}]-{scriptName}"; } @@ -59,7 +59,7 @@ private void AddScript(string scriptName) { var key = Key(scriptName); - lock (Padlock) + lock (Lock) { if (_scripts.ContainsKey(key)) { diff --git a/Shuttle.Core.Data/Shuttle.Core.Data.csproj b/Shuttle.Core.Data/Shuttle.Core.Data.csproj index 58de6ff..6bc714a 100644 --- a/Shuttle.Core.Data/Shuttle.Core.Data.csproj +++ b/Shuttle.Core.Data/Shuttle.Core.Data.csproj @@ -20,6 +20,7 @@ + diff --git a/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs b/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs deleted file mode 100644 index 9c6d185..0000000 --- a/Shuttle.Core.Data/ThreadStaticDatabaseContextCache.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; - -namespace Shuttle.Core.Data -{ - public class ThreadStaticDatabaseContextService : IDatabaseContextService - { - [ThreadStatic] private static DatabaseContextService _service; - - public bool HasCurrent => GuardedCache().HasCurrent; - - public IDatabaseContext Current => GuardedCache().Current; - - public ActiveDatabaseContext Use(string name) - { - return GuardedCache().Use(name); - } - - public ActiveDatabaseContext Use(IDatabaseContext context) - { - return GuardedCache().Use(context); - } - - public IDatabaseContext Find(Predicate match) - { - return GuardedCache().Find(match); - } - - public void Add(IDatabaseContext context) - { - GuardedCache().Add(context); - } - - public void Remove(IDatabaseContext context) - { - GuardedCache().Remove(context); - } - - public IDatabaseContext Get(string name) - { - return GuardedCache().Get(name); - } - - private static DatabaseContextService GuardedCache() - { - return _service ?? (_service = new DatabaseContextService()); - } - } -} \ No newline at end of file From ae357f6025d0d851a33e56a553a300b2ea313715 Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 10 Feb 2023 20:08:17 +0200 Subject: [PATCH 03/37] - RawQuery --- .../MappedColumnFixture.cs | 24 +++---- .../Mapping/DataRowMapperFixture.cs | 18 +++--- .../Mapping/MappingFixture.cs | 2 +- .../Mapping/QueryMapperFixture.cs | 16 ++--- Shuttle.Core.Data.Tests/RawQueryFixture.cs | 8 +-- Shuttle.Core.Data/Column.cs | 6 +- .../Extensions/DataRepositoryExtensions.cs | 40 ++++++------ .../Extensions/DatabaseGatewayExtensions.cs | 48 +++++++------- .../Extensions/QueryMapperExtensions.cs | 64 +++++++++---------- Shuttle.Core.Data/{Query.cs => RawQuery.cs} | 8 +-- 10 files changed, 117 insertions(+), 117 deletions(-) rename Shuttle.Core.Data/{Query.cs => RawQuery.cs} (89%) diff --git a/Shuttle.Core.Data.Tests/MappedColumnFixture.cs b/Shuttle.Core.Data.Tests/MappedColumnFixture.cs index 07d605e..40894a8 100644 --- a/Shuttle.Core.Data.Tests/MappedColumnFixture.cs +++ b/Shuttle.Core.Data.Tests/MappedColumnFixture.cs @@ -107,14 +107,14 @@ public void Should_be_able_to_map_from_a_dynamic_instance() var mc = new Column("column1", DbType.AnsiString, 65); var missing = new Column("missing", DbType.AnsiString, 65); - Assert.AreEqual(default(string), mc.MapFrom(instanceA)); - Assert.AreEqual(default(string), missing.MapFrom(instanceA)); + Assert.AreEqual(default(string), mc.Value(instanceA)); + Assert.AreEqual(default(string), missing.Value(instanceA)); Assert.AreEqual(null, mc.RawValue(instanceA)); var instanceB = new { column1 = "value-1" }; - Assert.AreEqual("value-1", mc.MapFrom(instanceB)); - Assert.AreEqual(default(string), missing.MapFrom(instanceB)); + Assert.AreEqual("value-1", mc.Value(instanceB)); + Assert.AreEqual(default(string), missing.Value(instanceB)); Assert.AreEqual("value-1", mc.RawValue(instanceB)); } @@ -130,14 +130,14 @@ public void Should_be_able_to_map_from_a_data_row() var mc = new Column("column-1", DbType.AnsiString, 65); var missing = new Column("missing", DbType.AnsiString, 65); - Assert.AreEqual(default(string), mc.MapFrom(row)); - Assert.AreEqual(default(string), missing.MapFrom(row)); + Assert.AreEqual(default(string), mc.Value(row)); + Assert.AreEqual(default(string), missing.Value(row)); Assert.AreEqual(DBNull.Value, mc.RawValue(row)); row["column-1"] = "value-1"; - Assert.AreEqual("value-1", mc.MapFrom(row)); - Assert.AreEqual(default(string), missing.MapFrom(row)); + Assert.AreEqual("value-1", mc.Value(row)); + Assert.AreEqual(default(string), missing.Value(row)); Assert.AreEqual("value-1", mc.RawValue(row)); } @@ -151,13 +151,13 @@ public void Should_be_able_to_map_from_a_data_record() var column1 = new Column("column-1", DbType.AnsiString, 65); var column2 = new Column("column-2", DbType.AnsiString, 65); - Assert.AreEqual(default(string), column1.MapFrom(record)); - Assert.AreEqual(default(string), column2.MapFrom(record)); + Assert.AreEqual(default(string), column1.Value(record)); + Assert.AreEqual(default(string), column2.Value(record)); record.SetSqlString(0, new SqlString("value-1")); - Assert.AreEqual("value-1", column1.MapFrom(record)); - Assert.AreEqual(default(string), column2.MapFrom(record)); + Assert.AreEqual("value-1", column1.Value(record)); + Assert.AreEqual(default(string), column2.Value(record)); } [Test] diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 0a779b9..27fa112 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -13,7 +13,7 @@ public void Should_be_able_to_perform_basic_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = Query.Create(@" + var rowQuery = RawQuery.Create(@" select top 1 Id, Name, @@ -22,7 +22,7 @@ select top 1 BasicMapping "); - var rowsQuery = Query.Create(@" + var rowsQuery = RawQuery.Create(@" select Id, Name, @@ -53,7 +53,7 @@ public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_miss var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = Query.Create(@" + var rowQuery = RawQuery.Create(@" select top 1 Id, Name as NotMapped, @@ -62,7 +62,7 @@ Age as TheAge BasicMapping "); - var rowsQuery = Query.Create(@" + var rowsQuery = RawQuery.Create(@" select Id, Name, @@ -93,14 +93,14 @@ public void Should_be_able_to_perform_value_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = Query.Create(@" + var rowQuery = RawQuery.Create(@" select top 1 Id from BasicMapping "); - var rowsQuery = Query.Create(@" + var rowsQuery = RawQuery.Create(@" select Id from @@ -136,8 +136,8 @@ public void Should_be_able_to_perform_dynamic_mapping() Id = @Id "; - var rowQuery = Query.Create(rowSql).AddParameterValue(Columns.Id, id); - var rowsQuery = Query.Create(@" + var rowQuery = RawQuery.Create(rowSql).AddParameterValue(Columns.Id, id); + var rowsQuery = RawQuery.Create(@" select Id, Name, @@ -156,7 +156,7 @@ public void Should_be_able_to_perform_dynamic_mapping() Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(databaseGateway.GetRow(Query.Create(rowSql, new { Id = id })).Result); + item = dataRowMapper.MapItem(databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id })).Result); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index 0338b7e..bd34cc8 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -9,7 +9,7 @@ public void SetUp() { using (GetDatabaseContext()) { - GetDatabaseGateway().Execute(Query.Create(@" + GetDatabaseGateway().Execute(RawQuery.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index 39abb3b..f468a4e 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -13,7 +13,7 @@ public void Should_be_able_to_perform_basic_mapping() { var mapper = GetQueryMapper(); - var queryRow = Query.Create(@" + var queryRow = RawQuery.Create(@" select top 1 Id, Name, @@ -22,7 +22,7 @@ select top 1 BasicMapping "); - var queryRows = Query.Create(@" + var queryRows = RawQuery.Create(@" select Id, Name, @@ -52,7 +52,7 @@ public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_miss { var mapper = GetQueryMapper(); - var queryRow = Query.Create(@" + var queryRow = RawQuery.Create(@" select top 1 Id, Name as NotMapped, @@ -61,7 +61,7 @@ Age as TheAge BasicMapping "); - var queryRows = Query.Create(@" + var queryRows = RawQuery.Create(@" select Id, Name, @@ -91,14 +91,14 @@ public void Should_be_able_to_perform_value_mapping() { var mapper = GetQueryMapper(); - var queryRow = Query.Create(@" + var queryRow = RawQuery.Create(@" select top 1 Id from BasicMapping "); - var queryRows = Query.Create(@" + var queryRows = RawQuery.Create(@" select Id from @@ -121,7 +121,7 @@ public void Should_be_able_to_perform_dynamic_mapping() var databaseGateway = GetDatabaseGateway(); var queryMapper = GetQueryMapper(); - var queryRow = Query.Create(@" + var queryRow = RawQuery.Create(@" select top 1 Id, Name, @@ -130,7 +130,7 @@ select top 1 BasicMapping "); - var queryRows = Query.Create(@" + var queryRows = RawQuery.Create(@" select Id, Name, diff --git a/Shuttle.Core.Data.Tests/RawQueryFixture.cs b/Shuttle.Core.Data.Tests/RawQueryFixture.cs index d33e213..ddf0f71 100644 --- a/Shuttle.Core.Data.Tests/RawQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/RawQueryFixture.cs @@ -16,7 +16,7 @@ public void Should_be_able_prepare_a_query() var guid = Guid.NewGuid(); var mc = new Column("Id", DbType.Guid); - var query = new Query(sql).AddParameterValue(mc, guid); + var query = new RawQuery(sql).AddParameterValue(mc, guid); var dataParameterCollection = new Mock(); var command = new Mock(); @@ -38,9 +38,9 @@ public void Should_be_able_to_create_a_query() { const string sql = "select 1"; - var query1 = new Query(sql); - var query2 = Query.Create(sql); - var query3 = Query.Create("select {0}", 1); + var query1 = new RawQuery(sql); + var query2 = RawQuery.Create(sql); + var query3 = RawQuery.Create("select {0}", 1); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Column.cs b/Shuttle.Core.Data/Column.cs index 66961ee..ba3b6aa 100644 --- a/Shuttle.Core.Data/Column.cs +++ b/Shuttle.Core.Data/Column.cs @@ -276,7 +276,7 @@ private void GetUnderlyingSystemType() _underlyingSystemType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); } - public T MapFrom(dynamic instance) + public T Value(dynamic instance) { Guard.AgainstNull(instance, nameof(instance)); @@ -293,7 +293,7 @@ public T MapFrom(dynamic instance) return default; } - public T MapFrom(DataRow row) + public T Value(DataRow row) { Guard.AgainstNull(row, nameof(row)); @@ -307,7 +307,7 @@ public T MapFrom(DataRow row) return default; } - public T MapFrom(IDataRecord record) + public T Value(IDataRecord record) { Guard.AgainstNull(record, nameof(record)); diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index 65d33ac..1faef8a 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -11,140 +11,140 @@ public static async Task Contains(this IDataRepository dataRepositor { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(Query.Create(sql), CancellationToken.None); + return await dataRepository.Contains(RawQuery.Create(sql), CancellationToken.None); } public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(Query.Create(sql, parameters), CancellationToken.None); + return await dataRepository.Contains(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(Query.Create(sql), cancellationToken); + return await dataRepository.Contains(RawQuery.Create(sql), cancellationToken); } public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(Query.Create(sql, parameters), cancellationToken); + return await dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(Query.Create(sql), CancellationToken.None); + return await dataRepository.FetchItem(RawQuery.Create(sql), CancellationToken.None); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(Query.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(Query.Create(sql), cancellationToken); + return await dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(Query.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(Query.Create(sql), CancellationToken.None); + return await dataRepository.FetchItems(RawQuery.Create(sql), CancellationToken.None); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(Query.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(Query.Create(sql), cancellationToken); + return await dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(Query.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(Query.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql), CancellationToken.None); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(Query.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(Query.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(Query.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(Query.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql), CancellationToken.None); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(Query.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(Query.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(Query.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index 24f54a1..a2ec2f2 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -13,168 +13,168 @@ public static async Task Execute(this IDatabaseGateway databaseGateway, str { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(Query.Create(sql), CancellationToken.None); + return await databaseGateway.Execute(RawQuery.Create(sql), CancellationToken.None); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(Query.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.Execute(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(Query.Create(sql), cancellationToken); + return await databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(Query.Create(sql, parameters), cancellationToken); + return await databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(Query.Create(sql), CancellationToken.None); + return await databaseGateway.GetDataTable(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(Query.Create(sql), cancellationToken); + return await databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(Query.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(Query.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(Query.Create(sql), CancellationToken.None); + return await databaseGateway.GetReader(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(Query.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(Query.Create(sql), cancellationToken); + return await databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(Query.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(Query.Create(sql), CancellationToken.None); + return await databaseGateway.GetRow(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(Query.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(Query.Create(sql), cancellationToken); + return await databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(Query.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(Query.Create(sql), CancellationToken.None); + return await databaseGateway.GetRows(RawQuery.Create(sql), CancellationToken.None); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(Query.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(Query.Create(sql), cancellationToken); + return await databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(Query.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(Query.Create(sql), CancellationToken.None); + return await databaseGateway.GetScalar(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(Query.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(Query.Create(sql), cancellationToken); + return await databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(Query.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 62c22f9..68f7791 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -11,224 +11,224 @@ public static async Task MapItem(this IQueryMapper queryMapper, string { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapItem(RawQuery.Create(sql), CancellationToken.None); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItem(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(Query.Create(sql), cancellationToken); + return await queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItem(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapItems(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItems(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(Query.Create(sql), cancellationToken); + return await queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapObject(RawQuery.Create(sql), CancellationToken.None); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObject(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(Query.Create(sql), cancellationToken); + return await queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapObjects(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(Query.Create(sql), cancellationToken); + return await queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapRow(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRow(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(Query.Create(sql), cancellationToken); + return await queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapRows(RawQuery.Create(sql), CancellationToken.None); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRows(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(Query.Create(sql), cancellationToken); + return await queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task MapValue(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapValue(RawQuery.Create(sql), CancellationToken.None); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValue(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(Query.Create(sql), cancellationToken); + return await queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(Query.Create(sql), CancellationToken.None); + return await queryMapper.MapValues(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(Query.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValues(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(Query.Create(sql), cancellationToken); + return await queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(Query.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Query.cs b/Shuttle.Core.Data/RawQuery.cs similarity index 89% rename from Shuttle.Core.Data/Query.cs rename to Shuttle.Core.Data/RawQuery.cs index a8d3cfd..49b1c02 100644 --- a/Shuttle.Core.Data/Query.cs +++ b/Shuttle.Core.Data/RawQuery.cs @@ -5,12 +5,12 @@ namespace Shuttle.Core.Data { - public class Query : IQueryParameter + public class RawQuery : IQueryParameter { private readonly Dictionary _parameterValues; private readonly string _sql; - public Query(string sql) + public RawQuery(string sql) { _sql = Guard.AgainstNullOrEmptyString(sql, nameof(sql)); _parameterValues = new Dictionary(); @@ -40,12 +40,12 @@ public IQueryParameter AddParameterValue(IColumn column, object value) public static IQueryParameter Create(string sql, params object[] args) { - return new Query(args != null && args.Length > 0 ? string.Format(sql, args) : sql); + return new RawQuery(args != null && args.Length > 0 ? string.Format(sql, args) : sql); } public static IQueryParameter Create(string sql, dynamic parameters) { - var result = new Query(sql); + var result = new RawQuery(sql); if (parameters != null) { From 67ffd68ab4f20ac35476c5163d625b4b927bdef1 Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 10 Feb 2023 20:33:22 +0200 Subject: [PATCH 04/37] - default cancellation token --- Shuttle.Core.Data/DataRepository.cs | 10 +++++----- Shuttle.Core.Data/DatabaseGateway.cs | 12 ++++++------ Shuttle.Core.Data/IDataRepository.cs | 10 +++++----- Shuttle.Core.Data/IDatabaseGateway.cs | 12 ++++++------ Shuttle.Core.Data/IQueryMapper.cs | 16 ++++++++-------- Shuttle.Core.Data/QueryMapper.cs | 16 ++++++++-------- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index 3146b2e..eb0a188 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -17,35 +17,35 @@ public DataRepository(IDatabaseGateway databaseGateway, IDataRowMapper dataRo _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> FetchItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task> FetchItems(IQuery query, CancellationToken cancellationToken = default) { var rows = await _databaseGateway.GetRows(query, cancellationToken); return await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList()); } - public async Task FetchItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task FetchItem(IQuery query, CancellationToken cancellationToken = default) { var row = await _databaseGateway.GetRow(query, cancellationToken); return row == null ? default : _dataRowMapper.Map(row).Result; } - public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default) { var row = await _databaseGateway.GetRow(query, cancellationToken); return row == null ? null : _dataRowMapper.Map(row); } - public async Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default) { var rows = await _databaseGateway.GetRows(query, cancellationToken); return rows.MappedRowsUsing(_dataRowMapper); } - public async Task Contains(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task Contains(IQuery query, CancellationToken cancellationToken = default) { return await _databaseGateway.GetScalar(query, cancellationToken) == 1; } diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 3d68359..30bb590 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -18,7 +18,7 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public async Task GetDataTable(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task GetDataTable(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); @@ -32,14 +32,14 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) return results; } - public async Task> GetRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task> GetRows(IQuery query, CancellationToken cancellationToken = default) { var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); return table.Rows.Cast(); } - public async Task GetRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task GetRow(IQuery query, CancellationToken cancellationToken = default) { var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); @@ -55,7 +55,7 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) { }; - public async Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task GetReader(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); @@ -69,7 +69,7 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) } } - public async Task Execute(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task Execute(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); @@ -81,7 +81,7 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) } } - public async Task GetScalar(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task GetScalar(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); diff --git a/Shuttle.Core.Data/IDataRepository.cs b/Shuttle.Core.Data/IDataRepository.cs index 993a328..39da152 100644 --- a/Shuttle.Core.Data/IDataRepository.cs +++ b/Shuttle.Core.Data/IDataRepository.cs @@ -6,10 +6,10 @@ namespace Shuttle.Core.Data { public interface IDataRepository where T : class { - Task> FetchItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task FetchItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task Contains(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task> FetchItems(IQuery query, CancellationToken cancellationToken = default); + Task FetchItem(IQuery query, CancellationToken cancellationToken = default); + Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default); + Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default); + Task Contains(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index 915f475..dbbe448 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -11,11 +11,11 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - Task GetReader(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task Execute(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task GetScalar(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task GetDataTable(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task> GetRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task GetRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task GetReader(IQuery query, CancellationToken cancellationToken = default); + Task Execute(IQuery query, CancellationToken cancellationToken = default); + Task GetScalar(IQuery query, CancellationToken cancellationToken = default); + Task GetDataTable(IQuery query, CancellationToken cancellationToken = default); + Task> GetRows(IQuery query, CancellationToken cancellationToken = default); + Task GetRow(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryMapper.cs b/Shuttle.Core.Data/IQueryMapper.cs index 01ba26b..6c3bdc9 100644 --- a/Shuttle.Core.Data/IQueryMapper.cs +++ b/Shuttle.Core.Data/IQueryMapper.cs @@ -6,13 +6,13 @@ namespace Shuttle.Core.Data { public interface IQueryMapper { - Task> MapRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); - Task>> MapRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); - Task MapObject(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); - Task> MapObjects(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new(); - Task MapValue(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task> MapValues(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task MapItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()); - Task> MapItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()); + Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapObject(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapValue(IQuery query, CancellationToken cancellationToken = default); + Task> MapValues(IQuery query, CancellationToken cancellationToken = default); + Task MapItem(IQuery query, CancellationToken cancellationToken = default); + Task> MapItems(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index ed5e6b0..f938af3 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -25,21 +25,21 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> MapRow(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new() + public async Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); return _dataRowMapper.MapRow(await _databaseGateway.GetRow(query, cancellationToken)); } - public async Task>> MapRows(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new() + public async Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); return _dataRowMapper.MapRows(await _databaseGateway.GetRows(query, cancellationToken)); } - public async Task MapObject(IQuery query, CancellationToken cancellationToken= new CancellationToken()) where T : new() + public async Task MapObject(IQuery query, CancellationToken cancellationToken= default) where T : new() { Guard.AgainstNull(query, nameof(query)); @@ -55,7 +55,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return default; } - public async Task> MapObjects(IQuery query, CancellationToken cancellationToken = new CancellationToken()) where T : new() + public async Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); @@ -73,7 +73,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return result; } - public async Task MapValue(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task MapValue(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); @@ -87,7 +87,7 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return default; } - public async Task> MapValues(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task> MapValues(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); @@ -120,7 +120,7 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return result; } - public async Task MapItem(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task MapItem(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); @@ -135,7 +135,7 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return default; } - public async Task> MapItems(IQuery query, CancellationToken cancellationToken = new CancellationToken()) + public async Task> MapItems(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); From 7a3b666bd345009c4c99c8f5e622dbbcec75ada9 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 11 Feb 2023 10:43:06 +0200 Subject: [PATCH 05/37] - scoped database context service --- README.md | 4 +- .../DataRepositoryFixture.cs | 24 +-- .../DatabaseContextFactoryFixture.cs | 10 +- .../DatabaseContextServiceFixture.cs | 17 +- Shuttle.Core.Data.Tests/Fixture.cs | 5 +- .../Mapping/DataRowMapperFixture.cs | 39 ++-- .../Mapping/MappingFixture.cs | 9 +- .../Mapping/QueryMapperFixture.cs | 21 +- .../ServiceCollectionExtensions.cs | 2 +- Shuttle.Core.Data/DataRepository.cs | 4 +- Shuttle.Core.Data/DatabaseContextFactory.cs | 25 +-- Shuttle.Core.Data/DatabaseContextService.cs | 186 ++++++------------ Shuttle.Core.Data/DbConnectionFactory.cs | 15 -- Shuttle.Core.Data/IDatabaseContextFactory.cs | 10 +- Shuttle.Core.Data/Shuttle.Core.Data.csproj | 5 - 15 files changed, 153 insertions(+), 223 deletions(-) diff --git a/README.md b/README.md index af2a9e6..c348b10 100644 --- a/README.md +++ b/README.md @@ -438,13 +438,13 @@ public class OrderAssembler : IAssembler foreach (var orderRow in data.MappedRows()) { - var order = orderRow.Result; + var order = orderRow; foreach (var orderLineRow in data.MappedRows()) { if (orderLineRow.Row["OrderId"].Equals(order.OrderId)) { - order.AddLine(orderLineRow.Result); + order.AddLine(orderLineRow); } } diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index 705f6b5..68acb50 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -12,7 +12,7 @@ namespace Shuttle.Core.Data.Tests public class DataRepositoryFixture : Fixture { [Test] - public void Should_be_able_to_fetch_all_items() + public async Task Should_be_able_to_fetch_all_items() { var gateway = new Mock(); var mapper = new Mock>(); @@ -25,7 +25,7 @@ public void Should_be_able_to_fetch_all_items() var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchItems(query.Object).Result.ToList(); + var result = (await repository.FetchItems(query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -33,7 +33,7 @@ public void Should_be_able_to_fetch_all_items() } [Test] - public void Should_be_able_to_fetch_a_single_item() + public async Task Should_be_able_to_fetch_a_single_item() { var gateway = new Mock(); var mapper = new Mock>(); @@ -46,14 +46,14 @@ public void Should_be_able_to_fetch_a_single_item() var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchItem(query.Object).Result; + var result = await repository.FetchItem(query.Object); Assert.IsNotNull(result); Assert.AreSame(anObject, result); } [Test] - public void Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() + public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() { var gateway = new Mock(); var query = new Mock(); @@ -62,13 +62,13 @@ public void Should_be_able_to_get_default_when_fetching_a_single_item_that_is_no var repository = new DataRepository(gateway.Object, new Mock>().Object); - var result = repository.FetchItem(query.Object).Result; + var result = await repository.FetchItem(query.Object); Assert.IsNull(result); } [Test] - public void Should_be_able_to_call_contains() + public async Task Should_be_able_to_call_contains() { var gateway = new Mock(); var query = new Mock(); @@ -77,11 +77,11 @@ public void Should_be_able_to_call_contains() var repository = new DataRepository(gateway.Object, new Mock>().Object); - Assert.IsTrue(repository.Contains(query.Object).Result); + Assert.That(await repository.Contains(query.Object), Is.True); } [Test] - public void Should_be_able_to_fetch_mapped_rows() + public async Task Should_be_able_to_fetch_mapped_rows() { var gateway = new Mock(); var mapper = new Mock>(); @@ -95,7 +95,7 @@ public void Should_be_able_to_fetch_mapped_rows() var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchMappedRows(query.Object).Result.ToList(); + var result = (await repository.FetchMappedRows(query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -104,7 +104,7 @@ public void Should_be_able_to_fetch_mapped_rows() } [Test] - public void Should_be_able_to_fetch_a_single_row() + public async Task Should_be_able_to_fetch_a_single_row() { var gateway = new Mock(); var mapper = new Mock>(); @@ -118,7 +118,7 @@ public void Should_be_able_to_fetch_a_single_row() var repository = new DataRepository(gateway.Object, mapper.Object); - var result = repository.FetchMappedRow(query.Object).Result; + var result = await repository.FetchMappedRow(query.Object); Assert.IsNotNull(result); Assert.AreSame(dataRow, result.Row); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 5ad690d..973ab4f 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -2,6 +2,8 @@ using System.Data; using System.Data.Common; using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; @@ -21,12 +23,12 @@ public void Should_be_able_to_create_a_database_context() } [Test] - public void Should_be_able_to_get_an_existing_database_context() - { + public async Task Should_be_able_to_get_an_existing_database_context() + { var factory = GetDatabaseContextFactory(); - using (var context = factory.Create(DefaultConnectionStringName)) - using (var existingContext = factory.Create(DefaultConnectionStringName)) + using (var context = await factory.Create(DefaultConnectionStringName)) + using (var existingContext = await factory.Create(DefaultConnectionStringName)) { Assert.IsNotNull(context); Assert.IsNotNull(existingContext); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index df0a002..a11495d 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -1,5 +1,6 @@ using System.Data; using System.Data.Common; +using System.Threading.Tasks; using Moq; using NUnit.Framework; @@ -8,7 +9,7 @@ namespace Shuttle.Core.Data.Tests public class DatabaseContextServiceFixture : Fixture { [Test] - public void Should_be_able_to_use_different_contexts() + public async Task Should_be_able_to_use_different_contexts() { var service = new DatabaseContextService(); @@ -23,6 +24,8 @@ public void Should_be_able_to_use_different_contexts() using (service.Use("mock-1")) { Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + + await AssertContextFlow(service, "mock-1"); } Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); @@ -34,5 +37,17 @@ public void Should_be_able_to_use_different_contexts() Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); } + + private async Task AssertContextFlow(IDatabaseContextService service, string name) + { + Assert.That(service.Current, Is.Not.Null); + Assert.That(service.Current.Name, Is.EqualTo(name)); + + await Task.Run(() => + { + Assert.That(service.Current, Is.Not.Null); + Assert.That(service.Current.Name, Is.EqualTo(name)); + }); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Fixture.cs b/Shuttle.Core.Data.Tests/Fixture.cs index dcae96f..d243f64 100644 --- a/Shuttle.Core.Data.Tests/Fixture.cs +++ b/Shuttle.Core.Data.Tests/Fixture.cs @@ -1,5 +1,6 @@ using System; using System.Data.Common; +using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -42,9 +43,9 @@ protected IDatabaseContextFactory GetDatabaseContextFactory() return Provider.GetRequiredService(); } - protected IDatabaseContext GetDatabaseContext() + protected async Task GetDatabaseContext() { - return GetDatabaseContextFactory().Create(DefaultConnectionStringName); + return await GetDatabaseContextFactory().Create(DefaultConnectionStringName).ConfigureAwait(false); } protected IDatabaseGateway GetDatabaseGateway() diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 27fa112..b48fe0a 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using NUnit.Framework; namespace Shuttle.Core.Data.Tests @@ -8,7 +9,7 @@ namespace Shuttle.Core.Data.Tests public class DataRowMapperFixture : MappingFixture { [Test] - public void Should_be_able_to_perform_basic_mapping() + public async Task Should_be_able_to_perform_basic_mapping() { var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); @@ -31,16 +32,16 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) + using (await GetDatabaseContext()) { - var item = dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery).Result); - var items = dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery).Result); + var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery).Result); - var mappedRows = dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery).Result); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -48,7 +49,7 @@ select top 1 } [Test] - public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() + public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() { var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); @@ -71,16 +72,16 @@ Age as TheAge BasicMapping "); - using (GetDatabaseContext()) + using (await GetDatabaseContext()) { - var item = dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery).Result); - var items = dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery).Result); + var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery).Result); - var mappedRows = dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery).Result); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -88,7 +89,7 @@ Age as TheAge } [Test] - public void Should_be_able_to_perform_value_mapping() + public async Task Should_be_able_to_perform_value_mapping() { var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); @@ -109,8 +110,8 @@ select top 1 using (GetDatabaseContext()) { - var value = dataRowMapper.MapValue(databaseGateway.GetRow(rowQuery).Result); - var values = dataRowMapper.MapValues(databaseGateway.GetRows(rowsQuery).Result); + var value = dataRowMapper.MapValue(await databaseGateway.GetRow(rowQuery)); + var values = dataRowMapper.MapValues(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -118,7 +119,7 @@ select top 1 } [Test] - public void Should_be_able_to_perform_dynamic_mapping() + public async Task Should_be_able_to_perform_dynamic_mapping() { var id = new Guid("B5E0088E-4873-4244-9B91-1059E0383C3E"); @@ -148,15 +149,15 @@ public void Should_be_able_to_perform_dynamic_mapping() using (GetDatabaseContext()) { - var item = dataRowMapper.MapItem(databaseGateway.GetRow(rowQuery).Result); + var item = dataRowMapper.MapItem(await databaseGateway.GetRow(rowQuery)); Assert.IsNotNull(item); - var items = dataRowMapper.MapItems(databaseGateway.GetRows(rowsQuery).Result); + var items = dataRowMapper.MapItems(await databaseGateway.GetRows(rowsQuery)); Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id })).Result); + item = dataRowMapper.MapItem(await databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id }))); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index bd34cc8..0d4b23f 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -1,15 +1,16 @@ -using NUnit.Framework; +using System.Threading.Tasks; +using NUnit.Framework; namespace Shuttle.Core.Data.Tests; public class MappingFixture : Fixture { [SetUp] - public void SetUp() + public async Task SetUp() { - using (GetDatabaseContext()) + using (await GetDatabaseContext()) { - GetDatabaseGateway().Execute(RawQuery.Create(@" + await GetDatabaseGateway().Execute(RawQuery.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index f468a4e..c2ae170 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; namespace Shuttle.Core.Data.Tests @@ -9,7 +10,7 @@ namespace Shuttle.Core.Data.Tests public class QueryMapperFixture : MappingFixture { [Test] - public void Should_be_able_to_perform_basic_mapping() + public async Task Should_be_able_to_perform_basic_mapping() { var mapper = GetQueryMapper(); @@ -31,16 +32,16 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) + using (await GetDatabaseContext()) { - var item = mapper.MapObject(queryRow).Result; - var items = mapper.MapObjects(queryRows).Result; + var item = await mapper.MapObject(queryRow); + var items = await mapper.MapObjects(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRow(queryRow).Result; - var mappedRows = mapper.MapRows(queryRows).Result; + var mappedRow = await mapper.MapRow(queryRow); + var mappedRows = await mapper.MapRows(queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -48,7 +49,7 @@ select top 1 } [Test] - public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() + public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() { var mapper = GetQueryMapper(); @@ -72,14 +73,14 @@ Age as TheAge using (GetDatabaseContext()) { - var item = mapper.MapObject(queryRow).Result; - var items = mapper.MapObjects(queryRows).Result; + var item = await mapper.MapObject(queryRow); + var items = await mapper.MapObjects(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); var mappedRow = mapper.MapRow(queryRow).Result; - var mappedRows = mapper.MapRows(queryRows).Result; + var mappedRows = await mapper.MapRows(queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); diff --git a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs index 1c638f9..7ea4f0e 100644 --- a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs @@ -25,7 +25,7 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, options.DatabaseContextFactory = dataAccessBuilder.Options.DatabaseContextFactory; }); - services.TryAddSingleton(); + services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index eb0a188..84d9525 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -21,14 +21,14 @@ public async Task> FetchItems(IQuery query, CancellationToken can { var rows = await _databaseGateway.GetRows(query, cancellationToken); - return await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList()); + return (IEnumerable)await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); } public async Task FetchItem(IQuery query, CancellationToken cancellationToken = default) { var row = await _databaseGateway.GetRow(query, cancellationToken); - return row == null ? default : _dataRowMapper.Map(row).Result; + return await Task.FromResult(row == null ? default : _dataRowMapper.Map(row).Result); } public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default) diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 8d8153c..72331ca 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -1,5 +1,6 @@ using System; using System.Data.Common; +using System.Threading.Tasks; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; @@ -24,7 +25,7 @@ public DatabaseContextFactory(IOptionsMonitor connectio DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public IDatabaseContext Create(string name) + public async Task Create(string name) { var connectionStringOptions = _connectionStringOptions.Get(name); @@ -33,40 +34,42 @@ public IDatabaseContext Create(string name) throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, name)); } - return Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString).WithName(name); + var databaseContext = await Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString).ConfigureAwait(false); + + return databaseContext.WithName(name); } - public IDatabaseContext Create(string providerName, string connectionString) + public async Task Create(string providerName, string connectionString) { - return DatabaseContextService.ContainsConnectionString(connectionString) + return await Task.FromResult(DatabaseContextService.ContainsConnectionString(connectionString) ? DatabaseContextService.GetConnectionString(connectionString).Suppressed() : new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), - DbCommandFactory, DatabaseContextService); + DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); } - public IDatabaseContext Create(string providerName, DbConnection dbConnection) + public async Task Create(string providerName, DbConnection dbConnection) { Guard.AgainstNull(dbConnection, nameof(dbConnection)); - return DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) + return await Task.FromResult(DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) ? DatabaseContextService.GetConnectionString(dbConnection.ConnectionString).Suppressed() - : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService); + : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); } public IDbConnectionFactory DbConnectionFactory { get; } public IDbCommandFactory DbCommandFactory { get; } public IDatabaseContextService DatabaseContextService { get; } - public IDatabaseContext Create() + public async Task Create() { if (!string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName)) { - return Create(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName); + return await Create(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName).ConfigureAwait(false); } if (!string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName) && !string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionString)) { - return Create(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName, _dataAccessOptions.DatabaseContextFactory.DefaultConnectionString); + return await Create(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName, _dataAccessOptions.DatabaseContextFactory.DefaultConnectionString).ConfigureAwait(false); } throw new InvalidOperationException(Resources.DatabaseContextFactoryOptionsException); diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 1ad4899..7be295c 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -1,179 +1,105 @@ -using Shuttle.Core.Contract; -using System; +using System; using System.Collections.Generic; +using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public class DatabaseContextService : IDatabaseContextService { - private class CallContextService : IDatabaseContextService - { - private readonly List _databaseContexts = new List(); - private IDatabaseContext _current; - - public CallContextService() - { - _current = null; - } + private readonly List _databaseContexts = new List(); + private IDatabaseContext _current = null; - public bool HasCurrent => _current != null; - - public IDatabaseContext Current - { - get - { - if (_current == null) - { - throw new InvalidOperationException(Resources.DatabaseContextMissing); - } - - return _current; - } - } + public bool HasCurrent => _current != null; - public ActiveDatabaseContext Use(IDatabaseContext context) + public IDatabaseContext Current + { + get { - Guard.AgainstNull(context, nameof(context)); - - var current = _current; - - _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); - if (_current == null) { - throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); + throw new InvalidOperationException(Resources.DatabaseContextMissing); } - return new ActiveDatabaseContext(this, current); + return _current; } + } - public IDatabaseContext Find(Predicate match) - { - Guard.AgainstNull(match, nameof(match)); - - return _databaseContexts.Find(match); - } - - public void Add(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - if (Find(context) != null) - { - throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); - } - - _databaseContexts.Add(context); - - Use(context); - } - - public void Remove(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - var candidate = Find(context); - - if (candidate == null) - { - return; - } - - if (_current != null && candidate.Key.Equals(_current.Key)) - { - _current = null; - } + public ActiveDatabaseContext Use(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); - _databaseContexts.Remove(candidate); - } + var current = _current; - private IDatabaseContext Find(IDatabaseContext context) - { - return Find(candidate => candidate.Key.Equals(context.Key)); - } + _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); - public ActiveDatabaseContext Use(string name) + if (_current == null) { - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - var current = _current; - - _current = _databaseContexts.Find(candidate => - candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - if (_current == null) - { - throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); - } - - return new ActiveDatabaseContext(this, current); + throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); } - } - - public IDatabaseContext Current => Guarded().Current; - public ActiveDatabaseContext Use(string name) - { - return Guarded().Use(name); - } - - public ActiveDatabaseContext Use(IDatabaseContext context) - { - return Guarded().Use(context); + return new ActiveDatabaseContext(this, current); } public IDatabaseContext Find(Predicate match) { - return Guarded().Find(match); - } + Guard.AgainstNull(match, nameof(match)); - public bool Contains(string connectionString) - { - return Guarded().Contains(connectionString); + return _databaseContexts.Find(match); } - public bool ContainsConnectionString(string connectionString) + public void Add(IDatabaseContext context) { - return Guarded().ContainsConnectionString(connectionString); - } + Guard.AgainstNull(context, nameof(context)); - public IDatabaseContext GetConnectionString(string connectionString) - { - return Guarded().GetConnectionString(connectionString); - } + if (Find(context) != null) + { + throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); + } + + _databaseContexts.Add(context); - public void Add(IDatabaseContext context) - { - Guarded().Add(context); - Guarded().Use(context); + Use(context); } public void Remove(IDatabaseContext context) { - Guarded().Remove(context); - } + Guard.AgainstNull(context, nameof(context)); + + var candidate = Find(context); - public bool HasCurrent => Guarded().HasCurrent; + if (candidate == null) + { + return; + } - public IDatabaseContext Get(string connectionString) + if (_current != null && candidate.Key.Equals(_current.Key)) + { + _current = null; + } + + _databaseContexts.Remove(candidate); + } + + private IDatabaseContext Find(IDatabaseContext context) { - return Guarded().Get(connectionString); + return Find(candidate => candidate.Key.Equals(context.Key)); } - private IDatabaseContextService Guarded() + public ActiveDatabaseContext Use(string name) { - const string key = "__database-context-service-item__"; + Guard.AgainstNullOrEmptyString(name, nameof(name)); - var result = (IDatabaseContextService)Threading.CallContext.GetData(key); + var current = _current; - if (result != null) + _current = _databaseContexts.Find(candidate => + candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + if (_current == null) { - return result; + throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); } - Threading.CallContext.SetData(key, new CallContextService()); - - return (IDatabaseContextService)Threading.CallContext.GetData(key); + return new ActiveDatabaseContext(this, current); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DbConnectionFactory.cs b/Shuttle.Core.Data/DbConnectionFactory.cs index 11f4361..3ae036e 100644 --- a/Shuttle.Core.Data/DbConnectionFactory.cs +++ b/Shuttle.Core.Data/DbConnectionFactory.cs @@ -1,23 +1,12 @@ using System; using System.Data; -#if !NETSTANDARD2_0 using System.Data.Common; -#endif using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public class DbConnectionFactory : IDbConnectionFactory { -#if (NETSTANDARD2_0) - private readonly IDbProviderFactories _providerFactories; - - public DbConnectionFactory(IDbProviderFactories providerFactories) - { - _providerFactories = Guard.AgainstNull(providerFactories, nameof(providerFactories)); - } -#endif - public event EventHandler DbConnectionCreated = delegate { }; @@ -27,11 +16,7 @@ public DbConnection Create(string providerName, string connectionString) Guard.AgainstNullOrEmptyString(providerName, nameof(providerName)); Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); -#if NETSTANDARD2_0 - var dbProviderFactory = _providerFactories.GetFactory(providerName); -#else var dbProviderFactory = DbProviderFactories.GetFactory(providerName); -#endif var connection = dbProviderFactory.CreateConnection(); if (connection == null) diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index d2bf9ed..d698b69 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -1,14 +1,14 @@ -using System.Data; using System.Data.Common; +using System.Threading.Tasks; namespace Shuttle.Core.Data { public interface IDatabaseContextFactory { - IDatabaseContext Create(string name); - IDatabaseContext Create(string providerName, string connectionString); - IDatabaseContext Create(string providerName, DbConnection dbConnection); - IDatabaseContext Create(); + Task Create(string name); + Task Create(string providerName, string connectionString); + Task Create(string providerName, DbConnection dbConnection); + Task Create(); IDbConnectionFactory DbConnectionFactory { get; } IDbCommandFactory DbCommandFactory { get; } diff --git a/Shuttle.Core.Data/Shuttle.Core.Data.csproj b/Shuttle.Core.Data/Shuttle.Core.Data.csproj index 6bc714a..f47446f 100644 --- a/Shuttle.Core.Data/Shuttle.Core.Data.csproj +++ b/Shuttle.Core.Data/Shuttle.Core.Data.csproj @@ -37,9 +37,4 @@ Resources.Designer.cs - - - - - From e7407a8b053b2e7c76c54a79238df54e8d3d3c0c Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 12 Feb 2023 17:38:01 +0200 Subject: [PATCH 06/37] - removed ambient connections --- .../DataRepositoryFixture.cs | 40 +++--- .../DatabaseContextFactoryFixture.cs | 4 +- .../DatabaseContextFixture.cs | 16 +-- .../DatabaseContextServiceFixture.cs | 53 -------- .../Mapping/DataRowMapperFixture.cs | 34 ++--- .../Mapping/MappingFixture.cs | 5 +- .../Mapping/QueryMapperFixture.cs | 37 +++-- .../ScriptProviderFixture.cs | 33 ++--- .../ScriptProviderNegativeFixture.cs | 17 --- Shuttle.Core.Data/ActiveDatabaseContext.cs | 27 ---- .../ServiceCollectionExtensions.cs | 1 - Shuttle.Core.Data/DataRepository.cs | 20 +-- Shuttle.Core.Data/DatabaseContext.cs | 20 +-- Shuttle.Core.Data/DatabaseContextFactory.cs | 15 +- Shuttle.Core.Data/DatabaseContextService.cs | 105 -------------- Shuttle.Core.Data/DatabaseGateway.cs | 43 ++---- .../Extensions/DataRepositoryExtensions.cs | 80 +++++------ .../DatabaseContextServiceExtensions.cs | 91 ------------- .../Extensions/DatabaseGatewayExtensions.cs | 96 ++++++------- .../Extensions/QueryMapperExtensions.cs | 128 +++++++++--------- Shuttle.Core.Data/IDataRepository.cs | 10 +- Shuttle.Core.Data/IDatabaseContextFactory.cs | 1 - Shuttle.Core.Data/IDatabaseContextService.cs | 14 -- Shuttle.Core.Data/IDatabaseGateway.cs | 13 +- Shuttle.Core.Data/IQueryMapper.cs | 16 +-- Shuttle.Core.Data/IScriptProvider.cs | 4 +- Shuttle.Core.Data/QueryMapper.cs | 32 ++--- Shuttle.Core.Data/ScriptProvider.cs | 45 +++--- 28 files changed, 318 insertions(+), 682 deletions(-) delete mode 100644 Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs delete mode 100644 Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs delete mode 100644 Shuttle.Core.Data/ActiveDatabaseContext.cs delete mode 100644 Shuttle.Core.Data/DatabaseContextService.cs delete mode 100644 Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs delete mode 100644 Shuttle.Core.Data/IDatabaseContextService.cs diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index 68acb50..e821db7 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -14,18 +14,19 @@ public class DataRepositoryFixture : Fixture [Test] public async Task Should_be_able_to_fetch_all_items() { + var context = new Mock(); var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); + gateway.Setup(m => m.GetRows(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = (await repository.FetchItems(query.Object)).ToList(); + var result = (await repository.FetchItems(context.Object, query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -35,18 +36,19 @@ public async Task Should_be_able_to_fetch_all_items() [Test] public async Task Should_be_able_to_fetch_a_single_item() { - var gateway = new Mock(); + var context = new Mock(); + var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + gateway.Setup(m => m.GetRow(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = await repository.FetchItem(query.Object); + var result = await repository.FetchItem(context.Object, query.Object); Assert.IsNotNull(result); Assert.AreSame(anObject, result); @@ -55,14 +57,15 @@ public async Task Should_be_able_to_fetch_a_single_item() [Test] public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() { - var gateway = new Mock(); + var context = new Mock(); + var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); + gateway.Setup(m => m.GetRow(context.Object, query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); var repository = new DataRepository(gateway.Object, new Mock>().Object); - var result = await repository.FetchItem(query.Object); + var result = await repository.FetchItem(context.Object, query.Object); Assert.IsNull(result); } @@ -70,32 +73,34 @@ public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that [Test] public async Task Should_be_able_to_call_contains() { - var gateway = new Mock(); + var context = new Mock(); + var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetScalar(query.Object, CancellationToken.None)).ReturnsAsync(1); + gateway.Setup(m => m.GetScalar(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(1); var repository = new DataRepository(gateway.Object, new Mock>().Object); - Assert.That(await repository.Contains(query.Object), Is.True); + Assert.That(await repository.Contains(context.Object, query.Object), Is.True); } [Test] public async Task Should_be_able_to_fetch_mapped_rows() { - var gateway = new Mock(); + var context = new Mock(); + var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); + gateway.Setup(m => m.GetRows(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = (await repository.FetchMappedRows(query.Object)).ToList(); + var result = (await repository.FetchMappedRows(context.Object, query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -106,19 +111,20 @@ public async Task Should_be_able_to_fetch_mapped_rows() [Test] public async Task Should_be_able_to_fetch_a_single_row() { - var gateway = new Mock(); + var context = new Mock(); + var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + gateway.Setup(m => m.GetRow(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = await repository.FetchMappedRow(query.Object); + var result = await repository.FetchMappedRow(context.Object, query.Object); Assert.IsNotNull(result); Assert.AreSame(dataRow, result.Row); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 973ab4f..6aa3c2d 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -23,7 +23,7 @@ public void Should_be_able_to_create_a_database_context() } [Test] - public async Task Should_be_able_to_get_an_existing_database_context() + public async Task Should_be_able_to_get_an_independent_database_context() { var factory = GetDatabaseContextFactory(); @@ -33,7 +33,7 @@ public async Task Should_be_able_to_get_an_existing_database_context() Assert.IsNotNull(context); Assert.IsNotNull(existingContext); - Assert.AreSame(existingContext.Connection, context.Connection); + Assert.That(existingContext.Connection, Is.Not.SameAs(context.Connection)); } } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index e4e8bca..0325409 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -15,7 +15,7 @@ public void Should_not_be_able_to_create_an_invalid_connection() { Assert.Throws(() => { - using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) + using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object)) { } }); @@ -28,7 +28,7 @@ public void Should_not_be_able_to_create_a_non_existent_connection() { using ( new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object, new DatabaseContextService())) + new Mock().Object)) { } }); @@ -39,7 +39,7 @@ public void Should_be_able_to_create_a_valid_connection() { using ( new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object)) { } } @@ -50,7 +50,7 @@ public void Should_be_able_to_begin_and_commit_a_transaction() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object)) { connection.BeginTransaction(); connection.CommitTransaction(); @@ -63,7 +63,7 @@ public void Should_be_able_to_begin_and_rollback_a_transaction() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object)) { connection.BeginTransaction(); } @@ -75,7 +75,7 @@ public void Should_be_able_to_call_commit_without_a_transaction() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object)) { connection.CommitTransaction(); } @@ -87,7 +87,7 @@ public void Should_be_able_to_call_dispose_more_than_once() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object)) { connection.Dispose(); connection.Dispose(); @@ -105,7 +105,7 @@ public void Should_be_able_to_create_a_command() dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); using ( - var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) + var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object)) { connection.CreateCommand(query.Object); } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs deleted file mode 100644 index a11495d..0000000 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Data; -using System.Data.Common; -using System.Threading.Tasks; -using Moq; -using NUnit.Framework; - -namespace Shuttle.Core.Data.Tests -{ - public class DatabaseContextServiceFixture : Fixture - { - [Test] - public async Task Should_be_able_to_use_different_contexts() - { - var service = new DatabaseContextService(); - - var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, service).WithName("mock-1"); - - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - - var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, service).WithName("mock-2"); - - Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - - using (service.Use("mock-1")) - { - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - - await AssertContextFlow(service, "mock-1"); - } - - Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - - using (service.Use(context1)) - { - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - } - - Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - } - - private async Task AssertContextFlow(IDatabaseContextService service, string name) - { - Assert.That(service.Current, Is.Not.Null); - Assert.That(service.Current.Name, Is.EqualTo(name)); - - await Task.Run(() => - { - Assert.That(service.Current, Is.Not.Null); - Assert.That(service.Current.Name, Is.EqualTo(name)); - }); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index b48fe0a..51fba03 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -32,16 +32,16 @@ select top 1 BasicMapping "); - using (await GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); + var item = dataRowMapper.MapObject(await databaseGateway.GetRow(databaseContext, rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(databaseContext, rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(databaseContext, rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(databaseContext, rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -72,16 +72,16 @@ Age as TheAge BasicMapping "); - using (await GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); + var item = dataRowMapper.MapObject(await databaseGateway.GetRow(databaseContext, rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(databaseContext, rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(databaseContext, rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(databaseContext, rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -108,10 +108,10 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var value = dataRowMapper.MapValue(await databaseGateway.GetRow(rowQuery)); - var values = dataRowMapper.MapValues(await databaseGateway.GetRows(rowsQuery)); + var value = dataRowMapper.MapValue(await databaseGateway.GetRow(databaseContext, rowQuery)); + var values = dataRowMapper.MapValues(await databaseGateway.GetRows(databaseContext, rowsQuery)); Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -147,17 +147,17 @@ public async Task Should_be_able_to_perform_dynamic_mapping() BasicMapping "); - using (GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var item = dataRowMapper.MapItem(await databaseGateway.GetRow(rowQuery)); + var item = dataRowMapper.MapItem(await databaseGateway.GetRow(databaseContext, rowQuery)); Assert.IsNotNull(item); - var items = dataRowMapper.MapItems(await databaseGateway.GetRows(rowsQuery)); + var items = dataRowMapper.MapItems(await databaseGateway.GetRows(databaseContext, rowsQuery)); Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(await databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id }))); + item = dataRowMapper.MapItem(await databaseGateway.GetRow(databaseContext, RawQuery.Create(rowSql, new { Id = id }))); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index 0d4b23f..e4c91bd 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; namespace Shuttle.Core.Data.Tests; @@ -8,9 +9,9 @@ public class MappingFixture : Fixture [SetUp] public async Task SetUp() { - using (await GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - await GetDatabaseGateway().Execute(RawQuery.Create(@" + await GetDatabaseGateway().Execute(databaseContext, RawQuery.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index c2ae170..191dc5d 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -32,16 +32,16 @@ select top 1 BasicMapping "); - using (await GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var item = await mapper.MapObject(queryRow); - var items = await mapper.MapObjects(queryRows); + var item = await mapper.MapObject(databaseContext, queryRow); + var items = await mapper.MapObjects(databaseContext, queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = await mapper.MapRow(queryRow); - var mappedRows = await mapper.MapRows(queryRows); + var mappedRow = await mapper.MapRow(databaseContext, queryRow); + var mappedRows = await mapper.MapRows(databaseContext, queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -71,16 +71,16 @@ Age as TheAge BasicMapping "); - using (GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var item = await mapper.MapObject(queryRow); - var items = await mapper.MapObjects(queryRows); + var item = await mapper.MapObject(databaseContext, queryRow); + var items = await mapper.MapObjects(databaseContext, queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRow(queryRow).Result; - var mappedRows = await mapper.MapRows(queryRows); + var mappedRow = mapper.MapRow(databaseContext, queryRow).Result; + var mappedRows = await mapper.MapRows(databaseContext, queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -88,7 +88,7 @@ Age as TheAge } [Test] - public void Should_be_able_to_perform_value_mapping() + public async Task Should_be_able_to_perform_value_mapping() { var mapper = GetQueryMapper(); @@ -106,10 +106,10 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var value = mapper.MapValue(queryRow).Result; - var values = mapper.MapValues(queryRows).Result; + var value = mapper.MapValue(databaseContext, queryRow).Result; + var values = mapper.MapValues(databaseContext, queryRows).Result; Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -117,9 +117,8 @@ select top 1 } [Test] - public void Should_be_able_to_perform_dynamic_mapping() + public async Task Should_be_able_to_perform_dynamic_mapping() { - var databaseGateway = GetDatabaseGateway(); var queryMapper = GetQueryMapper(); var queryRow = RawQuery.Create(@" @@ -140,10 +139,10 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) + using (var databaseContext = await GetDatabaseContext()) { - var item = queryMapper.MapItem(queryRow).Result; - var items = queryMapper.MapItems(queryRows).Result; + var item = queryMapper.MapItem(databaseContext, queryRow).Result; + var items = queryMapper.MapItems(databaseContext, queryRows).Result; Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); diff --git a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs index e5c389e..47bef3f 100644 --- a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs +++ b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs @@ -8,55 +8,38 @@ namespace Shuttle.Core.Data.Tests { public class ScriptProviderFixture : Fixture { - [SetUp] - public void SetupContext() - { - var cache = new Mock(); - - cache.Setup(m => m.Current).Returns(new Mock().Object); - } - [Test] public void Should_fail_when_there_is_no_ambient_database_context() { Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); + () => new ScriptProvider(Options.Create(new ScriptProviderOptions())).Get("Microsoft.Data.SqlClient", "throw")); } [Test] - public void Should_eb_able_to_retrieve_script_from_file() + public void Should_be_able_to_retrieve_script_from_file() { var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { FileNameFormat = "{ScriptName}.sql", ScriptFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @".\.scripts\") - }), MockDatabaseContextService()); + })); - var script = provider.Get("file-script"); + var script = provider.Get("Microsoft.Data.SqlClient", "file-script"); Assert.IsFalse(string.IsNullOrEmpty(script)); Assert.AreEqual("select 'file-script'", script); } - private static IDatabaseContextService MockDatabaseContextService() - { - var databaseContextCache = new Mock(); - - databaseContextCache.Setup(m => m.Current).Returns(new Mock().Object); - - return databaseContextCache.Object; - } - [Test] - public void Should_eb_able_to_retrieve_script_from_resource() + public void Should_be_able_to_retrieve_script_from_resource() { var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly, ResourceNameFormat = "Shuttle.Core.Data.Tests..scripts.Microsoft.Data.SqlClient.{ScriptName}.sql" - }), MockDatabaseContextService()); + })); - var script = provider.Get("embedded-script"); + var script = provider.Get("Microsoft.Data.SqlClient", "embedded-script"); Assert.IsFalse(string.IsNullOrEmpty(script)); Assert.AreEqual("select 'embedded-script'", script); @@ -68,7 +51,7 @@ public void Should_throw_exception_when_no_resource_or_file_found() var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly - }), MockDatabaseContextService()); + })); Assert.Throws(() => provider.Get("Microsoft.Data.SqlClient", "missing-script")); } diff --git a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs deleted file mode 100644 index 0be7b18..0000000 --- a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; - -namespace Shuttle.Core.Data.Tests -{ - public class ScriptProviderNegativeFixture : Fixture - { - [Test] - public void Should_fail_when_there_is_no_ambient_database_context() - { - Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/ActiveDatabaseContext.cs b/Shuttle.Core.Data/ActiveDatabaseContext.cs deleted file mode 100644 index b80b230..0000000 --- a/Shuttle.Core.Data/ActiveDatabaseContext.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public class ActiveDatabaseContext : IDisposable - { - private readonly IDatabaseContextService _databaseContextService; - private readonly IDatabaseContext _current; - - public ActiveDatabaseContext(IDatabaseContextService databaseContextService, IDatabaseContext current) - { - _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - _current = current; - } - - public void Dispose() - { - if (_current == null) - { - return; - } - - _databaseContextService.Use(_current); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs index 7ea4f0e..3354546 100644 --- a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs @@ -25,7 +25,6 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, options.DatabaseContextFactory = dataAccessBuilder.Options.DatabaseContextFactory; }); - services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index 84d9525..ce611f3 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -17,37 +17,37 @@ public DataRepository(IDatabaseGateway databaseGateway, IDataRowMapper dataRo _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> FetchItems(IQuery query, CancellationToken cancellationToken = default) + public async Task> FetchItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - var rows = await _databaseGateway.GetRows(query, cancellationToken); + var rows = await _databaseGateway.GetRows(databaseContext, query, cancellationToken); return (IEnumerable)await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); } - public async Task FetchItem(IQuery query, CancellationToken cancellationToken = default) + public async Task FetchItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - var row = await _databaseGateway.GetRow(query, cancellationToken); + var row = await _databaseGateway.GetRow(databaseContext, query, cancellationToken); return await Task.FromResult(row == null ? default : _dataRowMapper.Map(row).Result); } - public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default) + public async Task> FetchMappedRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - var row = await _databaseGateway.GetRow(query, cancellationToken); + var row = await _databaseGateway.GetRow(databaseContext, query, cancellationToken); return row == null ? null : _dataRowMapper.Map(row); } - public async Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default) + public async Task>> FetchMappedRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - var rows = await _databaseGateway.GetRows(query, cancellationToken); + var rows = await _databaseGateway.GetRows(databaseContext, query, cancellationToken); return rows.MappedRowsUsing(_dataRowMapper); } - public async Task Contains(IQuery query, CancellationToken cancellationToken = default) + public async Task Contains(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - return await _databaseGateway.GetScalar(query, cancellationToken) == 1; + return await _databaseGateway.GetScalar(databaseContext, query, cancellationToken) == 1; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 8d2b350..68b558f 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -1,23 +1,18 @@ using System; using System.Data; -using System.Data.Common; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public class DatabaseContext : IDatabaseContext { - private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; private bool _dispose; private bool _disposed; - private readonly IDatabaseContext _activeContext; - public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, - IDatabaseContextService databaseContextService) + public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory) { _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); - _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); _dispose = true; Key = Guid.NewGuid(); @@ -29,10 +24,6 @@ public DatabaseContext(string providerName, IDbConnection dbConnection, IDbComma { Connection.Open(); } - - _activeContext = databaseContextService.HasCurrent ? databaseContextService.Current : null; - - _databaseContextService.Add(this); } public Guid Key { get; } @@ -50,7 +41,7 @@ public IDatabaseContext WithName(string name) public IDatabaseContext Suppressed() { - return new DatabaseContext(ProviderName, Connection, _dbCommandFactory, _databaseContextService) + return new DatabaseContext(ProviderName, Connection, _dbCommandFactory) { Transaction = Transaction, _dispose = false @@ -101,13 +92,6 @@ public void CommitTransaction() public void Dispose() { - _databaseContextService.Remove(this); - - if (_activeContext != null && _databaseContextService.Contains(_activeContext)) - { - _databaseContextService.Use(_activeContext); - } - Dispose(true); GC.SuppressFinalize(this); diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 72331ca..bedbdd7 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -12,8 +12,7 @@ public class DatabaseContextFactory : IDatabaseContextFactory private readonly DataAccessOptions _dataAccessOptions; public DatabaseContextFactory(IOptionsMonitor connectionStringOptions, IOptions dataAccessOptions, - IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory, - IDatabaseContextService databaseContextService) + IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory) { Guard.AgainstNull(dataAccessOptions, nameof(dataAccessOptions)); @@ -22,7 +21,6 @@ public DatabaseContextFactory(IOptionsMonitor connectio DbConnectionFactory = Guard.AgainstNull(dbConnectionFactory, nameof(dbConnectionFactory)); DbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); - DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } public async Task Create(string name) @@ -41,24 +39,19 @@ public async Task Create(string name) public async Task Create(string providerName, string connectionString) { - return await Task.FromResult(DatabaseContextService.ContainsConnectionString(connectionString) - ? DatabaseContextService.GetConnectionString(connectionString).Suppressed() - : new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), - DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); + return await Task.FromResult(new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), + DbCommandFactory)).ConfigureAwait(false); } public async Task Create(string providerName, DbConnection dbConnection) { Guard.AgainstNull(dbConnection, nameof(dbConnection)); - return await Task.FromResult(DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) - ? DatabaseContextService.GetConnectionString(dbConnection.ConnectionString).Suppressed() - : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); + return await Task.FromResult(new DatabaseContext(providerName, dbConnection, DbCommandFactory)).ConfigureAwait(false); } public IDbConnectionFactory DbConnectionFactory { get; } public IDbCommandFactory DbCommandFactory { get; } - public IDatabaseContextService DatabaseContextService { get; } public async Task Create() { diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs deleted file mode 100644 index 7be295c..0000000 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public class DatabaseContextService : IDatabaseContextService - { - private readonly List _databaseContexts = new List(); - private IDatabaseContext _current = null; - - public bool HasCurrent => _current != null; - - public IDatabaseContext Current - { - get - { - if (_current == null) - { - throw new InvalidOperationException(Resources.DatabaseContextMissing); - } - - return _current; - } - } - - public ActiveDatabaseContext Use(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - var current = _current; - - _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); - - if (_current == null) - { - throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); - } - - return new ActiveDatabaseContext(this, current); - } - - public IDatabaseContext Find(Predicate match) - { - Guard.AgainstNull(match, nameof(match)); - - return _databaseContexts.Find(match); - } - - public void Add(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - if (Find(context) != null) - { - throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); - } - - _databaseContexts.Add(context); - - Use(context); - } - - public void Remove(IDatabaseContext context) - { - Guard.AgainstNull(context, nameof(context)); - - var candidate = Find(context); - - if (candidate == null) - { - return; - } - - if (_current != null && candidate.Key.Equals(_current.Key)) - { - _current = null; - } - - _databaseContexts.Remove(candidate); - } - - private IDatabaseContext Find(IDatabaseContext context) - { - return Find(candidate => candidate.Key.Equals(context.Key)); - } - - public ActiveDatabaseContext Use(string name) - { - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - var current = _current; - - _current = _databaseContexts.Find(candidate => - candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - if (_current == null) - { - throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); - } - - return new ActiveDatabaseContext(this, current); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 30bb590..a50c6e6 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -11,20 +11,11 @@ namespace Shuttle.Core.Data { public class DatabaseGateway : IDatabaseGateway { - private readonly IDatabaseContextService _databaseContextService; - - public DatabaseGateway(IDatabaseContextService databaseContextService) + public async Task GetDataTable(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - } - - public async Task GetDataTable(IQuery query, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(query, nameof(query)); - var results = new DataTable(); - using (var reader = await GetReader(query, cancellationToken).ConfigureAwait(false)) + using (var reader = await GetReader(Guard.AgainstNull(databaseContext, nameof(databaseContext)), Guard.AgainstNull(query, nameof(query)), cancellationToken).ConfigureAwait(false)) { results.Load(reader); } @@ -32,16 +23,16 @@ public async Task GetDataTable(IQuery query, CancellationToken cancel return results; } - public async Task> GetRows(IQuery query, CancellationToken cancellationToken = default) + public async Task> GetRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); + var table = await GetDataTable(Guard.AgainstNull(databaseContext, nameof(databaseContext)), Guard.AgainstNull(query, nameof(query)), cancellationToken).ConfigureAwait(false); return table.Rows.Cast(); } - public async Task GetRow(IQuery query, CancellationToken cancellationToken = default) + public async Task GetRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); + var table = await GetDataTable(Guard.AgainstNull(databaseContext, nameof(databaseContext)), Guard.AgainstNull(query, nameof(query)), cancellationToken).ConfigureAwait(false); if (table == null || table.Rows.Count == 0) { @@ -55,13 +46,9 @@ public async Task GetRow(IQuery query, CancellationToken cancellationTo { }; - public async Task GetReader(IQuery query, CancellationToken cancellationToken = default) + public async Task GetReader(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - - await using var _ = command.ConfigureAwait(false); + await using (var command = (DbCommand)Guard.AgainstNull(databaseContext, nameof(databaseContext)).CreateCommand(Guard.AgainstNull(query, nameof(query)))) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); @@ -69,11 +56,9 @@ public async Task GetReader(IQuery query, CancellationToken cancell } } - public async Task Execute(IQuery query, CancellationToken cancellationToken = default) + public async Task Execute(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - await using (var command = (DbCommand)_databaseContextService.Current.CreateCommand(query)) + await using (var command = (DbCommand)Guard.AgainstNull(databaseContext, nameof(databaseContext)).CreateCommand(Guard.AgainstNull(query, nameof(query)))) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); @@ -81,13 +66,9 @@ public async Task Execute(IQuery query, CancellationToken cancellationToken } } - public async Task GetScalar(IQuery query, CancellationToken cancellationToken = default) + public async Task GetScalar(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); - - await using (command.ConfigureAwait(false)) + await using (var command = (DbCommand)Guard.AgainstNull(databaseContext, nameof(databaseContext)).CreateCommand(Guard.AgainstNull(query, nameof(query)))) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index 1faef8a..af3efe8 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -7,144 +7,144 @@ namespace Shuttle.Core.Data { public static class DataRepositoryExtensions { - public static async Task Contains(this IDataRepository dataRepository, string sql) where T : class + public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.Contains(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.Contains(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql), cancellationToken); + return await dataRepository.Contains(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.Contains(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs deleted file mode 100644 index b4d7185..0000000 --- a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Data.Common; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public static class DatabaseContextServiceExtensions - { - public static bool Contains(this IDatabaseContextService databaseContextService, IDatabaseContext context) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNull(context, nameof(context)); - - return databaseContextService.Find(databaseContext => databaseContext.Key.Equals(context.Key)) != null; - } - - public static bool Contains(this IDatabaseContextService databaseContextService, string name) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) != null; - } - - public static bool ContainsConnectionString(this IDatabaseContextService databaseContextService, string connectionString) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); - - return databaseContextService.FindConnectionString(connectionString) != null; - } - - public static IDatabaseContext Get(this IDatabaseContextService databaseContextService, string name) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); - } - - public static IDatabaseContext GetConnectionString(this IDatabaseContextService databaseContextService, string connectionString) - { - return databaseContextService.FindConnectionString(connectionString) ?? throw new Exception(Resources.DatabaseContextNotFoundException); - } - - public static ActiveDatabaseContext Use(this IDatabaseContextService databaseContextService, string name) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - return databaseContextService.Use(databaseContextService.Get(name)); - } - - public static IDatabaseContext FindConnectionString(this IDatabaseContextService databaseContextService, string connectionString) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); - - var result = databaseContextService.Find(candidate => - candidate.Connection.ConnectionString.Equals(connectionString, - StringComparison.OrdinalIgnoreCase)); - - if (result == null) - { - var matchDbConnectionStringBuilder = new DbConnectionStringBuilder - { - ConnectionString = connectionString.ToLowerInvariant() - }; - - matchDbConnectionStringBuilder.Remove("password"); - matchDbConnectionStringBuilder.Remove("pwd"); - - result = databaseContextService.Find(candidate => - { - var candidateDbConnectionStringBuilder = new DbConnectionStringBuilder - { - ConnectionString = candidate.Connection.ConnectionString - }; - - candidateDbConnectionStringBuilder.Remove("password"); - candidateDbConnectionStringBuilder.Remove("pwd"); - - return candidateDbConnectionStringBuilder.ConnectionString.Equals(matchDbConnectionStringBuilder.ConnectionString, - StringComparison.OrdinalIgnoreCase); - }); - } - - return result; - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index a2ec2f2..eacc5ef 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -9,172 +9,172 @@ namespace Shuttle.Core.Data { public static class DatabaseGatewayExtensions { - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql) + public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) + public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) + public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 68f7791..101d786 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -7,228 +7,228 @@ namespace Shuttle.Core.Data { public static class QueryMapperExtensions { - public static async Task MapItem(this IQueryMapper queryMapper, string sql) + public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql) + public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql) + public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql) + public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql), cancellationToken); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDataRepository.cs b/Shuttle.Core.Data/IDataRepository.cs index 39da152..395055c 100644 --- a/Shuttle.Core.Data/IDataRepository.cs +++ b/Shuttle.Core.Data/IDataRepository.cs @@ -6,10 +6,10 @@ namespace Shuttle.Core.Data { public interface IDataRepository where T : class { - Task> FetchItems(IQuery query, CancellationToken cancellationToken = default); - Task FetchItem(IQuery query, CancellationToken cancellationToken = default); - Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default); - Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default); - Task Contains(IQuery query, CancellationToken cancellationToken = default); + Task> FetchItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task FetchItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task> FetchMappedRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task>> FetchMappedRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task Contains(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index d698b69..a5f93b8 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -12,6 +12,5 @@ public interface IDatabaseContextFactory IDbConnectionFactory DbConnectionFactory { get; } IDbCommandFactory DbCommandFactory { get; } - IDatabaseContextService DatabaseContextService { get; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs deleted file mode 100644 index 792df3f..0000000 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Shuttle.Core.Data -{ - public interface IDatabaseContextService - { - bool HasCurrent { get; } - IDatabaseContext Current { get; } - ActiveDatabaseContext Use(IDatabaseContext context); - IDatabaseContext Find(Predicate match); - void Add(IDatabaseContext context); - void Remove(IDatabaseContext context); - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index dbbe448..16ba213 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Threading; using System.Threading.Tasks; @@ -11,11 +10,11 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - Task GetReader(IQuery query, CancellationToken cancellationToken = default); - Task Execute(IQuery query, CancellationToken cancellationToken = default); - Task GetScalar(IQuery query, CancellationToken cancellationToken = default); - Task GetDataTable(IQuery query, CancellationToken cancellationToken = default); - Task> GetRows(IQuery query, CancellationToken cancellationToken = default); - Task GetRow(IQuery query, CancellationToken cancellationToken = default); + Task GetReader(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task Execute(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task GetScalar(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task GetDataTable(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task> GetRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task GetRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryMapper.cs b/Shuttle.Core.Data/IQueryMapper.cs index 6c3bdc9..57feb99 100644 --- a/Shuttle.Core.Data/IQueryMapper.cs +++ b/Shuttle.Core.Data/IQueryMapper.cs @@ -6,13 +6,13 @@ namespace Shuttle.Core.Data { public interface IQueryMapper { - Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task MapObject(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task MapValue(IQuery query, CancellationToken cancellationToken = default); - Task> MapValues(IQuery query, CancellationToken cancellationToken = default); - Task MapItem(IQuery query, CancellationToken cancellationToken = default); - Task> MapItems(IQuery query, CancellationToken cancellationToken = default); + Task> MapRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task>> MapRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapObject(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task> MapObjects(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapValue(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task> MapValues(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task MapItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task> MapItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IScriptProvider.cs b/Shuttle.Core.Data/IScriptProvider.cs index 0ab13bd..8d37afb 100644 --- a/Shuttle.Core.Data/IScriptProvider.cs +++ b/Shuttle.Core.Data/IScriptProvider.cs @@ -2,7 +2,7 @@ { public interface IScriptProvider { - string Get(string scriptName); - string Get(string scriptName, params object[] parameters); + string Get(string providerName, string scriptName); + string Get(string providerName, string scriptName, params object[] parameters); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index f938af3..a2af89a 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -25,25 +25,25 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task> MapRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRow(await _databaseGateway.GetRow(query, cancellationToken)); + return _dataRowMapper.MapRow(await _databaseGateway.GetRow(databaseContext, query, cancellationToken)); } - public async Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task>> MapRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRows(await _databaseGateway.GetRows(query, cancellationToken)); + return _dataRowMapper.MapRows(await _databaseGateway.GetRows(databaseContext, query, cancellationToken)); } - public async Task MapObject(IQuery query, CancellationToken cancellationToken= default) where T : new() + public async Task MapObject(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); var columns = GetColumns(reader); @@ -55,13 +55,13 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return default; } - public async Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task> MapObjects(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); var columns = GetColumns(reader); @@ -73,11 +73,11 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return result; } - public async Task MapValue(IQuery query, CancellationToken cancellationToken = default) + public async Task MapValue(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -87,13 +87,13 @@ public async Task MapValue(IQuery query, CancellationToken cancellationTok return default; } - public async Task> MapValues(IQuery query, CancellationToken cancellationToken = default) + public async Task> MapValues(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -120,11 +120,11 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return result; } - public async Task MapItem(IQuery query, CancellationToken cancellationToken = default) + public async Task MapItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); var columns = GetColumns(reader); if (await reader.ReadAsync(cancellationToken)) @@ -135,13 +135,13 @@ public async Task MapItem(IQuery query, CancellationToken cancellationT return default; } - public async Task> MapItems(IQuery query, CancellationToken cancellationToken = default) + public async Task> MapItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); var columns = GetColumns(reader); diff --git a/Shuttle.Core.Data/ScriptProvider.cs b/Shuttle.Core.Data/ScriptProvider.cs index 0e2f87b..870547a 100644 --- a/Shuttle.Core.Data/ScriptProvider.cs +++ b/Shuttle.Core.Data/ScriptProvider.cs @@ -10,35 +10,34 @@ public class ScriptProvider : IScriptProvider { private static readonly object Lock = new object(); private readonly ScriptProviderOptions _options; - private readonly IDatabaseContextService _databaseContextService; private readonly string[] _emptyFiles = Array.Empty(); private readonly Dictionary _scripts = new Dictionary(); - public ScriptProvider(IOptions options, IDatabaseContextService databaseContextService) + public ScriptProvider(IOptions options) { Guard.AgainstNull(options, nameof(options)); _options = Guard.AgainstNull(options.Value, nameof(options.Value)); - _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public string Get(string scriptName) + public string Get(string providerName, string scriptName) { - return Get(scriptName, null); + return Get(providerName, scriptName, null); } - public string Get(string scriptName, params object[] parameters) + public string Get(string providerName, string scriptName, params object[] parameters) { + Guard.AgainstNullOrEmptyString(providerName, nameof(providerName)); Guard.AgainstNullOrEmptyString(scriptName, nameof(scriptName)); - var key = Key(scriptName); + var key = Key(providerName, scriptName); lock (Lock) { if (!_scripts.ContainsKey(key)) { - AddScript(scriptName); + AddScript(providerName, scriptName); } return parameters != null @@ -47,17 +46,17 @@ public string Get(string scriptName, params object[] parameters) } } - private string Key(string scriptName) + private string Key(string providerName, string scriptName) { lock (Lock) { - return $"[{_databaseContextService.Current.ProviderName}]-{scriptName}"; + return $"[{providerName}]-{scriptName}"; } } - private void AddScript(string scriptName) + private void AddScript(string providerName, string scriptName) { - var key = Key(scriptName); + var key = Key(providerName, scriptName); lock (Lock) { @@ -70,12 +69,12 @@ private void AddScript(string scriptName) if (!string.IsNullOrEmpty(_options.ScriptFolder) && Directory.Exists(_options.ScriptFolder)) { - files = Directory.GetFiles(_options.ScriptFolder, FormattedFileName(scriptName), SearchOption.AllDirectories); + files = Directory.GetFiles(_options.ScriptFolder, FormattedFileName(providerName, scriptName), SearchOption.AllDirectories); } if (files.Length == 0) { - AddEmbeddedScript(scriptName); + AddEmbeddedScript(providerName, scriptName); return; } @@ -90,29 +89,29 @@ private void AddScript(string scriptName) } } - private string FormattedFileName(string scriptName) + private string FormattedFileName(string providerName, string scriptName) { - return FormattedScriptPath(_options.FileNameFormat, scriptName); + return FormattedScriptPath(_options.FileNameFormat, providerName, scriptName); } - private string FormattedResourceName(string scriptName) + private string FormattedResourceName(string providerName, string scriptName) { - return FormattedScriptPath(_options.ResourceNameFormat, scriptName); + return FormattedScriptPath(_options.ResourceNameFormat, providerName, scriptName); } - private string FormattedScriptPath(string format, string scriptName) + private string FormattedScriptPath(string format, string providerName, string scriptName) { - return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", _databaseContextService.Current.ProviderName); + return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", providerName); } - private void AddEmbeddedScript(string scriptName) + private void AddEmbeddedScript(string providerName, string scriptName) { if (_options.ResourceAssembly == null) { throw new InvalidOperationException(Resources.ResourceAssemblyMissingException); } - var path = _options.ResourceNameFormat != null ? FormattedResourceName(scriptName) : scriptName; + var path = _options.ResourceNameFormat != null ? FormattedResourceName(providerName, scriptName) : scriptName; using (var stream = _options.ResourceAssembly.GetManifestResourceStream(path)) { @@ -123,7 +122,7 @@ private void AddEmbeddedScript(string scriptName) using (var reader = new StreamReader(stream)) { - _scripts.Add(Key(scriptName), reader.ReadToEnd()); + _scripts.Add(Key(providerName, scriptName), reader.ReadToEnd()); } } } From 115bf8ff01453abd660aa731b0bf57eb942c2c41 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 12 Feb 2023 17:47:12 +0200 Subject: [PATCH 07/37] Update README.md --- README.md | 139 ++++++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index c348b10..0ea1bbb 100644 --- a/README.md +++ b/README.md @@ -72,24 +72,24 @@ The default JSON settings structure is as follows: In order to access a database we need a database connection. A database connection is represented by an `IDatabaseContext` instance that may be obtained by using an instance of an `IDatabaseContextFactory` implementation. -The `DatabaseContextFactory` implementation makes use of an `IDbConnectionFactory` implementation which creates a `System.Data.IDbConnection` by using the provider name and connection string. An `IDbCommandFactory` creates a `System.Data.IDbCommand` by using an `IDbConnection` instance. The `DatabaseContextFactory` also requires an instance of an `IDatabaseContextService` that stores connections and is assigned to the `DatabaseContext` in order to obtain the active connection. +The `DatabaseContextFactory` implementation makes use of an `IDbConnectionFactory` implementation which creates a `System.Data.IDbConnection` by using the provider name and connection string. An `IDbCommandFactory` creates a `System.Data.IDbCommand` by using an `IDbConnection` instance. ``` c# var databaseContextFactory = provider.GetRequiredService(); -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { // database interaction } -using (var context = databaseContextFactory +using (var databaseContext = databaseContextFactory .Create("Microsoft.Data.SqlClient", "Data Source=.\sqlexpress;Initial Catalog=Shuttle;Integrated Security=SSPI;TrustServerCertificate=true")) { // database interaction } -using (var context = databaseContextFactory.Create(existingIDbConnection)) +using (var databaseContext = databaseContextFactory.Create(existingIDbConnection)) { // database interaction } @@ -115,7 +115,7 @@ public static IQueryParameter Create(string sql, params object[] args); public static IQueryParameter Create(string sql, dynamic parameters); ``` -You can either use the constructor or one of the static methods to specify the `sql` to use. Parameters may either be added using the `AddParameterValue` of the returned `IQueryParameter` or they may be added as `params object[] args` in order to insert them at the required index. When using `dynamic` parameters the object values are converted to `MappedColumn` instances and added as parameters using `AddParameterValue`. +You can either use the constructor or one of the static methods to specify the `sql` to use. Parameters may either be added using the `AddParameterValue` of the returned `IQueryParameter` or they may be added as `params object[] args` in order to insert them at the required index. When using `dynamic` parameters the object values are converted to `Column` instances and added as parameters using `AddParameterValue`. ## ProcedureQuery @@ -131,9 +131,9 @@ public static int DataAccessMethod(this IDataAccessInterface dataAccessInstance, Where `DataAccessMethod` is the relevant method on the `IDataAccessInterface` that you would like to call. -# MappedColumn +# Column -Typically you would not want to create a `MappedColumn` each time you need it and these are also quite fixed. A column mapping can, therefore, by defined statically: +Typically you would not want to create a `Column` each time you need it and these are also quite fixed. A column mapping can, therefore, by defined statically: ``` c# using System; @@ -144,37 +144,37 @@ namespace Shuttle.Ordering.DataAccess { public class OrderColumns { - public static readonly MappedColumn Id = - new MappedColumn("Id", DbType.Guid); + public static readonly Column Id = + new Column("Id", DbType.Guid); - public static readonly MappedColumn OrderNumber = - new MappedColumn("OrderNumber", DbType.String, 20); + public static readonly Column OrderNumber = + new Column("OrderNumber", DbType.String, 20); - public static readonly MappedColumn OrderDate = - new MappedColumn("OrderDate", DbType.DateTime); + public static readonly Column OrderDate = + new Column("OrderDate", DbType.DateTime); - public static readonly MappedColumn CustomerName = - new MappedColumn("CustomerName", DbType.String, 65); + public static readonly Column CustomerName = + new Column("CustomerName", DbType.String, 65); - public static readonly MappedColumn CustomerEMail = - new MappedColumn("CustomerEMail", DbType.String); // size omitted + public static readonly Column CustomerEMail = + new Column("CustomerEMail", DbType.String); // size omitted } } ``` -There are quite a few options that you can set on the `MappedColumn` in order to represent your column properly. +There are quite a few options that you can set on the `Column` in order to represent your column properly. -## MapFrom +## Value ``` c# -public T MapFrom(DataRow row) +public T Value(DataRow row) ``` This will return the typed value of the specified column as contained in the passed-in `DataRow`. # IQueryParameter: IQuery -An `IQueryParameter` inherits the `IQuery` interface and extends it by allowing you to add parameters to a query by specifying an `IMappedColumn` (see below) instance along with the value for the parameter. +An `IQueryParameter` inherits the `IQuery` interface and extends it by allowing you to add parameters to a query by specifying an `IColumn` (see below) instance along with the value for the parameter. There are two implementations of this interface. @@ -187,58 +187,51 @@ The following section each describe the methods available in the `IDatabaseGatew ## GetReader ``` c# -IDataReader GetReader(IQuery query); +Task GetReader(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` -Returns an `IDataReader` instance for the given `select` statement: +Returns an `IDataReader` instance for the given `query` statement: ``` c# -var databaseContextFactory = DatabaseContextFactory.Default(); -var gateway = new DatabaseGateway(); - -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { - var reader = gateway.GetReader(RawQuery.Create("select Id, Username from dbo.Member")); + var reader = await gateway.GetReader(databaseContext, RawQuery.Create("select Id, Username from dbo.Member")); } ``` ## Execute ``` c# -int Execute(IQuery query); +Task Execute(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Executes the given query and returns the number of rows affected: ``` c# -var databaseContextFactory = DatabaseContextFactory.Default(); -var gateway = new DatabaseGateway(); - -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { - gateway.Execute(RawQuery.Create("delete from dbo.Member where Username = 'mr.resistor'")); + gateway.Execute(databaseContext, RawQuery.Create("delete from dbo.Member where Username = 'mr.resistor'")); } ``` ## GetScalar ```c# -T GetScalar(IQuery query); +Task GetScalar(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Get the scalar value returned by the `select` query. The query shoud return only one value (scalar): ```c# -var databaseContextFactory = DatabaseContextFactory.Default(); -var gateway = new DatabaseGateway(); - -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { - var username = gateway.GetScalar( + var username = await gateway.GetScalar( + databaseContext, RawQuery.Create("select Username from dbo.Member where Id = 10") ); - var id = gateway.GetScalar( + var id = await gateway.GetScalar( + databaseContext, RawQuery.Create("select Id from dbo.Member where Username = 'mr.resistor'") ); } @@ -247,54 +240,46 @@ using (var context = databaseContextFactory.Create("connection-name")) ## GetDataTable ``` c# -DataTable GetDataTable(IQuery query); +Task GetDataTable(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` -Returns a `DataTable` containing the rows returned for the given `select` statement. +Returns a `DataTable` containing the rows returned for the given `select` query. ``` c# -var databaseContextFactory = DatabaseContextFactory.Default(); -var gateway = new DatabaseGateway(); - -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { - var table = gateway.GetDataTable(RawQuery.Create("select Id, Username from dbo.Member")); + var table = await gateway.GetDataTable(databaseContext, RawQuery.Create("select Id, Username from dbo.Member")); } ``` ## GetRows ``` c# -IEnumerable GetRows(IQuery query); +Task> GetRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Returns an enumerable containing the `DataRow` instances returned for a `select` query: ``` c# -var databaseContextFactory = DatabaseContextFactory.Default(); -var gateway = new DatabaseGateway(); - -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { - var rows = gateway.GetRows(RawQuery.Create("select Id, Username from dbo.Member")); + var rows = gateway.GetRows(databaseContext, RawQuery.Create("select Id, Username from dbo.Member")); } ``` ## GetRow ``` c# -DataRow GetRow(IQuery query); +Task GetRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Returns a single `DataRow` containing the values returned for a `select` statement that returns exactly one row: ``` c# -var databaseContextFactory = DatabaseContextFactory.Default(); -var gateway = new DatabaseGateway(); - -using (var context = databaseContextFactory.Create("connection-name")) +using (var databaseContext = databaseContextFactory.Create("connection-name")) { - var row = gateway.GetRow( + var row = await gateway.GetRow( + databaseContext, RawQuery.Create("select Id, Username, EMail, DateActivated from dbo.Member where Id = 10") ); } @@ -309,7 +294,7 @@ The following methods can be used to interact with your object type. ## FetchItems ``` c# -IEnumerable FetchItems(IQuery query); +Task> FetchItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Uses the `select` clause represented by the `IQuery` instance to create a list of objects of type `T`. The `select` clause will need to select all the required columns and will, typically, return more than one instance. @@ -317,7 +302,7 @@ Uses the `select` clause represented by the `IQuery` instance to create a list o ## FetchItem ``` c# -T FetchItem(IQuery query); +Task FetchItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Returns a single object instance of type `T` that is hydrated using the data returned from the `select` clause represented by the `IQuery` instance. @@ -333,15 +318,23 @@ This is similar to the `FetchItems` method but instead returns a list of `Mapped ## FetchMappedRow ``` c# -MappedRow FetchMappedRow(IQuery query); +Task> FetchMappedRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Similar to the `FetchItem` method but instead return a `MappedRow` instance that is hydrated using the data returned from the `select` clause represented by the `IQuery` instance. +## FetchMappedRows + +``` c# +Task>> FetchMappedRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +``` + +Similar to the `FetchMappedRow` method but returns an enumerable containing all the `MappedRow` instances. + ## Contains ``` c# -bool Contains(IQuery query); +Task Contains(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); ``` Returns `true` is the `IQuery` instance `select` clause returns an `int` scalar that equals `1`; else returns `false`. @@ -352,7 +345,7 @@ The `RawQuery` enables you to create any query using the native language structu ``` c# var query = RawQuery.Create("select UserName from dbo.Member where Id = @Id") - .AddParameterValue(new MappedColumn("Id", DbType.Guid), + .AddParameterValue(new Column("Id", DbType.Guid), new Guid('{75208260-CF93-454E-95EC-FE1903F3664E}')); ``` @@ -362,7 +355,7 @@ The `ProcedureQuery` is used to execute a stored procedure: ``` c# var query = ProcedureQuery.Create("uspMemberById") - .AddParameterValue(new MappedColumn("Id", DbType.Guid), + .AddParameterValue(new Column("Id", DbType.Guid), new Guid('{75208260-CF93-454E-95EC-FE1903F3664E}')); ``` @@ -381,14 +374,14 @@ namespace Shuttle.ProcessManagement { public MappedRow Map(DataRow row) { - var result = new OrderProcess(OrderProcessColumns.Id.MapFrom(row)) + var result = new OrderProcess(OrderProcessColumns.Id.Value(row)) { - CustomerName = OrderProcessColumns.CustomerName.MapFrom(row), - CustomerEMail = OrderProcessColumns.CustomerEMail.MapFrom(row), - OrderId = OrderProcessColumns.OrderId.MapFrom(row), - InvoiceId = OrderProcessColumns.InvoiceId.MapFrom(row), - DateRegistered = OrderProcessColumns.DateRegistered.MapFrom(row), - OrderNumber = OrderProcessColumns.OrderNumber.MapFrom(row) + CustomerName = OrderProcessColumns.CustomerName.Value(row), + CustomerEMail = OrderProcessColumns.CustomerEMail.Value(row), + OrderId = OrderProcessColumns.OrderId.Value(row), + InvoiceId = OrderProcessColumns.InvoiceId.Value(row), + DateRegistered = OrderProcessColumns.DateRegistered.Value(row), + OrderNumber = OrderProcessColumns.OrderNumber.Value(row) }; return new MappedRow(row, result); From 2a2b072701a1f2b07286d118c73a288618b65b4b Mon Sep 17 00:00:00 2001 From: Eben Date: Tue, 14 Feb 2023 20:45:09 +0200 Subject: [PATCH 08/37] - package upgrade --- Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj | 4 ++-- Shuttle.Core.Data/.package/package.nuspec | 5 ++--- Shuttle.Core.Data/Shuttle.Core.Data.csproj | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj index fe04e5b..b5f184c 100644 --- a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj +++ b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index 50167e2..e078479 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -18,11 +18,10 @@ - + - + - diff --git a/Shuttle.Core.Data/Shuttle.Core.Data.csproj b/Shuttle.Core.Data/Shuttle.Core.Data.csproj index f47446f..8c634c3 100644 --- a/Shuttle.Core.Data/Shuttle.Core.Data.csproj +++ b/Shuttle.Core.Data/Shuttle.Core.Data.csproj @@ -16,11 +16,10 @@ - + - + - From 6b3a02de0d75352bb6a1ee85c1c757ae9ae40835 Mon Sep 17 00:00:00 2001 From: Eben Date: Wed, 15 Feb 2023 19:45:43 +0200 Subject: [PATCH 09/37] Revert "- removed ambient connections" This reverts commit e7407a8b053b2e7c76c54a79238df54e8d3d3c0c. --- .../DataRepositoryFixture.cs | 40 +++--- .../DatabaseContextFactoryFixture.cs | 4 +- .../DatabaseContextFixture.cs | 16 +-- .../DatabaseContextServiceFixture.cs | 53 ++++++++ .../Mapping/DataRowMapperFixture.cs | 34 ++--- .../Mapping/MappingFixture.cs | 5 +- .../Mapping/QueryMapperFixture.cs | 37 ++--- .../ScriptProviderFixture.cs | 33 +++-- .../ScriptProviderNegativeFixture.cs | 17 +++ Shuttle.Core.Data/ActiveDatabaseContext.cs | 27 ++++ .../ServiceCollectionExtensions.cs | 1 + Shuttle.Core.Data/DataRepository.cs | 20 +-- Shuttle.Core.Data/DatabaseContext.cs | 20 ++- Shuttle.Core.Data/DatabaseContextFactory.cs | 15 +- Shuttle.Core.Data/DatabaseContextService.cs | 105 ++++++++++++++ Shuttle.Core.Data/DatabaseGateway.cs | 43 ++++-- .../Extensions/DataRepositoryExtensions.cs | 80 +++++------ .../DatabaseContextServiceExtensions.cs | 91 +++++++++++++ .../Extensions/DatabaseGatewayExtensions.cs | 96 ++++++------- .../Extensions/QueryMapperExtensions.cs | 128 +++++++++--------- Shuttle.Core.Data/IDataRepository.cs | 10 +- Shuttle.Core.Data/IDatabaseContextFactory.cs | 1 + Shuttle.Core.Data/IDatabaseContextService.cs | 14 ++ Shuttle.Core.Data/IDatabaseGateway.cs | 13 +- Shuttle.Core.Data/IQueryMapper.cs | 16 +-- Shuttle.Core.Data/IScriptProvider.cs | 4 +- Shuttle.Core.Data/QueryMapper.cs | 32 ++--- Shuttle.Core.Data/ScriptProvider.cs | 45 +++--- 28 files changed, 682 insertions(+), 318 deletions(-) create mode 100644 Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs create mode 100644 Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs create mode 100644 Shuttle.Core.Data/ActiveDatabaseContext.cs create mode 100644 Shuttle.Core.Data/DatabaseContextService.cs create mode 100644 Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs create mode 100644 Shuttle.Core.Data/IDatabaseContextService.cs diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index e821db7..68acb50 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -14,19 +14,18 @@ public class DataRepositoryFixture : Fixture [Test] public async Task Should_be_able_to_fetch_all_items() { - var context = new Mock(); var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRows(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); + gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = (await repository.FetchItems(context.Object, query.Object)).ToList(); + var result = (await repository.FetchItems(query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -36,19 +35,18 @@ public async Task Should_be_able_to_fetch_all_items() [Test] public async Task Should_be_able_to_fetch_a_single_item() { - var context = new Mock(); - var gateway = new Mock(); + var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRow(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = await repository.FetchItem(context.Object, query.Object); + var result = await repository.FetchItem(query.Object); Assert.IsNotNull(result); Assert.AreSame(anObject, result); @@ -57,15 +55,14 @@ public async Task Should_be_able_to_fetch_a_single_item() [Test] public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() { - var context = new Mock(); - var gateway = new Mock(); + var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetRow(context.Object, query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); var repository = new DataRepository(gateway.Object, new Mock>().Object); - var result = await repository.FetchItem(context.Object, query.Object); + var result = await repository.FetchItem(query.Object); Assert.IsNull(result); } @@ -73,34 +70,32 @@ public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that [Test] public async Task Should_be_able_to_call_contains() { - var context = new Mock(); - var gateway = new Mock(); + var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetScalar(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(1); + gateway.Setup(m => m.GetScalar(query.Object, CancellationToken.None)).ReturnsAsync(1); var repository = new DataRepository(gateway.Object, new Mock>().Object); - Assert.That(await repository.Contains(context.Object, query.Object), Is.True); + Assert.That(await repository.Contains(query.Object), Is.True); } [Test] public async Task Should_be_able_to_fetch_mapped_rows() { - var context = new Mock(); - var gateway = new Mock(); + var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRows(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); + gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = (await repository.FetchMappedRows(context.Object, query.Object)).ToList(); + var result = (await repository.FetchMappedRows(query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -111,20 +106,19 @@ public async Task Should_be_able_to_fetch_mapped_rows() [Test] public async Task Should_be_able_to_fetch_a_single_row() { - var context = new Mock(); - var gateway = new Mock(); + var gateway = new Mock(); var mapper = new Mock>(); var query = new Mock(); var dataRow = new DataTable().NewRow(); var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRow(context.Object, query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = await repository.FetchMappedRow(context.Object, query.Object); + var result = await repository.FetchMappedRow(query.Object); Assert.IsNotNull(result); Assert.AreSame(dataRow, result.Row); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 6aa3c2d..973ab4f 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -23,7 +23,7 @@ public void Should_be_able_to_create_a_database_context() } [Test] - public async Task Should_be_able_to_get_an_independent_database_context() + public async Task Should_be_able_to_get_an_existing_database_context() { var factory = GetDatabaseContextFactory(); @@ -33,7 +33,7 @@ public async Task Should_be_able_to_get_an_independent_database_context() Assert.IsNotNull(context); Assert.IsNotNull(existingContext); - Assert.That(existingContext.Connection, Is.Not.SameAs(context.Connection)); + Assert.AreSame(existingContext.Connection, context.Connection); } } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index 0325409..e4e8bca 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -15,7 +15,7 @@ public void Should_not_be_able_to_create_an_invalid_connection() { Assert.Throws(() => { - using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object)) + using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) { } }); @@ -28,7 +28,7 @@ public void Should_not_be_able_to_create_a_non_existent_connection() { using ( new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object)) + new Mock().Object, new DatabaseContextService())) { } }); @@ -39,7 +39,7 @@ public void Should_be_able_to_create_a_valid_connection() { using ( new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object)) + new Mock().Object, new DatabaseContextService())) { } } @@ -50,7 +50,7 @@ public void Should_be_able_to_begin_and_commit_a_transaction() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object)) + new Mock().Object, new DatabaseContextService())) { connection.BeginTransaction(); connection.CommitTransaction(); @@ -63,7 +63,7 @@ public void Should_be_able_to_begin_and_rollback_a_transaction() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object)) + new Mock().Object, new DatabaseContextService())) { connection.BeginTransaction(); } @@ -75,7 +75,7 @@ public void Should_be_able_to_call_commit_without_a_transaction() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object)) + new Mock().Object, new DatabaseContextService())) { connection.CommitTransaction(); } @@ -87,7 +87,7 @@ public void Should_be_able_to_call_dispose_more_than_once() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object)) + new Mock().Object, new DatabaseContextService())) { connection.Dispose(); connection.Dispose(); @@ -105,7 +105,7 @@ public void Should_be_able_to_create_a_command() dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); using ( - var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object)) + var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) { connection.CreateCommand(query.Object); } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs new file mode 100644 index 0000000..a11495d --- /dev/null +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -0,0 +1,53 @@ +using System.Data; +using System.Data.Common; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; + +namespace Shuttle.Core.Data.Tests +{ + public class DatabaseContextServiceFixture : Fixture + { + [Test] + public async Task Should_be_able_to_use_different_contexts() + { + var service = new DatabaseContextService(); + + var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, service).WithName("mock-1"); + + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + + var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, service).WithName("mock-2"); + + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + + using (service.Use("mock-1")) + { + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + + await AssertContextFlow(service, "mock-1"); + } + + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + + using (service.Use(context1)) + { + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + } + + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + } + + private async Task AssertContextFlow(IDatabaseContextService service, string name) + { + Assert.That(service.Current, Is.Not.Null); + Assert.That(service.Current.Name, Is.EqualTo(name)); + + await Task.Run(() => + { + Assert.That(service.Current, Is.Not.Null); + Assert.That(service.Current.Name, Is.EqualTo(name)); + }); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 51fba03..b48fe0a 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -32,16 +32,16 @@ select top 1 BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (await GetDatabaseContext()) { - var item = dataRowMapper.MapObject(await databaseGateway.GetRow(databaseContext, rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(databaseContext, rowsQuery)); + var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(databaseContext, rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(databaseContext, rowsQuery)); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -72,16 +72,16 @@ Age as TheAge BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (await GetDatabaseContext()) { - var item = dataRowMapper.MapObject(await databaseGateway.GetRow(databaseContext, rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(databaseContext, rowsQuery)); + var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(databaseContext, rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(databaseContext, rowsQuery)); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -108,10 +108,10 @@ select top 1 BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (GetDatabaseContext()) { - var value = dataRowMapper.MapValue(await databaseGateway.GetRow(databaseContext, rowQuery)); - var values = dataRowMapper.MapValues(await databaseGateway.GetRows(databaseContext, rowsQuery)); + var value = dataRowMapper.MapValue(await databaseGateway.GetRow(rowQuery)); + var values = dataRowMapper.MapValues(await databaseGateway.GetRows(rowsQuery)); Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -147,17 +147,17 @@ public async Task Should_be_able_to_perform_dynamic_mapping() BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (GetDatabaseContext()) { - var item = dataRowMapper.MapItem(await databaseGateway.GetRow(databaseContext, rowQuery)); + var item = dataRowMapper.MapItem(await databaseGateway.GetRow(rowQuery)); Assert.IsNotNull(item); - var items = dataRowMapper.MapItems(await databaseGateway.GetRows(databaseContext, rowsQuery)); + var items = dataRowMapper.MapItems(await databaseGateway.GetRows(rowsQuery)); Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(await databaseGateway.GetRow(databaseContext, RawQuery.Create(rowSql, new { Id = id }))); + item = dataRowMapper.MapItem(await databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id }))); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index e4c91bd..0d4b23f 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; namespace Shuttle.Core.Data.Tests; @@ -9,9 +8,9 @@ public class MappingFixture : Fixture [SetUp] public async Task SetUp() { - using (var databaseContext = await GetDatabaseContext()) + using (await GetDatabaseContext()) { - await GetDatabaseGateway().Execute(databaseContext, RawQuery.Create(@" + await GetDatabaseGateway().Execute(RawQuery.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index 191dc5d..c2ae170 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -32,16 +32,16 @@ select top 1 BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (await GetDatabaseContext()) { - var item = await mapper.MapObject(databaseContext, queryRow); - var items = await mapper.MapObjects(databaseContext, queryRows); + var item = await mapper.MapObject(queryRow); + var items = await mapper.MapObjects(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = await mapper.MapRow(databaseContext, queryRow); - var mappedRows = await mapper.MapRows(databaseContext, queryRows); + var mappedRow = await mapper.MapRow(queryRow); + var mappedRows = await mapper.MapRows(queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -71,16 +71,16 @@ Age as TheAge BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (GetDatabaseContext()) { - var item = await mapper.MapObject(databaseContext, queryRow); - var items = await mapper.MapObjects(databaseContext, queryRows); + var item = await mapper.MapObject(queryRow); + var items = await mapper.MapObjects(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRow(databaseContext, queryRow).Result; - var mappedRows = await mapper.MapRows(databaseContext, queryRows); + var mappedRow = mapper.MapRow(queryRow).Result; + var mappedRows = await mapper.MapRows(queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -88,7 +88,7 @@ Age as TheAge } [Test] - public async Task Should_be_able_to_perform_value_mapping() + public void Should_be_able_to_perform_value_mapping() { var mapper = GetQueryMapper(); @@ -106,10 +106,10 @@ select top 1 BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (GetDatabaseContext()) { - var value = mapper.MapValue(databaseContext, queryRow).Result; - var values = mapper.MapValues(databaseContext, queryRows).Result; + var value = mapper.MapValue(queryRow).Result; + var values = mapper.MapValues(queryRows).Result; Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -117,8 +117,9 @@ select top 1 } [Test] - public async Task Should_be_able_to_perform_dynamic_mapping() + public void Should_be_able_to_perform_dynamic_mapping() { + var databaseGateway = GetDatabaseGateway(); var queryMapper = GetQueryMapper(); var queryRow = RawQuery.Create(@" @@ -139,10 +140,10 @@ select top 1 BasicMapping "); - using (var databaseContext = await GetDatabaseContext()) + using (GetDatabaseContext()) { - var item = queryMapper.MapItem(databaseContext, queryRow).Result; - var items = queryMapper.MapItems(databaseContext, queryRows).Result; + var item = queryMapper.MapItem(queryRow).Result; + var items = queryMapper.MapItems(queryRows).Result; Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); diff --git a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs index 47bef3f..e5c389e 100644 --- a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs +++ b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs @@ -8,38 +8,55 @@ namespace Shuttle.Core.Data.Tests { public class ScriptProviderFixture : Fixture { + [SetUp] + public void SetupContext() + { + var cache = new Mock(); + + cache.Setup(m => m.Current).Returns(new Mock().Object); + } + [Test] public void Should_fail_when_there_is_no_ambient_database_context() { Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions())).Get("Microsoft.Data.SqlClient", "throw")); + () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); } [Test] - public void Should_be_able_to_retrieve_script_from_file() + public void Should_eb_able_to_retrieve_script_from_file() { var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { FileNameFormat = "{ScriptName}.sql", ScriptFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @".\.scripts\") - })); + }), MockDatabaseContextService()); - var script = provider.Get("Microsoft.Data.SqlClient", "file-script"); + var script = provider.Get("file-script"); Assert.IsFalse(string.IsNullOrEmpty(script)); Assert.AreEqual("select 'file-script'", script); } + private static IDatabaseContextService MockDatabaseContextService() + { + var databaseContextCache = new Mock(); + + databaseContextCache.Setup(m => m.Current).Returns(new Mock().Object); + + return databaseContextCache.Object; + } + [Test] - public void Should_be_able_to_retrieve_script_from_resource() + public void Should_eb_able_to_retrieve_script_from_resource() { var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly, ResourceNameFormat = "Shuttle.Core.Data.Tests..scripts.Microsoft.Data.SqlClient.{ScriptName}.sql" - })); + }), MockDatabaseContextService()); - var script = provider.Get("Microsoft.Data.SqlClient", "embedded-script"); + var script = provider.Get("embedded-script"); Assert.IsFalse(string.IsNullOrEmpty(script)); Assert.AreEqual("select 'embedded-script'", script); @@ -51,7 +68,7 @@ public void Should_throw_exception_when_no_resource_or_file_found() var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly - })); + }), MockDatabaseContextService()); Assert.Throws(() => provider.Get("Microsoft.Data.SqlClient", "missing-script")); } diff --git a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs new file mode 100644 index 0000000..0be7b18 --- /dev/null +++ b/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; + +namespace Shuttle.Core.Data.Tests +{ + public class ScriptProviderNegativeFixture : Fixture + { + [Test] + public void Should_fail_when_there_is_no_ambient_database_context() + { + Assert.Throws( + () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/ActiveDatabaseContext.cs b/Shuttle.Core.Data/ActiveDatabaseContext.cs new file mode 100644 index 0000000..b80b230 --- /dev/null +++ b/Shuttle.Core.Data/ActiveDatabaseContext.cs @@ -0,0 +1,27 @@ +using System; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public class ActiveDatabaseContext : IDisposable + { + private readonly IDatabaseContextService _databaseContextService; + private readonly IDatabaseContext _current; + + public ActiveDatabaseContext(IDatabaseContextService databaseContextService, IDatabaseContext current) + { + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + _current = current; + } + + public void Dispose() + { + if (_current == null) + { + return; + } + + _databaseContextService.Use(_current); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs index 3354546..7ea4f0e 100644 --- a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs @@ -25,6 +25,7 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, options.DatabaseContextFactory = dataAccessBuilder.Options.DatabaseContextFactory; }); + services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index ce611f3..84d9525 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -17,37 +17,37 @@ public DataRepository(IDatabaseGateway databaseGateway, IDataRowMapper dataRo _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> FetchItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task> FetchItems(IQuery query, CancellationToken cancellationToken = default) { - var rows = await _databaseGateway.GetRows(databaseContext, query, cancellationToken); + var rows = await _databaseGateway.GetRows(query, cancellationToken); return (IEnumerable)await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); } - public async Task FetchItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task FetchItem(IQuery query, CancellationToken cancellationToken = default) { - var row = await _databaseGateway.GetRow(databaseContext, query, cancellationToken); + var row = await _databaseGateway.GetRow(query, cancellationToken); return await Task.FromResult(row == null ? default : _dataRowMapper.Map(row).Result); } - public async Task> FetchMappedRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default) { - var row = await _databaseGateway.GetRow(databaseContext, query, cancellationToken); + var row = await _databaseGateway.GetRow(query, cancellationToken); return row == null ? null : _dataRowMapper.Map(row); } - public async Task>> FetchMappedRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default) { - var rows = await _databaseGateway.GetRows(databaseContext, query, cancellationToken); + var rows = await _databaseGateway.GetRows(query, cancellationToken); return rows.MappedRowsUsing(_dataRowMapper); } - public async Task Contains(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task Contains(IQuery query, CancellationToken cancellationToken = default) { - return await _databaseGateway.GetScalar(databaseContext, query, cancellationToken) == 1; + return await _databaseGateway.GetScalar(query, cancellationToken) == 1; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 68b558f..8d2b350 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -1,18 +1,23 @@ using System; using System.Data; +using System.Data.Common; using Shuttle.Core.Contract; namespace Shuttle.Core.Data { public class DatabaseContext : IDatabaseContext { + private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; private bool _dispose; private bool _disposed; + private readonly IDatabaseContext _activeContext; - public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory) + public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, + IDatabaseContextService databaseContextService) { _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); _dispose = true; Key = Guid.NewGuid(); @@ -24,6 +29,10 @@ public DatabaseContext(string providerName, IDbConnection dbConnection, IDbComma { Connection.Open(); } + + _activeContext = databaseContextService.HasCurrent ? databaseContextService.Current : null; + + _databaseContextService.Add(this); } public Guid Key { get; } @@ -41,7 +50,7 @@ public IDatabaseContext WithName(string name) public IDatabaseContext Suppressed() { - return new DatabaseContext(ProviderName, Connection, _dbCommandFactory) + return new DatabaseContext(ProviderName, Connection, _dbCommandFactory, _databaseContextService) { Transaction = Transaction, _dispose = false @@ -92,6 +101,13 @@ public void CommitTransaction() public void Dispose() { + _databaseContextService.Remove(this); + + if (_activeContext != null && _databaseContextService.Contains(_activeContext)) + { + _databaseContextService.Use(_activeContext); + } + Dispose(true); GC.SuppressFinalize(this); diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index bedbdd7..72331ca 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -12,7 +12,8 @@ public class DatabaseContextFactory : IDatabaseContextFactory private readonly DataAccessOptions _dataAccessOptions; public DatabaseContextFactory(IOptionsMonitor connectionStringOptions, IOptions dataAccessOptions, - IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory) + IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory, + IDatabaseContextService databaseContextService) { Guard.AgainstNull(dataAccessOptions, nameof(dataAccessOptions)); @@ -21,6 +22,7 @@ public DatabaseContextFactory(IOptionsMonitor connectio DbConnectionFactory = Guard.AgainstNull(dbConnectionFactory, nameof(dbConnectionFactory)); DbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); + DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } public async Task Create(string name) @@ -39,19 +41,24 @@ public async Task Create(string name) public async Task Create(string providerName, string connectionString) { - return await Task.FromResult(new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), - DbCommandFactory)).ConfigureAwait(false); + return await Task.FromResult(DatabaseContextService.ContainsConnectionString(connectionString) + ? DatabaseContextService.GetConnectionString(connectionString).Suppressed() + : new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), + DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); } public async Task Create(string providerName, DbConnection dbConnection) { Guard.AgainstNull(dbConnection, nameof(dbConnection)); - return await Task.FromResult(new DatabaseContext(providerName, dbConnection, DbCommandFactory)).ConfigureAwait(false); + return await Task.FromResult(DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) + ? DatabaseContextService.GetConnectionString(dbConnection.ConnectionString).Suppressed() + : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); } public IDbConnectionFactory DbConnectionFactory { get; } public IDbCommandFactory DbCommandFactory { get; } + public IDatabaseContextService DatabaseContextService { get; } public async Task Create() { diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs new file mode 100644 index 0000000..7be295c --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public class DatabaseContextService : IDatabaseContextService + { + private readonly List _databaseContexts = new List(); + private IDatabaseContext _current = null; + + public bool HasCurrent => _current != null; + + public IDatabaseContext Current + { + get + { + if (_current == null) + { + throw new InvalidOperationException(Resources.DatabaseContextMissing); + } + + return _current; + } + } + + public ActiveDatabaseContext Use(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); + + var current = _current; + + _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); + + if (_current == null) + { + throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); + } + + return new ActiveDatabaseContext(this, current); + } + + public IDatabaseContext Find(Predicate match) + { + Guard.AgainstNull(match, nameof(match)); + + return _databaseContexts.Find(match); + } + + public void Add(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); + + if (Find(context) != null) + { + throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); + } + + _databaseContexts.Add(context); + + Use(context); + } + + public void Remove(IDatabaseContext context) + { + Guard.AgainstNull(context, nameof(context)); + + var candidate = Find(context); + + if (candidate == null) + { + return; + } + + if (_current != null && candidate.Key.Equals(_current.Key)) + { + _current = null; + } + + _databaseContexts.Remove(candidate); + } + + private IDatabaseContext Find(IDatabaseContext context) + { + return Find(candidate => candidate.Key.Equals(context.Key)); + } + + public ActiveDatabaseContext Use(string name) + { + Guard.AgainstNullOrEmptyString(name, nameof(name)); + + var current = _current; + + _current = _databaseContexts.Find(candidate => + candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + if (_current == null) + { + throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); + } + + return new ActiveDatabaseContext(this, current); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index a50c6e6..30bb590 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -11,11 +11,20 @@ namespace Shuttle.Core.Data { public class DatabaseGateway : IDatabaseGateway { - public async Task GetDataTable(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + private readonly IDatabaseContextService _databaseContextService; + + public DatabaseGateway(IDatabaseContextService databaseContextService) { + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + } + + public async Task GetDataTable(IQuery query, CancellationToken cancellationToken = default) + { + Guard.AgainstNull(query, nameof(query)); + var results = new DataTable(); - using (var reader = await GetReader(Guard.AgainstNull(databaseContext, nameof(databaseContext)), Guard.AgainstNull(query, nameof(query)), cancellationToken).ConfigureAwait(false)) + using (var reader = await GetReader(query, cancellationToken).ConfigureAwait(false)) { results.Load(reader); } @@ -23,16 +32,16 @@ public async Task GetDataTable(IDatabaseContext databaseContext, IQue return results; } - public async Task> GetRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task> GetRows(IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTable(Guard.AgainstNull(databaseContext, nameof(databaseContext)), Guard.AgainstNull(query, nameof(query)), cancellationToken).ConfigureAwait(false); + var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); return table.Rows.Cast(); } - public async Task GetRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task GetRow(IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTable(Guard.AgainstNull(databaseContext, nameof(databaseContext)), Guard.AgainstNull(query, nameof(query)), cancellationToken).ConfigureAwait(false); + var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); if (table == null || table.Rows.Count == 0) { @@ -46,9 +55,13 @@ public async Task GetRow(IDatabaseContext databaseContext, IQuery query { }; - public async Task GetReader(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task GetReader(IQuery query, CancellationToken cancellationToken = default) { - await using (var command = (DbCommand)Guard.AgainstNull(databaseContext, nameof(databaseContext)).CreateCommand(Guard.AgainstNull(query, nameof(query)))) + Guard.AgainstNull(query, nameof(query)); + + var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); + + await using var _ = command.ConfigureAwait(false); { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); @@ -56,9 +69,11 @@ public async Task GetReader(IDatabaseContext databaseContext, IQuer } } - public async Task Execute(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task Execute(IQuery query, CancellationToken cancellationToken = default) { - await using (var command = (DbCommand)Guard.AgainstNull(databaseContext, nameof(databaseContext)).CreateCommand(Guard.AgainstNull(query, nameof(query)))) + Guard.AgainstNull(query, nameof(query)); + + await using (var command = (DbCommand)_databaseContextService.Current.CreateCommand(query)) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); @@ -66,9 +81,13 @@ public async Task Execute(IDatabaseContext databaseContext, IQuery query, C } } - public async Task GetScalar(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task GetScalar(IQuery query, CancellationToken cancellationToken = default) { - await using (var command = (DbCommand)Guard.AgainstNull(databaseContext, nameof(databaseContext)).CreateCommand(Guard.AgainstNull(query, nameof(query)))) + Guard.AgainstNull(query, nameof(query)); + + var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); + + await using (command.ConfigureAwait(false)) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index af3efe8..1faef8a 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -7,144 +7,144 @@ namespace Shuttle.Core.Data { public static class DataRepositoryExtensions { - public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.Contains(RawQuery.Create(sql), CancellationToken.None); } - public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.Contains(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(databaseContext, RawQuery.Create(sql), cancellationToken); + return await dataRepository.Contains(RawQuery.Create(sql), cancellationToken); } - public static async Task Contains(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItem(RawQuery.Create(sql), CancellationToken.None); } - public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItems(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); } - public static async Task> FetchItems(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql), CancellationToken.None); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs new file mode 100644 index 0000000..b4d7185 --- /dev/null +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -0,0 +1,91 @@ +using System; +using System.Data.Common; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public static class DatabaseContextServiceExtensions + { + public static bool Contains(this IDatabaseContextService databaseContextService, IDatabaseContext context) + { + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + Guard.AgainstNull(context, nameof(context)); + + return databaseContextService.Find(databaseContext => databaseContext.Key.Equals(context.Key)) != null; + } + + public static bool Contains(this IDatabaseContextService databaseContextService, string name) + { + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + Guard.AgainstNullOrEmptyString(name, nameof(name)); + + return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) != null; + } + + public static bool ContainsConnectionString(this IDatabaseContextService databaseContextService, string connectionString) + { + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); + + return databaseContextService.FindConnectionString(connectionString) != null; + } + + public static IDatabaseContext Get(this IDatabaseContextService databaseContextService, string name) + { + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + Guard.AgainstNullOrEmptyString(name, nameof(name)); + + return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); + } + + public static IDatabaseContext GetConnectionString(this IDatabaseContextService databaseContextService, string connectionString) + { + return databaseContextService.FindConnectionString(connectionString) ?? throw new Exception(Resources.DatabaseContextNotFoundException); + } + + public static ActiveDatabaseContext Use(this IDatabaseContextService databaseContextService, string name) + { + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + Guard.AgainstNullOrEmptyString(name, nameof(name)); + + return databaseContextService.Use(databaseContextService.Get(name)); + } + + public static IDatabaseContext FindConnectionString(this IDatabaseContextService databaseContextService, string connectionString) + { + Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); + + var result = databaseContextService.Find(candidate => + candidate.Connection.ConnectionString.Equals(connectionString, + StringComparison.OrdinalIgnoreCase)); + + if (result == null) + { + var matchDbConnectionStringBuilder = new DbConnectionStringBuilder + { + ConnectionString = connectionString.ToLowerInvariant() + }; + + matchDbConnectionStringBuilder.Remove("password"); + matchDbConnectionStringBuilder.Remove("pwd"); + + result = databaseContextService.Find(candidate => + { + var candidateDbConnectionStringBuilder = new DbConnectionStringBuilder + { + ConnectionString = candidate.Connection.ConnectionString + }; + + candidateDbConnectionStringBuilder.Remove("password"); + candidateDbConnectionStringBuilder.Remove("pwd"); + + return candidateDbConnectionStringBuilder.ConnectionString.Equals(matchDbConnectionStringBuilder.ConnectionString, + StringComparison.OrdinalIgnoreCase); + }); + } + + return result; + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index eacc5ef..a2ec2f2 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -9,172 +9,172 @@ namespace Shuttle.Core.Data { public static class DatabaseGatewayExtensions { - public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.Execute(RawQuery.Create(sql), CancellationToken.None); } - public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.Execute(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql), cancellationToken); + return await databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); } - public static async Task Execute(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetDataTable(RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetReader(RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRow(RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRows(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetScalar(RawQuery.Create(sql), CancellationToken.None); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 101d786..68f7791 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -7,228 +7,228 @@ namespace Shuttle.Core.Data { public static class QueryMapperExtensions { - public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) + public static async Task MapItem(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItem(RawQuery.Create(sql), CancellationToken.None); } - public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItem(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); } - public static async Task MapItem(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItem(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) + public static async Task> MapItems(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItems(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItems(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapItems(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObject(RawQuery.Create(sql), CancellationToken.None); } - public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObject(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); } - public static async Task MapObject(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObjects(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRow(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRow(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapRow(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRows(RawQuery.Create(sql), CancellationToken.None); } - public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRows(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) + public static async Task MapValue(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValue(RawQuery.Create(sql), CancellationToken.None); } - public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValue(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); } - public static async Task MapValue(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql) + public static async Task> MapValues(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValues(RawQuery.Create(sql), CancellationToken.None); } - public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters) + public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValues(RawQuery.Create(sql, parameters), CancellationToken.None); } - public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, CancellationToken cancellationToken) + public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapValues(this IQueryMapper queryMapper, IDatabaseContext databaseContext, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(databaseContext, RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDataRepository.cs b/Shuttle.Core.Data/IDataRepository.cs index 395055c..39da152 100644 --- a/Shuttle.Core.Data/IDataRepository.cs +++ b/Shuttle.Core.Data/IDataRepository.cs @@ -6,10 +6,10 @@ namespace Shuttle.Core.Data { public interface IDataRepository where T : class { - Task> FetchItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task FetchItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task> FetchMappedRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task>> FetchMappedRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task Contains(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task> FetchItems(IQuery query, CancellationToken cancellationToken = default); + Task FetchItem(IQuery query, CancellationToken cancellationToken = default); + Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default); + Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default); + Task Contains(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index a5f93b8..d698b69 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -12,5 +12,6 @@ public interface IDatabaseContextFactory IDbConnectionFactory DbConnectionFactory { get; } IDbCommandFactory DbCommandFactory { get; } + IDatabaseContextService DatabaseContextService { get; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs new file mode 100644 index 0000000..792df3f --- /dev/null +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -0,0 +1,14 @@ +using System; + +namespace Shuttle.Core.Data +{ + public interface IDatabaseContextService + { + bool HasCurrent { get; } + IDatabaseContext Current { get; } + ActiveDatabaseContext Use(IDatabaseContext context); + IDatabaseContext Find(Predicate match); + void Add(IDatabaseContext context); + void Remove(IDatabaseContext context); + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index 16ba213..dbbe448 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Threading; using System.Threading.Tasks; @@ -10,11 +11,11 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - Task GetReader(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task Execute(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task GetScalar(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task GetDataTable(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task> GetRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task GetRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task GetReader(IQuery query, CancellationToken cancellationToken = default); + Task Execute(IQuery query, CancellationToken cancellationToken = default); + Task GetScalar(IQuery query, CancellationToken cancellationToken = default); + Task GetDataTable(IQuery query, CancellationToken cancellationToken = default); + Task> GetRows(IQuery query, CancellationToken cancellationToken = default); + Task GetRow(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryMapper.cs b/Shuttle.Core.Data/IQueryMapper.cs index 57feb99..6c3bdc9 100644 --- a/Shuttle.Core.Data/IQueryMapper.cs +++ b/Shuttle.Core.Data/IQueryMapper.cs @@ -6,13 +6,13 @@ namespace Shuttle.Core.Data { public interface IQueryMapper { - Task> MapRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task>> MapRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task MapObject(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task> MapObjects(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task MapValue(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task> MapValues(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task MapItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); - Task> MapItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); + Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapObject(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapValue(IQuery query, CancellationToken cancellationToken = default); + Task> MapValues(IQuery query, CancellationToken cancellationToken = default); + Task MapItem(IQuery query, CancellationToken cancellationToken = default); + Task> MapItems(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IScriptProvider.cs b/Shuttle.Core.Data/IScriptProvider.cs index 8d37afb..0ab13bd 100644 --- a/Shuttle.Core.Data/IScriptProvider.cs +++ b/Shuttle.Core.Data/IScriptProvider.cs @@ -2,7 +2,7 @@ { public interface IScriptProvider { - string Get(string providerName, string scriptName); - string Get(string providerName, string scriptName, params object[] parameters); + string Get(string scriptName); + string Get(string scriptName, params object[] parameters); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index a2af89a..f938af3 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -25,25 +25,25 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> MapRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRow(await _databaseGateway.GetRow(databaseContext, query, cancellationToken)); + return _dataRowMapper.MapRow(await _databaseGateway.GetRow(query, cancellationToken)); } - public async Task>> MapRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRows(await _databaseGateway.GetRows(databaseContext, query, cancellationToken)); + return _dataRowMapper.MapRows(await _databaseGateway.GetRows(query, cancellationToken)); } - public async Task MapObject(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task MapObject(IQuery query, CancellationToken cancellationToken= default) where T : new() { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); @@ -55,13 +55,13 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return default; } - public async Task> MapObjects(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); @@ -73,11 +73,11 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return result; } - public async Task MapValue(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task MapValue(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -87,13 +87,13 @@ public async Task MapValue(IDatabaseContext databaseContext, IQuery query, return default; } - public async Task> MapValues(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task> MapValues(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -120,11 +120,11 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return result; } - public async Task MapItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task MapItem(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); if (await reader.ReadAsync(cancellationToken)) @@ -135,13 +135,13 @@ public async Task MapItem(IDatabaseContext databaseContext, IQuery quer return default; } - public async Task> MapItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default) + public async Task> MapItems(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(databaseContext, query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); var columns = GetColumns(reader); diff --git a/Shuttle.Core.Data/ScriptProvider.cs b/Shuttle.Core.Data/ScriptProvider.cs index 870547a..0e2f87b 100644 --- a/Shuttle.Core.Data/ScriptProvider.cs +++ b/Shuttle.Core.Data/ScriptProvider.cs @@ -10,34 +10,35 @@ public class ScriptProvider : IScriptProvider { private static readonly object Lock = new object(); private readonly ScriptProviderOptions _options; + private readonly IDatabaseContextService _databaseContextService; private readonly string[] _emptyFiles = Array.Empty(); private readonly Dictionary _scripts = new Dictionary(); - public ScriptProvider(IOptions options) + public ScriptProvider(IOptions options, IDatabaseContextService databaseContextService) { Guard.AgainstNull(options, nameof(options)); _options = Guard.AgainstNull(options.Value, nameof(options.Value)); + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public string Get(string providerName, string scriptName) + public string Get(string scriptName) { - return Get(providerName, scriptName, null); + return Get(scriptName, null); } - public string Get(string providerName, string scriptName, params object[] parameters) + public string Get(string scriptName, params object[] parameters) { - Guard.AgainstNullOrEmptyString(providerName, nameof(providerName)); Guard.AgainstNullOrEmptyString(scriptName, nameof(scriptName)); - var key = Key(providerName, scriptName); + var key = Key(scriptName); lock (Lock) { if (!_scripts.ContainsKey(key)) { - AddScript(providerName, scriptName); + AddScript(scriptName); } return parameters != null @@ -46,17 +47,17 @@ public string Get(string providerName, string scriptName, params object[] parame } } - private string Key(string providerName, string scriptName) + private string Key(string scriptName) { lock (Lock) { - return $"[{providerName}]-{scriptName}"; + return $"[{_databaseContextService.Current.ProviderName}]-{scriptName}"; } } - private void AddScript(string providerName, string scriptName) + private void AddScript(string scriptName) { - var key = Key(providerName, scriptName); + var key = Key(scriptName); lock (Lock) { @@ -69,12 +70,12 @@ private void AddScript(string providerName, string scriptName) if (!string.IsNullOrEmpty(_options.ScriptFolder) && Directory.Exists(_options.ScriptFolder)) { - files = Directory.GetFiles(_options.ScriptFolder, FormattedFileName(providerName, scriptName), SearchOption.AllDirectories); + files = Directory.GetFiles(_options.ScriptFolder, FormattedFileName(scriptName), SearchOption.AllDirectories); } if (files.Length == 0) { - AddEmbeddedScript(providerName, scriptName); + AddEmbeddedScript(scriptName); return; } @@ -89,29 +90,29 @@ private void AddScript(string providerName, string scriptName) } } - private string FormattedFileName(string providerName, string scriptName) + private string FormattedFileName(string scriptName) { - return FormattedScriptPath(_options.FileNameFormat, providerName, scriptName); + return FormattedScriptPath(_options.FileNameFormat, scriptName); } - private string FormattedResourceName(string providerName, string scriptName) + private string FormattedResourceName(string scriptName) { - return FormattedScriptPath(_options.ResourceNameFormat, providerName, scriptName); + return FormattedScriptPath(_options.ResourceNameFormat, scriptName); } - private string FormattedScriptPath(string format, string providerName, string scriptName) + private string FormattedScriptPath(string format, string scriptName) { - return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", providerName); + return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", _databaseContextService.Current.ProviderName); } - private void AddEmbeddedScript(string providerName, string scriptName) + private void AddEmbeddedScript(string scriptName) { if (_options.ResourceAssembly == null) { throw new InvalidOperationException(Resources.ResourceAssemblyMissingException); } - var path = _options.ResourceNameFormat != null ? FormattedResourceName(providerName, scriptName) : scriptName; + var path = _options.ResourceNameFormat != null ? FormattedResourceName(scriptName) : scriptName; using (var stream = _options.ResourceAssembly.GetManifestResourceStream(path)) { @@ -122,7 +123,7 @@ private void AddEmbeddedScript(string providerName, string scriptName) using (var reader = new StreamReader(stream)) { - _scripts.Add(Key(providerName, scriptName), reader.ReadToEnd()); + _scripts.Add(Key(scriptName), reader.ReadToEnd()); } } } From 8380b0ec2dfe22477a444c0f7fb9df15af3a463f Mon Sep 17 00:00:00 2001 From: Eben Date: Wed, 15 Feb 2023 21:14:59 +0200 Subject: [PATCH 10/37] - ambient context --- .../DatabaseContextFactoryFixture.cs | 6 +-- .../DatabaseContextFixture.cs | 25 ++++----- Shuttle.Core.Data.Tests/Fixture.cs | 4 +- .../Mapping/DataRowMapperFixture.cs | 4 +- .../Mapping/MappingFixture.cs | 6 ++- .../Mapping/QueryMapperFixture.cs | 2 +- Shuttle.Core.Data/DatabaseContext.cs | 32 ++++++------ Shuttle.Core.Data/DatabaseContextFactory.cs | 22 ++++---- Shuttle.Core.Data/DatabaseContextService.cs | 52 +++++++++++++------ Shuttle.Core.Data/DatabaseGateway.cs | 6 +-- Shuttle.Core.Data/DbConnectionFactory.cs | 2 +- Shuttle.Core.Data/IDatabaseContext.cs | 8 +-- Shuttle.Core.Data/IDatabaseContextFactory.cs | 8 +-- Shuttle.Core.Data/IDbConnectionFactory.cs | 3 +- Shuttle.Core.Data/Shuttle.Core.Data.csproj | 1 + 15 files changed, 104 insertions(+), 77 deletions(-) diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 973ab4f..954073e 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -23,12 +23,12 @@ public void Should_be_able_to_create_a_database_context() } [Test] - public async Task Should_be_able_to_get_an_existing_database_context() + public void Should_be_able_to_get_an_existing_database_context() { var factory = GetDatabaseContextFactory(); - using (var context = await factory.Create(DefaultConnectionStringName)) - using (var existingContext = await factory.Create(DefaultConnectionStringName)) + using (var context = factory.Create(DefaultConnectionStringName)) + using (var existingContext = factory.Create(DefaultConnectionStringName)) { Assert.IsNotNull(context); Assert.IsNotNull(existingContext); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index e4e8bca..d58b242 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -1,6 +1,7 @@ using System; using System.Data; using System.Data.Common; +using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Moq; using NUnit.Framework; @@ -26,10 +27,10 @@ public void Should_not_be_able_to_create_a_non_existent_connection() { Assert.Throws(() => { - using ( - new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object, new DatabaseContextService())) + using (var databaseContext = new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), + new Mock().Object, new DatabaseContextService())) { + databaseContext.Connection.Open(); } }); } @@ -45,39 +46,39 @@ public void Should_be_able_to_create_a_valid_connection() } [Test] - public void Should_be_able_to_begin_and_commit_a_transaction() + public async Task Should_be_able_to_begin_and_commit_a_transaction() { using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { - connection.BeginTransaction(); - connection.CommitTransaction(); + await connection.BeginTransaction(); + await connection.CommitTransaction(); } } [Test] - public void Should_be_able_to_begin_and_rollback_a_transaction() + public async Task Should_be_able_to_begin_and_rollback_a_transaction() { using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { - connection.BeginTransaction(); + await connection.BeginTransaction(); } } [Test] - public void Should_be_able_to_call_commit_without_a_transaction() + public async Task Should_be_able_to_call_commit_without_a_transaction() { using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { - connection.CommitTransaction(); + await connection.CommitTransaction(); } } @@ -95,7 +96,7 @@ public void Should_be_able_to_call_dispose_more_than_once() } [Test] - public void Should_be_able_to_create_a_command() + public async Task Should_be_able_to_create_a_command() { var dbCommandFactory = new Mock(); var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); @@ -107,7 +108,7 @@ public void Should_be_able_to_create_a_command() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) { - connection.CreateCommand(query.Object); + await connection.CreateCommand(query.Object); } dbCommandFactory.VerifyAll(); diff --git a/Shuttle.Core.Data.Tests/Fixture.cs b/Shuttle.Core.Data.Tests/Fixture.cs index d243f64..173e117 100644 --- a/Shuttle.Core.Data.Tests/Fixture.cs +++ b/Shuttle.Core.Data.Tests/Fixture.cs @@ -43,9 +43,9 @@ protected IDatabaseContextFactory GetDatabaseContextFactory() return Provider.GetRequiredService(); } - protected async Task GetDatabaseContext() + protected IDatabaseContext GetDatabaseContext() { - return await GetDatabaseContextFactory().Create(DefaultConnectionStringName).ConfigureAwait(false); + return GetDatabaseContextFactory().Create(DefaultConnectionStringName); } protected IDatabaseGateway GetDatabaseGateway() diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index b48fe0a..4439a77 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -32,7 +32,7 @@ select top 1 BasicMapping "); - using (await GetDatabaseContext()) + using (GetDatabaseContext()) { var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); @@ -72,7 +72,7 @@ Age as TheAge BasicMapping "); - using (await GetDatabaseContext()) + using (GetDatabaseContext()) { var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index 0d4b23f..f308fab 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -1,5 +1,7 @@ using System.Threading.Tasks; using NUnit.Framework; +using Shuttle.Core.Threading; +using static Shuttle.Core.Data.DatabaseContextService; namespace Shuttle.Core.Data.Tests; @@ -8,7 +10,9 @@ public class MappingFixture : Fixture [SetUp] public async Task SetUp() { - using (await GetDatabaseContext()) + AmbientContext.SetData("__DatabaseContextService-AmbientData__", new AmbientData()); + + using (GetDatabaseContext()) { await GetDatabaseGateway().Execute(RawQuery.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index c2ae170..f6af2d9 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -32,7 +32,7 @@ select top 1 BasicMapping "); - using (await GetDatabaseContext()) + using (GetDatabaseContext()) { var item = await mapper.MapObject(queryRow); var items = await mapper.MapObjects(queryRows); diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 8d2b350..2a7cf11 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -1,6 +1,7 @@ using System; using System.Data; using System.Data.Common; +using System.Threading.Tasks; using Shuttle.Core.Contract; namespace Shuttle.Core.Data @@ -25,11 +26,6 @@ public DatabaseContext(string providerName, IDbConnection dbConnection, IDbComma ProviderName = Guard.AgainstNullOrEmptyString(providerName, "providerName"); Connection = Guard.AgainstNull(dbConnection, nameof(dbConnection)); - if (dbConnection.State == ConnectionState.Closed) - { - Connection.Open(); - } - _activeContext = databaseContextService.HasCurrent ? databaseContextService.Current : null; _databaseContextService.Add(this); @@ -64,38 +60,44 @@ public IDatabaseContext SuppressDispose() return this; } - public IDbCommand CreateCommand(IQuery query) + public async Task CreateCommand(IQuery query) { - var command = _dbCommandFactory.Create(Connection, Guard.AgainstNull(query, nameof(query))); + var command = _dbCommandFactory.Create(await GetOpenConnection().ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); command.Transaction = Transaction; return command; } - public bool HasTransaction => Transaction != null; - - public IDatabaseContext BeginTransaction() + private async Task GetOpenConnection() { - return BeginTransaction(IsolationLevel.Unspecified); + if (Connection.State == ConnectionState.Closed) + { + await ((DbConnection)Connection).OpenAsync().ConfigureAwait(false); + } + + return (DbConnection)Connection; } - public IDatabaseContext BeginTransaction(IsolationLevel isolationLevel) + public bool HasTransaction => Transaction != null; + + public async Task BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { if (!HasTransaction && System.Transactions.Transaction.Current == null) { - Transaction = Connection.BeginTransaction(isolationLevel); + Transaction = await (await GetOpenConnection().ConfigureAwait(false)).BeginTransactionAsync(isolationLevel); } return this; } - public void CommitTransaction() + public async Task CommitTransaction() { if (!HasTransaction) { return; } - Transaction.Commit(); + await ((DbTransaction)Transaction).CommitAsync(); + Transaction = null; } diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 72331ca..a047b68 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -25,7 +25,7 @@ public DatabaseContextFactory(IOptionsMonitor connectio DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public async Task Create(string name) + public IDatabaseContext Create(string name) { var connectionStringOptions = _connectionStringOptions.Get(name); @@ -34,42 +34,42 @@ public async Task Create(string name) throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, name)); } - var databaseContext = await Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString).ConfigureAwait(false); + var databaseContext = Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString); return databaseContext.WithName(name); } - public async Task Create(string providerName, string connectionString) + public IDatabaseContext Create(string providerName, string connectionString) { - return await Task.FromResult(DatabaseContextService.ContainsConnectionString(connectionString) + return DatabaseContextService.ContainsConnectionString(connectionString) ? DatabaseContextService.GetConnectionString(connectionString).Suppressed() : new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), - DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); + DbCommandFactory, DatabaseContextService); } - public async Task Create(string providerName, DbConnection dbConnection) + public IDatabaseContext Create(string providerName, DbConnection dbConnection) { Guard.AgainstNull(dbConnection, nameof(dbConnection)); - return await Task.FromResult(DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) + return DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) ? DatabaseContextService.GetConnectionString(dbConnection.ConnectionString).Suppressed() - : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService)).ConfigureAwait(false); + : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService); } public IDbConnectionFactory DbConnectionFactory { get; } public IDbCommandFactory DbCommandFactory { get; } public IDatabaseContextService DatabaseContextService { get; } - public async Task Create() + public IDatabaseContext Create() { if (!string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName)) { - return await Create(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName).ConfigureAwait(false); + return Create(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName); } if (!string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName) && !string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionString)) { - return await Create(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName, _dataAccessOptions.DatabaseContextFactory.DefaultConnectionString).ConfigureAwait(false); + return Create(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName, _dataAccessOptions.DatabaseContextFactory.DefaultConnectionString); } throw new InvalidOperationException(Resources.DatabaseContextFactoryOptionsException); diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 7be295c..e9b9f8e 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -1,26 +1,46 @@ using System; using System.Collections.Generic; using Shuttle.Core.Contract; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class DatabaseContextService : IDatabaseContextService { - private readonly List _databaseContexts = new List(); - private IDatabaseContext _current = null; + private const string AmbientDataKey = "__DatabaseContextService-AmbientData__"; - public bool HasCurrent => _current != null; + public class AmbientData + { + public readonly List DatabaseContexts = new List(); + public IDatabaseContext Current = null; + } + + public bool HasCurrent => GetAmbientData().Current != null; + + private AmbientData GetAmbientData() + { + var result = AmbientContext.GetData(AmbientDataKey) as AmbientData; + + if (result == null) + { + result = new AmbientData(); + + AmbientContext.SetData(AmbientDataKey, result); + } + + return result; + } public IDatabaseContext Current { get { - if (_current == null) + if (GetAmbientData().Current == null) { throw new InvalidOperationException(Resources.DatabaseContextMissing); } - return _current; + return GetAmbientData().Current; } } @@ -28,11 +48,11 @@ public ActiveDatabaseContext Use(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); - var current = _current; + var current = GetAmbientData().Current; - _current = _databaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); + GetAmbientData().Current = GetAmbientData().DatabaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); - if (_current == null) + if (GetAmbientData().Current == null) { throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); } @@ -44,7 +64,7 @@ public IDatabaseContext Find(Predicate match) { Guard.AgainstNull(match, nameof(match)); - return _databaseContexts.Find(match); + return GetAmbientData().DatabaseContexts.Find(match); } public void Add(IDatabaseContext context) @@ -56,7 +76,7 @@ public void Add(IDatabaseContext context) throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); } - _databaseContexts.Add(context); + GetAmbientData().DatabaseContexts.Add(context); Use(context); } @@ -72,12 +92,12 @@ public void Remove(IDatabaseContext context) return; } - if (_current != null && candidate.Key.Equals(_current.Key)) + if (GetAmbientData().Current != null && candidate.Key.Equals(GetAmbientData().Current.Key)) { - _current = null; + GetAmbientData().Current = null; } - _databaseContexts.Remove(candidate); + GetAmbientData().DatabaseContexts.Remove(candidate); } private IDatabaseContext Find(IDatabaseContext context) @@ -89,12 +109,12 @@ public ActiveDatabaseContext Use(string name) { Guard.AgainstNullOrEmptyString(name, nameof(name)); - var current = _current; + var current = GetAmbientData().Current; - _current = _databaseContexts.Find(candidate => + GetAmbientData().Current = GetAmbientData().DatabaseContexts.Find(candidate => candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - if (_current == null) + if (GetAmbientData().Current == null) { throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); } diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 30bb590..d577971 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -59,7 +59,7 @@ public async Task GetReader(IQuery query, CancellationToken cancell { Guard.AgainstNull(query, nameof(query)); - var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); + var command = (DbCommand)await (_databaseContextService.Current.CreateCommand(query).ConfigureAwait(false)); await using var _ = command.ConfigureAwait(false); { @@ -73,7 +73,7 @@ public async Task Execute(IQuery query, CancellationToken cancellationToken { Guard.AgainstNull(query, nameof(query)); - await using (var command = (DbCommand)_databaseContextService.Current.CreateCommand(query)) + await using (var command = (DbCommand)await (_databaseContextService.Current.CreateCommand(query).ConfigureAwait(false))) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); @@ -85,7 +85,7 @@ public async Task GetScalar(IQuery query, CancellationToken cancellationTo { Guard.AgainstNull(query, nameof(query)); - var command = (DbCommand)_databaseContextService.Current.CreateCommand(query); + var command = (DbCommand)await _databaseContextService.Current.CreateCommand(query).ConfigureAwait(false); await using (command.ConfigureAwait(false)) { diff --git a/Shuttle.Core.Data/DbConnectionFactory.cs b/Shuttle.Core.Data/DbConnectionFactory.cs index 3ae036e..1cf2d44 100644 --- a/Shuttle.Core.Data/DbConnectionFactory.cs +++ b/Shuttle.Core.Data/DbConnectionFactory.cs @@ -11,7 +11,7 @@ public class DbConnectionFactory : IDbConnectionFactory { }; - public DbConnection Create(string providerName, string connectionString) + public IDbConnection Create(string providerName, string connectionString) { Guard.AgainstNullOrEmptyString(providerName, nameof(providerName)); Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index 72d4756..5c627a0 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,6 +1,7 @@ using System; using System.Data; using System.Data.Common; +using System.Threading.Tasks; namespace Shuttle.Core.Data { @@ -11,14 +12,13 @@ public interface IDatabaseContext : IDisposable IDbTransaction Transaction { get; } IDbConnection Connection { get; } - IDbCommand CreateCommand(IQuery query); + Task CreateCommand(IQuery query); bool HasTransaction { get; } string ProviderName { get; } - IDatabaseContext BeginTransaction(); - IDatabaseContext BeginTransaction(IsolationLevel isolationLevel); - void CommitTransaction(); + Task BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); + Task CommitTransaction(); IDatabaseContext WithName(string name); IDatabaseContext Suppressed(); IDatabaseContext SuppressDispose(); diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index d698b69..ca31692 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -5,10 +5,10 @@ namespace Shuttle.Core.Data { public interface IDatabaseContextFactory { - Task Create(string name); - Task Create(string providerName, string connectionString); - Task Create(string providerName, DbConnection dbConnection); - Task Create(); + IDatabaseContext Create(string name); + IDatabaseContext Create(string providerName, string connectionString); + IDatabaseContext Create(string providerName, DbConnection dbConnection); + IDatabaseContext Create(); IDbConnectionFactory DbConnectionFactory { get; } IDbCommandFactory DbCommandFactory { get; } diff --git a/Shuttle.Core.Data/IDbConnectionFactory.cs b/Shuttle.Core.Data/IDbConnectionFactory.cs index 2c644e9..9e02610 100644 --- a/Shuttle.Core.Data/IDbConnectionFactory.cs +++ b/Shuttle.Core.Data/IDbConnectionFactory.cs @@ -1,6 +1,5 @@ using System; using System.Data; -using System.Data.Common; namespace Shuttle.Core.Data { @@ -8,6 +7,6 @@ public interface IDbConnectionFactory { event EventHandler DbConnectionCreated; - DbConnection Create(string providerName, string connectionString); + IDbConnection Create(string providerName, string connectionString); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Shuttle.Core.Data.csproj b/Shuttle.Core.Data/Shuttle.Core.Data.csproj index 8c634c3..d1199ef 100644 --- a/Shuttle.Core.Data/Shuttle.Core.Data.csproj +++ b/Shuttle.Core.Data/Shuttle.Core.Data.csproj @@ -20,6 +20,7 @@ + From a352e44727dad0cb4ab77b8b06dfb8addd407675 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 27 Aug 2023 11:34:33 +0200 Subject: [PATCH 11/37] - added back synchronous methods --- .../DataRepositoryFixture.cs | 24 +-- .../DatabaseContextFactoryFixture.cs | 101 ++++++------ .../DatabaseContextFixture.cs | 74 ++++++++- .../DatabaseContextServiceFixture.cs | 86 ++++++---- Shuttle.Core.Data.Tests/Fixture.cs | 1 - .../Mapping/DataRowMapperFixture.cs | 26 +-- .../Mapping/MappingFixture.cs | 6 +- .../Mapping/QueryMapperFixture.cs | 24 +-- Shuttle.Core.Data/.package/package.nuspec | 1 + Shuttle.Core.Data/DataRepository.cs | 49 ++++-- Shuttle.Core.Data/DatabaseContext.cs | 53 +++++- Shuttle.Core.Data/DatabaseContextService.cs | 2 +- Shuttle.Core.Data/DatabaseGateway.cs | 96 +++++++++-- .../Extensions/DataRepositoryExtensions.cs | 40 ++--- .../Extensions/DatabaseGatewayExtensions.cs | 48 +++--- .../Extensions/QueryMapperExtensions.cs | 64 ++++---- Shuttle.Core.Data/IAssembler.cs | 2 +- Shuttle.Core.Data/IDataRepository.cs | 16 +- Shuttle.Core.Data/IDatabaseContext.cs | 9 +- Shuttle.Core.Data/IDatabaseGateway.cs | 20 ++- Shuttle.Core.Data/IDbProviderFactories.cs | 9 - Shuttle.Core.Data/IQueryMapper.cs | 25 ++- Shuttle.Core.Data/QueryMapper.cs | 155 ++++++++++++++++-- 23 files changed, 640 insertions(+), 291 deletions(-) delete mode 100644 Shuttle.Core.Data/IDbProviderFactories.cs diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index 68acb50..f5c7e1e 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -20,12 +20,12 @@ public async Task Should_be_able_to_fetch_all_items() var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); + gateway.Setup(m => m.GetRowsAsync(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = (await repository.FetchItems(query.Object)).ToList(); + var result = (await repository.FetchItemsAsync(query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -41,12 +41,12 @@ public async Task Should_be_able_to_fetch_a_single_item() var dataRow = new DataTable().NewRow(); var anObject = new object(); - gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = await repository.FetchItem(query.Object); + var result = await repository.FetchItemAsync(query.Object); Assert.IsNotNull(result); Assert.AreSame(anObject, result); @@ -58,11 +58,11 @@ public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); + gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); var repository = new DataRepository(gateway.Object, new Mock>().Object); - var result = await repository.FetchItem(query.Object); + var result = await repository.FetchItemAsync(query.Object); Assert.IsNull(result); } @@ -73,11 +73,11 @@ public async Task Should_be_able_to_call_contains() var gateway = new Mock(); var query = new Mock(); - gateway.Setup(m => m.GetScalar(query.Object, CancellationToken.None)).ReturnsAsync(1); + gateway.Setup(m => m.GetScalarAsync(query.Object, CancellationToken.None)).ReturnsAsync(1); var repository = new DataRepository(gateway.Object, new Mock>().Object); - Assert.That(await repository.Contains(query.Object), Is.True); + Assert.That(await repository.ContainsAsync(query.Object), Is.True); } [Test] @@ -90,12 +90,12 @@ public async Task Should_be_able_to_fetch_mapped_rows() var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); + gateway.Setup(m => m.GetRowsAsync(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = (await repository.FetchMappedRows(query.Object)).ToList(); + var result = (await repository.FetchMappedRowsAsync(query.Object)).ToList(); Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); @@ -113,12 +113,12 @@ public async Task Should_be_able_to_fetch_a_single_row() var anObject = new object(); var mappedRow = new MappedRow(dataRow, anObject); - gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); var repository = new DataRepository(gateway.Object, mapper.Object); - var result = await repository.FetchMappedRow(query.Object); + var result = await repository.FetchMappedRowAsync(query.Object); Assert.IsNotNull(result); Assert.AreSame(dataRow, result.Row); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 954073e..05239c3 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -1,61 +1,56 @@ using System; -using System.Data; using System.Data.Common; using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; -namespace Shuttle.Core.Data.Tests +namespace Shuttle.Core.Data.Tests; + +[TestFixture] +public class DatabaseContextFactoryFixture : Fixture { - [TestFixture] - public class DatabaseContextFactoryFixture : Fixture - { - [Test] - public void Should_be_able_to_create_a_database_context() - { - using (var context = GetDatabaseContextFactory().Create(DefaultConnectionStringName)) - { - Assert.IsNotNull(context); - } - } - - [Test] - public void Should_be_able_to_get_an_existing_database_context() - { - var factory = GetDatabaseContextFactory(); - - using (var context = factory.Create(DefaultConnectionStringName)) - using (var existingContext = factory.Create(DefaultConnectionStringName)) - { - Assert.IsNotNull(context); - Assert.IsNotNull(existingContext); - - Assert.AreSame(existingContext.Connection, context.Connection); - } - } - - [Test] - public void Should_be_able_to_check_connection_availability() - { - var databaseContextFactory = new Mock(); - - Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.True); - Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.True); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.True); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.True); - - databaseContextFactory.Setup(m => m.Create()).Throws(new Exception()); - databaseContextFactory.Setup(m => m.Create(It.IsAny())).Throws(new Exception()); - databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); - databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); - - Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.False); - Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.False); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.False); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.False); - } - } + [Test] + public void Should_be_able_to_create_a_database_context() + { + using (var context = GetDatabaseContextFactory().Create(DefaultConnectionStringName)) + { + Assert.IsNotNull(context); + } + } + + [Test] + public void Should_be_able_to_get_an_existing_database_context() + { + var factory = GetDatabaseContextFactory(); + + using (var context = factory.Create(DefaultConnectionStringName)) + using (var existingContext = factory.Create(DefaultConnectionStringName)) + { + Assert.IsNotNull(context); + Assert.IsNotNull(existingContext); + + Assert.AreSame(existingContext.Connection, context.Connection); + } + } + + [Test] + public void Should_be_able_to_check_connection_availability() + { + var databaseContextFactory = new Mock(); + + Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.True); + + databaseContextFactory.Setup(m => m.Create()).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny())).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); + + Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.False); + } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index d58b242..f2b3894 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -45,40 +45,77 @@ public void Should_be_able_to_create_a_valid_connection() } } + [Test] + public void Should_be_able_to_begin_and_commit_a_transaction() + { + using ( + var connection = + new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + connection.BeginTransaction(); + connection.CommitTransaction(); + } + } + [Test] - public async Task Should_be_able_to_begin_and_commit_a_transaction() + public async Task Should_be_able_to_begin_and_commit_a_transaction_async() { using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { - await connection.BeginTransaction(); - await connection.CommitTransaction(); + await connection.BeginTransactionAsync(); + await connection.CommitTransactionAsync(); } } [Test] - public async Task Should_be_able_to_begin_and_rollback_a_transaction() + public void Should_be_able_to_begin_and_rollback_a_transaction() + { + using ( + var connection = + new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + connection.BeginTransaction(); + } + } + + [Test] + public async Task Should_be_able_to_begin_and_rollback_a_transaction_async() { using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { - await connection.BeginTransaction(); + await connection.BeginTransactionAsync(); } } [Test] - public async Task Should_be_able_to_call_commit_without_a_transaction() + public void Should_be_able_to_call_commit_without_a_transaction() + { + using ( + var connection = + new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + connection.CommitTransaction(); + } + } + + [Test] + public async Task Should_be_able_to_call_commit_without_a_transaction_async() { using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { - await connection.CommitTransaction(); + await connection.CommitTransactionAsync(); } } @@ -96,7 +133,26 @@ public void Should_be_able_to_call_dispose_more_than_once() } [Test] - public async Task Should_be_able_to_create_a_command() + public void Should_be_able_to_create_a_command() + { + var dbCommandFactory = new Mock(); + var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); + var query = new Mock(); + var dbCommand = new Mock(); + + dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); + + using ( + var connection = new DatabaseContext("System.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) + { + connection.CreateCommand(query.Object); + } + + dbCommandFactory.VerifyAll(); + } + + [Test] + public async Task Should_be_able_to_create_a_command_async() { var dbCommandFactory = new Mock(); var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); @@ -108,7 +164,7 @@ public async Task Should_be_able_to_create_a_command() using ( var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) { - await connection.CreateCommand(query.Object); + await connection.CreateCommandAsync(query.Object); } dbCommandFactory.VerifyAll(); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index a11495d..d0eb758 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -1,53 +1,79 @@ -using System.Data; -using System.Data.Common; +using System.Data.Common; using System.Threading.Tasks; using Moq; using NUnit.Framework; -namespace Shuttle.Core.Data.Tests +namespace Shuttle.Core.Data.Tests; + +public class DatabaseContextServiceFixture : Fixture { - public class DatabaseContextServiceFixture : Fixture + [Test] + public void Should_be_able_to_use_different_contexts() { - [Test] - public async Task Should_be_able_to_use_different_contexts() + var cache = new DatabaseContextService(); + + var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, cache).WithName("mock-1"); + + Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); + + var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, cache).WithName("mock-2"); + + Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); + + using (cache.Use("mock-1")) { - var service = new DatabaseContextService(); + Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); + } - var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, service).WithName("mock-1"); + Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + using (cache.Use(context1)) + { + Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); + } + + Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); + } + + [Test] + public async Task Should_be_able_to_use_different_contexts_async() + { + var service = new DatabaseContextService(); - var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, service).WithName("mock-2"); + var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, service).WithName("mock-1"); - Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - using (service.Use("mock-1")) - { - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, service).WithName("mock-2"); - await AssertContextFlow(service, "mock-1"); - } + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + using (service.Use("mock-1")) + { + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - using (service.Use(context1)) - { - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - } + await AssertContextFlow(service, "mock-1"); + } - Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + + using (service.Use(context1)) + { + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); } - private async Task AssertContextFlow(IDatabaseContextService service, string name) + Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); + } + + private async Task AssertContextFlow(IDatabaseContextService service, string name) + { + Assert.That(service.Current, Is.Not.Null); + Assert.That(service.Current.Name, Is.EqualTo(name)); + + await Task.Run(() => { Assert.That(service.Current, Is.Not.Null); Assert.That(service.Current.Name, Is.EqualTo(name)); - - await Task.Run(() => - { - Assert.That(service.Current, Is.Not.Null); - Assert.That(service.Current.Name, Is.EqualTo(name)); - }); - } + }); } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Fixture.cs b/Shuttle.Core.Data.Tests/Fixture.cs index 173e117..dcae96f 100644 --- a/Shuttle.Core.Data.Tests/Fixture.cs +++ b/Shuttle.Core.Data.Tests/Fixture.cs @@ -1,6 +1,5 @@ using System; using System.Data.Common; -using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 4439a77..6ea78f3 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -34,14 +34,14 @@ select top 1 using (GetDatabaseContext()) { - var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); + var item = dataRowMapper.MapObject(await databaseGateway.GetRowAsync(rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRowsAsync(rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRowAsync(rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRowsAsync(rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -74,14 +74,14 @@ Age as TheAge using (GetDatabaseContext()) { - var item = dataRowMapper.MapObject(await databaseGateway.GetRow(rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRows(rowsQuery)); + var item = dataRowMapper.MapObject(await databaseGateway.GetRowAsync(rowQuery)); + var items = dataRowMapper.MapObjects(await databaseGateway.GetRowsAsync(rowsQuery)); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRow(rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRows(rowsQuery)); + var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRowAsync(rowQuery)); + var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRowsAsync(rowsQuery)); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -110,8 +110,8 @@ select top 1 using (GetDatabaseContext()) { - var value = dataRowMapper.MapValue(await databaseGateway.GetRow(rowQuery)); - var values = dataRowMapper.MapValues(await databaseGateway.GetRows(rowsQuery)); + var value = dataRowMapper.MapValue(await databaseGateway.GetRowAsync(rowQuery)); + var values = dataRowMapper.MapValues(await databaseGateway.GetRowsAsync(rowsQuery)); Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -149,15 +149,15 @@ public async Task Should_be_able_to_perform_dynamic_mapping() using (GetDatabaseContext()) { - var item = dataRowMapper.MapItem(await databaseGateway.GetRow(rowQuery)); + var item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(rowQuery)); Assert.IsNotNull(item); - var items = dataRowMapper.MapItems(await databaseGateway.GetRows(rowsQuery)); + var items = dataRowMapper.MapItems(await databaseGateway.GetRowsAsync(rowsQuery)); Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(await databaseGateway.GetRow(RawQuery.Create(rowSql, new { Id = id }))); + item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(RawQuery.Create(rowSql, new { Id = id }))); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index f308fab..c829b92 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -1,7 +1,5 @@ using System.Threading.Tasks; using NUnit.Framework; -using Shuttle.Core.Threading; -using static Shuttle.Core.Data.DatabaseContextService; namespace Shuttle.Core.Data.Tests; @@ -10,11 +8,9 @@ public class MappingFixture : Fixture [SetUp] public async Task SetUp() { - AmbientContext.SetData("__DatabaseContextService-AmbientData__", new AmbientData()); - using (GetDatabaseContext()) { - await GetDatabaseGateway().Execute(RawQuery.Create(@" + await GetDatabaseGateway().ExecuteAsync(RawQuery.Create(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index f6af2d9..96d995e 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -34,14 +34,14 @@ select top 1 using (GetDatabaseContext()) { - var item = await mapper.MapObject(queryRow); - var items = await mapper.MapObjects(queryRows); + var item = await mapper.MapObjectAsync(queryRow); + var items = await mapper.MapObjectsAsync(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = await mapper.MapRow(queryRow); - var mappedRows = await mapper.MapRows(queryRows); + var mappedRow = await mapper.MapRowAsync(queryRow); + var mappedRows = await mapper.MapRowsAsync(queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -73,14 +73,14 @@ Age as TheAge using (GetDatabaseContext()) { - var item = await mapper.MapObject(queryRow); - var items = await mapper.MapObjects(queryRows); + var item = await mapper.MapObjectAsync(queryRow); + var items = await mapper.MapObjectsAsync(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRow(queryRow).Result; - var mappedRows = await mapper.MapRows(queryRows); + var mappedRow = mapper.MapRowAsync(queryRow).Result; + var mappedRows = await mapper.MapRowsAsync(queryRows); Assert.IsNotNull(mappedRow); Assert.AreEqual(2, mappedRows.Count()); @@ -108,8 +108,8 @@ select top 1 using (GetDatabaseContext()) { - var value = mapper.MapValue(queryRow).Result; - var values = mapper.MapValues(queryRows).Result; + var value = mapper.MapValueAsync(queryRow).Result; + var values = mapper.MapValuesAsync(queryRows).Result; Assert.IsNotNull(value); Assert.AreEqual(2, values.Count()); @@ -142,8 +142,8 @@ select top 1 using (GetDatabaseContext()) { - var item = queryMapper.MapItem(queryRow).Result; - var items = queryMapper.MapItems(queryRows).Result; + var item = queryMapper.MapItemAsync(queryRow).Result; + var items = queryMapper.MapItemsAsync(queryRows).Result; Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index e078479..6c3febd 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -22,6 +22,7 @@ + diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index 84d9525..812c1a6 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -17,37 +17,66 @@ public DataRepository(IDatabaseGateway databaseGateway, IDataRowMapper dataRo _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> FetchItems(IQuery query, CancellationToken cancellationToken = default) + public IEnumerable FetchItems(IQuery query) { - var rows = await _databaseGateway.GetRows(query, cancellationToken); + return _databaseGateway.GetRows(query).MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList(); + } + + public async Task> FetchItemsAsync(IQuery query, CancellationToken cancellationToken = default) + { + var rows = await _databaseGateway.GetRowsAsync(query, cancellationToken); return (IEnumerable)await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); } - public async Task FetchItem(IQuery query, CancellationToken cancellationToken = default) + public T FetchItem(IQuery query) { - var row = await _databaseGateway.GetRow(query, cancellationToken); + var row = _databaseGateway.GetRow(query); + + return row == null ? default : _dataRowMapper.Map(row).Result; + } + + public async Task FetchItemAsync(IQuery query, CancellationToken cancellationToken = default) + { + var row = await _databaseGateway.GetRowAsync(query, cancellationToken); return await Task.FromResult(row == null ? default : _dataRowMapper.Map(row).Result); } - public async Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default) + public MappedRow FetchMappedRow(IQuery query) + { + var row = _databaseGateway.GetRow(query); + + return row == null ? null : _dataRowMapper.Map(row); + } + + public IEnumerable> FetchMappedRows(IQuery query) { - var row = await _databaseGateway.GetRow(query, cancellationToken); + return _databaseGateway.GetRows(query).MappedRowsUsing(_dataRowMapper); + } + + public async Task> FetchMappedRowAsync(IQuery query, CancellationToken cancellationToken = default) + { + var row = await _databaseGateway.GetRowAsync(query, cancellationToken); return row == null ? null : _dataRowMapper.Map(row); } - public async Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default) + public async Task>> FetchMappedRowsAsync(IQuery query, CancellationToken cancellationToken = default) { - var rows = await _databaseGateway.GetRows(query, cancellationToken); + var rows = await _databaseGateway.GetRowsAsync(query, cancellationToken); return rows.MappedRowsUsing(_dataRowMapper); } - public async Task Contains(IQuery query, CancellationToken cancellationToken = default) + public bool Contains(IQuery query) + { + return _databaseGateway.GetScalar(query) == 1; + } + + public async Task ContainsAsync(IQuery query, CancellationToken cancellationToken = default) { - return await _databaseGateway.GetScalar(query, cancellationToken) == 1; + return await _databaseGateway.GetScalarAsync(query, cancellationToken) == 1; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 2a7cf11..a72336b 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -14,8 +14,7 @@ public class DatabaseContext : IDatabaseContext private bool _disposed; private readonly IDatabaseContext _activeContext; - public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, - IDatabaseContextService databaseContextService) + public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) { _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); @@ -60,14 +59,31 @@ public IDatabaseContext SuppressDispose() return this; } - public async Task CreateCommand(IQuery query) + public IDbCommand CreateCommand(IQuery query) { - var command = _dbCommandFactory.Create(await GetOpenConnection().ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); + var command = _dbCommandFactory.Create(GetOpenConnection(), Guard.AgainstNull(query, nameof(query))); command.Transaction = Transaction; return command; } - private async Task GetOpenConnection() + public async Task CreateCommandAsync(IQuery query) + { + var command = _dbCommandFactory.Create(await GetOpenConnectionAsync().ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); + command.Transaction = Transaction; + return command; + } + + private DbConnection GetOpenConnection() + { + if (Connection.State == ConnectionState.Closed) + { + ((DbConnection)Connection).Open(); + } + + return (DbConnection)Connection; + } + + private async Task GetOpenConnectionAsync() { if (Connection.State == ConnectionState.Closed) { @@ -79,17 +95,38 @@ private async Task GetOpenConnection() public bool HasTransaction => Transaction != null; - public async Task BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) + public IDatabaseContext BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) + { + if (!HasTransaction && System.Transactions.Transaction.Current == null) + { + Transaction = GetOpenConnection().BeginTransaction(isolationLevel); + } + + return this; + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { if (!HasTransaction && System.Transactions.Transaction.Current == null) { - Transaction = await (await GetOpenConnection().ConfigureAwait(false)).BeginTransactionAsync(isolationLevel); + Transaction = await (await GetOpenConnectionAsync().ConfigureAwait(false)).BeginTransactionAsync(isolationLevel); } return this; } - public async Task CommitTransaction() + public void CommitTransaction() + { + if (!HasTransaction) + { + return; + } + + Transaction.Commit(); + Transaction = null; + } + + public async Task CommitTransactionAsync() { if (!HasTransaction) { diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index e9b9f8e..2a869f8 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -9,7 +9,7 @@ public class DatabaseContextService : IDatabaseContextService { private const string AmbientDataKey = "__DatabaseContextService-AmbientData__"; - public class AmbientData + private class AmbientData { public readonly List DatabaseContexts = new List(); public IDatabaseContext Current = null; diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index d577971..3c9ef64 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -18,13 +18,30 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public async Task GetDataTable(IQuery query, CancellationToken cancellationToken = default) + public DataTable GetDataTable(IQuery query) + { + Guard.AgainstNull(query, nameof(query)); + + using (var reader = GetReader(query)) + { + var results = new DataTable(); + + if (reader != null) + { + results.Load(reader); + } + + return results; + } + } + + public async Task GetDataTableAsync(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var results = new DataTable(); - using (var reader = await GetReader(query, cancellationToken).ConfigureAwait(false)) + using (var reader = await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) { results.Load(reader); } @@ -32,16 +49,33 @@ public async Task GetDataTable(IQuery query, CancellationToken cancel return results; } - public async Task> GetRows(IQuery query, CancellationToken cancellationToken = default) + public IEnumerable GetRows(IQuery query) + { + return GetDataTable(query).Rows.Cast(); + } + + public async Task> GetRowsAsync(IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); + var table = await GetDataTableAsync(query, cancellationToken).ConfigureAwait(false); return table.Rows.Cast(); } - public async Task GetRow(IQuery query, CancellationToken cancellationToken = default) + public DataRow GetRow(IQuery query) { - var table = await GetDataTable(query, cancellationToken).ConfigureAwait(false); + var table = GetDataTable(query); + + if ((table == null) || (table.Rows.Count == 0)) + { + return null; + } + + return table.Rows[0]; + } + + public async Task GetRowAsync(IQuery query, CancellationToken cancellationToken = default) + { + var table = await GetDataTableAsync(query, cancellationToken).ConfigureAwait(false); if (table == null || table.Rows.Count == 0) { @@ -55,11 +89,23 @@ public async Task GetRow(IQuery query, CancellationToken cancellationTo { }; - public async Task GetReader(IQuery query, CancellationToken cancellationToken = default) + public IDataReader GetReader(IQuery query) { Guard.AgainstNull(query, nameof(query)); - var command = (DbCommand)await (_databaseContextService.Current.CreateCommand(query).ConfigureAwait(false)); + using (var command = _databaseContextService.Current.CreateCommand(query)) + { + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + + return command.ExecuteReader(); + } + } + + public async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default) + { + Guard.AgainstNull(query, nameof(query)); + + var command = (DbCommand)await (_databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false)); await using var _ = command.ConfigureAwait(false); { @@ -69,11 +115,23 @@ public async Task GetReader(IQuery query, CancellationToken cancell } } - public async Task Execute(IQuery query, CancellationToken cancellationToken = default) + public int Execute(IQuery query) + { + Guard.AgainstNull(query, nameof(query)); + + using (var command = _databaseContextService.Current.CreateCommand(query)) + { + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + + return command.ExecuteNonQuery(); + } + } + + public async Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - await using (var command = (DbCommand)await (_databaseContextService.Current.CreateCommand(query).ConfigureAwait(false))) + await using (var command = (DbCommand)await (_databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); @@ -81,11 +139,25 @@ public async Task Execute(IQuery query, CancellationToken cancellationToken } } - public async Task GetScalar(IQuery query, CancellationToken cancellationToken = default) + public T GetScalar(IQuery query) + { + Guard.AgainstNull(query, nameof(query)); + + using (var command = _databaseContextService.Current.CreateCommand(query)) + { + DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + + var scalar = command.ExecuteScalar(); + + return scalar != null && scalar != DBNull.Value ? (T)scalar : default(T); + } + } + + public async Task GetScalarAsync(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - var command = (DbCommand)await _databaseContextService.Current.CreateCommand(query).ConfigureAwait(false); + var command = (DbCommand)await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false); await using (command.ConfigureAwait(false)) { diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index 1faef8a..ccf9011 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -11,140 +11,140 @@ public static async Task Contains(this IDataRepository dataRepositor { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.ContainsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.ContainsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql), cancellationToken); + return await dataRepository.ContainsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.ContainsAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItemAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItemAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItemAsync(RawQuery.Create(sql), cancellationToken); } public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItem(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItemAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchItemsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItemsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchItemsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItemsAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql), CancellationToken.None); + return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); + return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index a2ec2f2..8e60287 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -13,168 +13,168 @@ public static async Task Execute(this IDatabaseGateway databaseGateway, str { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.ExecuteAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.ExecuteAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.ExecuteAsync(RawQuery.Create(sql), cancellationToken); } public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.ExecuteAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql), cancellationToken); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql), cancellationToken); } public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReader(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRowAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRowAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRowAsync(RawQuery.Create(sql), cancellationToken); } public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRowAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetRowsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetRowsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRows(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql), CancellationToken.None); + return await databaseGateway.GetScalarAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetScalarAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetScalarAsync(RawQuery.Create(sql), cancellationToken); } public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetScalarAsync(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 68f7791..1a0f65a 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -11,224 +11,224 @@ public static async Task MapItem(this IQueryMapper queryMapper, string { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItemAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItemAsync(RawQuery.Create(sql), cancellationToken); } public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItem(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapItemsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItemsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItemsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItemsAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObjectAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjectAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObjectAsync(RawQuery.Create(sql), cancellationToken); } public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjectAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapObjectsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjectsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObjectsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjectsAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRowAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRowAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRowAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRowAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapRowsAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRowsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapRowsAsync(RawQuery.Create(sql), cancellationToken); } public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task MapValue(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValueAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValueAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValueAsync(RawQuery.Create(sql), cancellationToken); } public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValueAsync(RawQuery.Create(sql, parameters), cancellationToken); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql), CancellationToken.None); + return await queryMapper.MapValuesAsync(RawQuery.Create(sql), CancellationToken.None); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValuesAsync(RawQuery.Create(sql, parameters), CancellationToken.None); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapValuesAsync(RawQuery.Create(sql), cancellationToken); } public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValuesAsync(RawQuery.Create(sql, parameters), cancellationToken); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IAssembler.cs b/Shuttle.Core.Data/IAssembler.cs index a122772..ace8bbe 100644 --- a/Shuttle.Core.Data/IAssembler.cs +++ b/Shuttle.Core.Data/IAssembler.cs @@ -2,7 +2,7 @@ namespace Shuttle.Core.Data { - public interface IAssembler where T : class + public interface IAssembler where T : class { IEnumerable Assemble(MappedData data); } diff --git a/Shuttle.Core.Data/IDataRepository.cs b/Shuttle.Core.Data/IDataRepository.cs index 39da152..34e423f 100644 --- a/Shuttle.Core.Data/IDataRepository.cs +++ b/Shuttle.Core.Data/IDataRepository.cs @@ -6,10 +6,16 @@ namespace Shuttle.Core.Data { public interface IDataRepository where T : class { - Task> FetchItems(IQuery query, CancellationToken cancellationToken = default); - Task FetchItem(IQuery query, CancellationToken cancellationToken = default); - Task> FetchMappedRow(IQuery query, CancellationToken cancellationToken = default); - Task>> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default); - Task Contains(IQuery query, CancellationToken cancellationToken = default); + IEnumerable FetchItems(IQuery query); + T FetchItem(IQuery query); + MappedRow FetchMappedRow(IQuery query); + IEnumerable> FetchMappedRows(IQuery query); + bool Contains(IQuery query); + + Task> FetchItemsAsync(IQuery query, CancellationToken cancellationToken = default); + Task FetchItemAsync(IQuery query, CancellationToken cancellationToken = default); + Task> FetchMappedRowAsync(IQuery query, CancellationToken cancellationToken = default); + Task>> FetchMappedRowsAsync(IQuery query, CancellationToken cancellationToken = default); + Task ContainsAsync(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index 5c627a0..6a3d43d 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -12,13 +12,16 @@ public interface IDatabaseContext : IDisposable IDbTransaction Transaction { get; } IDbConnection Connection { get; } - Task CreateCommand(IQuery query); + IDbCommand CreateCommand(IQuery query); + Task CreateCommandAsync(IQuery query); bool HasTransaction { get; } string ProviderName { get; } - Task BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); - Task CommitTransaction(); + IDatabaseContext BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); + Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified); + void CommitTransaction(); + Task CommitTransactionAsync(); IDatabaseContext WithName(string name); IDatabaseContext Suppressed(); IDatabaseContext SuppressDispose(); diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index dbbe448..0531b6a 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Threading; using System.Threading.Tasks; @@ -11,11 +10,18 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - Task GetReader(IQuery query, CancellationToken cancellationToken = default); - Task Execute(IQuery query, CancellationToken cancellationToken = default); - Task GetScalar(IQuery query, CancellationToken cancellationToken = default); - Task GetDataTable(IQuery query, CancellationToken cancellationToken = default); - Task> GetRows(IQuery query, CancellationToken cancellationToken = default); - Task GetRow(IQuery query, CancellationToken cancellationToken = default); + IDataReader GetReader(IQuery query); + int Execute(IQuery query); + T GetScalar(IQuery query); + DataTable GetDataTable(IQuery query); + IEnumerable GetRows(IQuery query); + DataRow GetRow(IQuery query); + + Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default); + Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default); + Task GetScalarAsync(IQuery query, CancellationToken cancellationToken = default); + Task GetDataTableAsync(IQuery query, CancellationToken cancellationToken = default); + Task> GetRowsAsync(IQuery query, CancellationToken cancellationToken = default); + Task GetRowAsync(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDbProviderFactories.cs b/Shuttle.Core.Data/IDbProviderFactories.cs deleted file mode 100644 index 0ab2c18..0000000 --- a/Shuttle.Core.Data/IDbProviderFactories.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Data.Common; - -namespace Shuttle.Core.Data -{ - public interface IDbProviderFactories - { - DbProviderFactory GetFactory(string providerName); - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryMapper.cs b/Shuttle.Core.Data/IQueryMapper.cs index 6c3bdc9..83eeceb 100644 --- a/Shuttle.Core.Data/IQueryMapper.cs +++ b/Shuttle.Core.Data/IQueryMapper.cs @@ -6,13 +6,22 @@ namespace Shuttle.Core.Data { public interface IQueryMapper { - Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task MapObject(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new(); - Task MapValue(IQuery query, CancellationToken cancellationToken = default); - Task> MapValues(IQuery query, CancellationToken cancellationToken = default); - Task MapItem(IQuery query, CancellationToken cancellationToken = default); - Task> MapItems(IQuery query, CancellationToken cancellationToken = default); + MappedRow MapRow(IQuery query) where T : new(); + IEnumerable> MapRows(IQuery query) where T : new(); + T MapObject(IQuery query) where T : new(); + IEnumerable MapObjects(IQuery query) where T : new(); + T MapValue(IQuery query); + IEnumerable MapValues(IQuery query); + dynamic MapItem(IQuery query); + IEnumerable MapItems(IQuery query); + + Task> MapRowAsync(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task>> MapRowsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapObjectAsync(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task> MapObjectsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new(); + Task MapValueAsync(IQuery query, CancellationToken cancellationToken = default); + Task> MapValuesAsync(IQuery query, CancellationToken cancellationToken = default); + Task MapItemAsync(IQuery query, CancellationToken cancellationToken = default); + Task> MapItemsAsync(IQuery query, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index f938af3..f50b5ad 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -25,25 +25,56 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public async Task> MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() + public MappedRow MapRow(IQuery query) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRow(await _databaseGateway.GetRow(query, cancellationToken)); + return _dataRowMapper.MapRow(_databaseGateway.GetRow(query)); } - public async Task>> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new() + public async Task> MapRowAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRows(await _databaseGateway.GetRows(query, cancellationToken)); + return _dataRowMapper.MapRow(await _databaseGateway.GetRowAsync(query, cancellationToken)); } - public async Task MapObject(IQuery query, CancellationToken cancellationToken= default) where T : new() + public IEnumerable> MapRows(IQuery query) where T : new() { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + return _dataRowMapper.MapRows(_databaseGateway.GetRows(query)); + } + + public async Task>> MapRowsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() + { + Guard.AgainstNull(query, nameof(query)); + + return _dataRowMapper.MapRows(await _databaseGateway.GetRowsAsync(query, cancellationToken)); + } + + public T MapObject(IQuery query) where T : new() + { + Guard.AgainstNull(query, nameof(query)); + + using (var reader = _databaseGateway.GetReader(query)) + { + var columns = GetColumns(reader); + + if (reader.Read()) + { + return Map(GetPropertyInfo(), reader, columns); + } + } + + return default; + } + + public async Task MapObjectAsync(IQuery query, CancellationToken cancellationToken= default) where T : new() + { + Guard.AgainstNull(query, nameof(query)); + + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); var columns = GetColumns(reader); @@ -55,13 +86,32 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return default; } - public async Task> MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new() + public IEnumerable MapObjects(IQuery query) where T : new() { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + using (var reader = _databaseGateway.GetReader(query)) + { + var columns = GetColumns(reader); + + while (reader.Read()) + { + result.Add(Map(GetPropertyInfo(), reader, columns)); + } + } + + return result; + } + + public async Task> MapObjectsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() + { + Guard.AgainstNull(query, nameof(query)); + + var result = new List(); + + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); var columns = GetColumns(reader); @@ -73,11 +123,26 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return result; } - public async Task MapValue(IQuery query, CancellationToken cancellationToken = default) + public T MapValue(IQuery query) { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + using (var reader = _databaseGateway.GetReader(query)) + { + while (reader.Read()) + { + return MapRowValue(reader); + } + } + + return default; + } + + public async Task MapValueAsync(IQuery query, CancellationToken cancellationToken = default) + { + Guard.AgainstNull(query, nameof(query)); + + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -87,13 +152,35 @@ public async Task MapValue(IQuery query, CancellationToken cancellationTok return default; } - public async Task> MapValues(IQuery query, CancellationToken cancellationToken = default) + public IEnumerable MapValues(IQuery query) + { + Guard.AgainstNull(query, nameof(query)); + + var result = new List(); + + using (var reader = _databaseGateway.GetReader(query)) + { + while (reader.Read()) + { + var value = MapRowValue(reader); + + if (value != null) + { + result.Add(value); + } + } + } + + return result; + } + + public async Task> MapValuesAsync(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); while (await reader.ReadAsync(cancellationToken)) { @@ -120,11 +207,28 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return result; } - public async Task MapItem(IQuery query, CancellationToken cancellationToken = default) + public dynamic MapItem(IQuery query) + { + Guard.AgainstNull(query, nameof(query)); + + using (var reader = _databaseGateway.GetReader(query)) + { + var columns = GetColumns(reader); + + if (reader.Read()) + { + return DynamicMap(reader, columns); + } + } + + return default; + } + + public async Task MapItemAsync(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); var columns = GetColumns(reader); if (await reader.ReadAsync(cancellationToken)) @@ -135,13 +239,32 @@ public async Task MapItem(IQuery query, CancellationToken cancellationT return default; } - public async Task> MapItems(IQuery query, CancellationToken cancellationToken = default) + public IEnumerable MapItems(IQuery query) + { + Guard.AgainstNull(query, nameof(query)); + + var result = new List(); + + using (var reader = _databaseGateway.GetReader(query)) + { + var columns = GetColumns(reader); + + while (reader.Read()) + { + result.Add(DynamicMap(reader, columns)); + } + } + + return result; + } + + public async Task> MapItemsAsync(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReader(query, cancellationToken); + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); var columns = GetColumns(reader); From a4f648ba9054f3387e8938e4774bc807e5fbe63e Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 3 Sep 2023 09:19:07 +0200 Subject: [PATCH 12/37] - removed empty delegates from events --- Shuttle.Core.Data/DatabaseGateway.cs | 16 +++++++--------- Shuttle.Core.Data/DbConnectionFactory.cs | 6 ++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 3c9ef64..ad17c21 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -85,9 +85,7 @@ public async Task GetRowAsync(IQuery query, CancellationToken cancellat return table.Rows[0]; } - public event EventHandler DbCommandCreated = delegate - { - }; + public event EventHandler DbCommandCreated; public IDataReader GetReader(IQuery query) { @@ -95,7 +93,7 @@ public IDataReader GetReader(IQuery query) using (var command = _databaseContextService.Current.CreateCommand(query)) { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); return command.ExecuteReader(); } @@ -109,7 +107,7 @@ public async Task GetReaderAsync(IQuery query, CancellationToken ca await using var _ = command.ConfigureAwait(false); { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); return await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); } @@ -121,7 +119,7 @@ public int Execute(IQuery query) using (var command = _databaseContextService.Current.CreateCommand(query)) { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); return command.ExecuteNonQuery(); } @@ -133,7 +131,7 @@ public async Task ExecuteAsync(IQuery query, CancellationToken cancellation await using (var command = (DbCommand)await (_databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); return await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } @@ -145,7 +143,7 @@ public T GetScalar(IQuery query) using (var command = _databaseContextService.Current.CreateCommand(query)) { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); var scalar = command.ExecuteScalar(); @@ -161,7 +159,7 @@ public async Task GetScalarAsync(IQuery query, CancellationToken cancellat await using (command.ConfigureAwait(false)) { - DbCommandCreated.Invoke(this, new DbCommandCreatedEventArgs(command)); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); var scalar = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); diff --git a/Shuttle.Core.Data/DbConnectionFactory.cs b/Shuttle.Core.Data/DbConnectionFactory.cs index 1cf2d44..dee1ed9 100644 --- a/Shuttle.Core.Data/DbConnectionFactory.cs +++ b/Shuttle.Core.Data/DbConnectionFactory.cs @@ -7,9 +7,7 @@ namespace Shuttle.Core.Data { public class DbConnectionFactory : IDbConnectionFactory { - public event EventHandler DbConnectionCreated = delegate - { - }; + public event EventHandler DbConnectionCreated; public IDbConnection Create(string providerName, string connectionString) { @@ -26,7 +24,7 @@ public IDbConnection Create(string providerName, string connectionString) connection.ConnectionString = connectionString; - DbConnectionCreated.Invoke(this, new DbConnectionCreatedEventArgs(connection)); + DbConnectionCreated?.Invoke(this, new DbConnectionCreatedEventArgs(connection)); return connection; } From 32079f621f0cfbbb513a2a10278bbdfea900fb7a Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 3 Sep 2023 15:24:42 +0200 Subject: [PATCH 13/37] - sync flag argument hack --- .../Fakes/OrderAssembler.cs | 6 + Shuttle.Core.Data/DataRepository.cs | 20 +-- Shuttle.Core.Data/DatabaseContext.cs | 77 ++++++----- Shuttle.Core.Data/DatabaseContextFactory.cs | 1 - Shuttle.Core.Data/DatabaseGateway.cs | 114 +++++++--------- .../Extensions/AssemblerExtensions.cs | 6 + .../Extensions/DataRepositoryExtensions.cs | 78 +++++------ .../Extensions/DatabaseGatewayExtensions.cs | 97 +++++++------ .../Extensions/QueryMapperExtensions.cs | 128 +++++++++--------- Shuttle.Core.Data/IAssembler.cs | 4 +- Shuttle.Core.Data/IDataRepository.cs | 10 +- Shuttle.Core.Data/IDatabaseGateway.cs | 12 +- Shuttle.Core.Data/IQueryMapper.cs | 16 +-- Shuttle.Core.Data/QueryMapper.cs | 32 ++--- 14 files changed, 303 insertions(+), 298 deletions(-) diff --git a/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs b/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs index 06fa1ac..45289c1 100644 --- a/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs +++ b/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; namespace Shuttle.Core.Data.Tests.Fakes { @@ -25,5 +26,10 @@ public IEnumerable Assemble(MappedData data) return result; } + + public Task> AssembleAsync(MappedData data) + { + return Task.FromResult(Assemble(data)); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index 812c1a6..e923f5f 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -17,9 +17,9 @@ public DataRepository(IDatabaseGateway databaseGateway, IDataRowMapper dataRo _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public IEnumerable FetchItems(IQuery query) + public IEnumerable FetchItems(IQuery query, CancellationToken cancellationToken = default) { - return _databaseGateway.GetRows(query).MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList(); + return _databaseGateway.GetRows(query, cancellationToken).MappedRowsUsing(_dataRowMapper).Select(row => row.Result).ToList(); } public async Task> FetchItemsAsync(IQuery query, CancellationToken cancellationToken = default) @@ -29,9 +29,9 @@ public async Task> FetchItemsAsync(IQuery query, CancellationToke return (IEnumerable)await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); } - public T FetchItem(IQuery query) + public T FetchItem(IQuery query, CancellationToken cancellationToken = default) { - var row = _databaseGateway.GetRow(query); + var row = _databaseGateway.GetRow(query, cancellationToken); return row == null ? default : _dataRowMapper.Map(row).Result; } @@ -43,16 +43,16 @@ public async Task FetchItemAsync(IQuery query, CancellationToken cancellation return await Task.FromResult(row == null ? default : _dataRowMapper.Map(row).Result); } - public MappedRow FetchMappedRow(IQuery query) + public MappedRow FetchMappedRow(IQuery query, CancellationToken cancellationToken = default) { - var row = _databaseGateway.GetRow(query); + var row = _databaseGateway.GetRow(query, cancellationToken); return row == null ? null : _dataRowMapper.Map(row); } - public IEnumerable> FetchMappedRows(IQuery query) + public IEnumerable> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default) { - return _databaseGateway.GetRows(query).MappedRowsUsing(_dataRowMapper); + return _databaseGateway.GetRows(query, cancellationToken).MappedRowsUsing(_dataRowMapper); } public async Task> FetchMappedRowAsync(IQuery query, CancellationToken cancellationToken = default) @@ -69,9 +69,9 @@ public async Task>> FetchMappedRowsAsync(IQuery query, return rows.MappedRowsUsing(_dataRowMapper); } - public bool Contains(IQuery query) + public bool Contains(IQuery query, CancellationToken cancellationToken = default) { - return _databaseGateway.GetScalar(query) == 1; + return _databaseGateway.GetScalar(query, cancellationToken) == 1; } public async Task ContainsAsync(IQuery query, CancellationToken cancellationToken = default) diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index a72336b..3105073 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -61,33 +61,33 @@ public IDatabaseContext SuppressDispose() public IDbCommand CreateCommand(IQuery query) { - var command = _dbCommandFactory.Create(GetOpenConnection(), Guard.AgainstNull(query, nameof(query))); - command.Transaction = Transaction; - return command; + return CreateCommandAsync(query, true).GetAwaiter().GetResult(); } public async Task CreateCommandAsync(IQuery query) { - var command = _dbCommandFactory.Create(await GetOpenConnectionAsync().ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); - command.Transaction = Transaction; - return command; + return await CreateCommandAsync(query, false).ConfigureAwait(false); } - private DbConnection GetOpenConnection() + private async Task CreateCommandAsync(IQuery query, bool sync) { - if (Connection.State == ConnectionState.Closed) - { - ((DbConnection)Connection).Open(); - } - - return (DbConnection)Connection; + var command = _dbCommandFactory.Create(sync ? GetOpenConnectionAsync(true).GetAwaiter().GetResult() : await GetOpenConnectionAsync(false).ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); + command.Transaction = Transaction; + return command; } - private async Task GetOpenConnectionAsync() + private async Task GetOpenConnectionAsync(bool sync) { if (Connection.State == ConnectionState.Closed) { - await ((DbConnection)Connection).OpenAsync().ConfigureAwait(false); + if (sync) + { + ((DbConnection)Connection).Open(); + } + else + { + await ((DbConnection)Connection).OpenAsync().ConfigureAwait(false); + } } return (DbConnection)Connection; @@ -97,19 +97,28 @@ private async Task GetOpenConnectionAsync() public IDatabaseContext BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { - if (!HasTransaction && System.Transactions.Transaction.Current == null) - { - Transaction = GetOpenConnection().BeginTransaction(isolationLevel); - } - - return this; + return BeginTransactionAsync(isolationLevel, true).GetAwaiter().GetResult(); } public async Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { - if (!HasTransaction && System.Transactions.Transaction.Current == null) + return await BeginTransactionAsync(isolationLevel, false).ConfigureAwait(false); + } + + private async Task BeginTransactionAsync(IsolationLevel isolationLevel, bool sync) + { + if (HasTransaction || System.Transactions.Transaction.Current != null) + { + return this; + } + + if (sync) { - Transaction = await (await GetOpenConnectionAsync().ConfigureAwait(false)).BeginTransactionAsync(isolationLevel); + Transaction = GetOpenConnectionAsync(true).GetAwaiter().GetResult().BeginTransaction(isolationLevel); + } + else + { + Transaction = await (await GetOpenConnectionAsync(false).ConfigureAwait(false)).BeginTransactionAsync(isolationLevel); } return this; @@ -117,23 +126,29 @@ public async Task BeginTransactionAsync(IsolationLevel isolati public void CommitTransaction() { - if (!HasTransaction) - { - return; - } - - Transaction.Commit(); - Transaction = null; + CommitTransactionAsync(true).GetAwaiter().GetResult(); } public async Task CommitTransactionAsync() + { + await CommitTransactionAsync(false).ConfigureAwait(false); + } + + private async Task CommitTransactionAsync(bool sync) { if (!HasTransaction) { return; } - await ((DbTransaction)Transaction).CommitAsync(); + if (sync) + { + Transaction.Commit(); + } + else + { + await ((DbTransaction)Transaction).CommitAsync(); + } Transaction = null; } diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index a047b68..833fafb 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -1,6 +1,5 @@ using System; using System.Data.Common; -using System.Threading.Tasks; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index ad17c21..bfd933f 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -18,30 +18,23 @@ public DatabaseGateway(IDatabaseContextService databaseContextService) _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public DataTable GetDataTable(IQuery query) + public DataTable GetDataTable(IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - using (var reader = GetReader(query)) - { - var results = new DataTable(); - - if (reader != null) - { - results.Load(reader); - } - - return results; - } + return GetDataTableAsync(query, cancellationToken, true).GetAwaiter().GetResult(); } public async Task GetDataTableAsync(IQuery query, CancellationToken cancellationToken = default) + { + return await GetDataTableAsync(query, cancellationToken, false).ConfigureAwait(false); + } + + private async Task GetDataTableAsync(IQuery query, CancellationToken cancellationToken, bool sync) { Guard.AgainstNull(query, nameof(query)); var results = new DataTable(); - using (var reader = await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) + using (var reader = sync ? GetReader(query, cancellationToken) : await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) { results.Load(reader); } @@ -49,33 +42,30 @@ public async Task GetDataTableAsync(IQuery query, CancellationToken c return results; } - public IEnumerable GetRows(IQuery query) + public IEnumerable GetRows(IQuery query, CancellationToken cancellationToken = default) { - return GetDataTable(query).Rows.Cast(); + return GetDataTable(query, cancellationToken).Rows.Cast(); } public async Task> GetRowsAsync(IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTableAsync(query, cancellationToken).ConfigureAwait(false); - return table.Rows.Cast(); + return (await GetDataTableAsync(query, cancellationToken).ConfigureAwait(false)).Rows.Cast(); } - public DataRow GetRow(IQuery query) + public DataRow GetRow(IQuery query, CancellationToken cancellationToken = default) { - var table = GetDataTable(query); - - if ((table == null) || (table.Rows.Count == 0)) - { - return null; - } - - return table.Rows[0]; + return GetRowAsync(query, cancellationToken, true).GetAwaiter().GetResult(); } public async Task GetRowAsync(IQuery query, CancellationToken cancellationToken = default) { - var table = await GetDataTableAsync(query, cancellationToken).ConfigureAwait(false); + return await GetRowAsync(query, cancellationToken, false).ConfigureAwait(false); + } + + private async Task GetRowAsync(IQuery query, CancellationToken cancellationToken, bool sync) + { + var table = sync ? GetDataTable(query, cancellationToken) : await GetDataTableAsync(query, cancellationToken).ConfigureAwait(false); if (table == null || table.Rows.Count == 0) { @@ -87,81 +77,69 @@ public async Task GetRowAsync(IQuery query, CancellationToken cancellat public event EventHandler DbCommandCreated; - public IDataReader GetReader(IQuery query) + public IDataReader GetReader(IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - using (var command = _databaseContextService.Current.CreateCommand(query)) - { - DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - - return command.ExecuteReader(); - } + return GetReaderAsync(query, cancellationToken, true).GetAwaiter().GetResult(); } public async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - var command = (DbCommand)await (_databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false)); - - await using var _ = command.ConfigureAwait(false); - { - DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - - return await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); - } + return await GetReaderAsync(query, cancellationToken, false).ConfigureAwait(false); } - public int Execute(IQuery query) + private async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken, bool sync) { Guard.AgainstNull(query, nameof(query)); - using (var command = _databaseContextService.Current.CreateCommand(query)) + using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - return command.ExecuteNonQuery(); + return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); } } - public async Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default) + public int Execute(IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); - - await using (var command = (DbCommand)await (_databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) - { - DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); + return ExecuteAsync(query, cancellationToken, true).GetAwaiter().GetResult(); + } - return await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); - } + public async Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default) + { + return await ExecuteAsync(query, cancellationToken, false).ConfigureAwait(false); } - public T GetScalar(IQuery query) + private async Task ExecuteAsync(IQuery query, CancellationToken cancellationToken, bool sync) { Guard.AgainstNull(query, nameof(query)); - using (var command = _databaseContextService.Current.CreateCommand(query)) + using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - var scalar = command.ExecuteScalar(); - - return scalar != null && scalar != DBNull.Value ? (T)scalar : default(T); + return sync ? command.ExecuteNonQuery() : await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } } + public T GetScalar(IQuery query, CancellationToken cancellationToken = default) + { + return GetScalarAsync(query, cancellationToken, true).GetAwaiter().GetResult(); + } + public async Task GetScalarAsync(IQuery query, CancellationToken cancellationToken = default) { - Guard.AgainstNull(query, nameof(query)); + return await GetScalarAsync(query, cancellationToken, false).ConfigureAwait(false); + } - var command = (DbCommand)await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false); + public async Task GetScalarAsync(IQuery query, CancellationToken cancellationToken, bool sync) + { + Guard.AgainstNull(query, nameof(query)); - await using (command.ConfigureAwait(false)) + using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - var scalar = await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + var scalar = sync ? command.ExecuteScalar() : await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); return scalar != null && scalar != DBNull.Value ? (T)scalar : default; } diff --git a/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs b/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs index 3c5c502..a3e6800 100644 --- a/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs +++ b/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; namespace Shuttle.Core.Data { @@ -8,5 +9,10 @@ public static T AssembleItem(this IAssembler assembler, MappedData data) w { return assembler.Assemble(data).FirstOrDefault(); } + + public static async Task AssembleItemAsync(this IAssembler assembler, MappedData data) where T : class + { + return (await assembler.AssembleAsync(data).ConfigureAwait(false)).FirstOrDefault(); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index ccf9011..dc7797f 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -7,144 +7,144 @@ namespace Shuttle.Core.Data { public static class DataRepositoryExtensions { - public static async Task Contains(this IDataRepository dataRepository, string sql) where T : class + public static bool Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.ContainsAsync(RawQuery.Create(sql), CancellationToken.None); + return dataRepository.Contains(RawQuery.Create(sql), cancellationToken); } - public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.ContainsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.ContainsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static bool Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.ContainsAsync(RawQuery.Create(sql), cancellationToken); + return dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); return await dataRepository.ContainsAsync(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql) where T : class + public static T FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemAsync(RawQuery.Create(sql), CancellationToken.None); + return dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItemAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static T FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemAsync(RawQuery.Create(sql), cancellationToken); + return dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItemAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql) where T : class + public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemsAsync(RawQuery.Create(sql), CancellationToken.None); + return dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchItemsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemsAsync(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchItemsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql) where T : class + public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql), CancellationToken.None); + return dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql) where T : class + public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql), CancellationToken.None); + return dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters) where T : class + public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken) where T : class + public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken) where T : class + public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index 8e60287..9c39920 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Data; -using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; @@ -9,172 +8,172 @@ namespace Shuttle.Core.Data { public static class DatabaseGatewayExtensions { - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql) + public static int Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.ExecuteAsync(RawQuery.Create(sql), CancellationToken.None); + return databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); } - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.ExecuteAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.ExecuteAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static int Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.ExecuteAsync(RawQuery.Create(sql), cancellationToken); + return databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.ExecuteAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.ExecuteAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql) + public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql), CancellationToken.None); + return databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql) + public static IDataReader GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql), CancellationToken.None); + return databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql), cancellationToken); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql) + public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowAsync(RawQuery.Create(sql), CancellationToken.None); + return databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRowAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowAsync(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRowAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql) + public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowsAsync(RawQuery.Create(sql), CancellationToken.None); + return databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetRowsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowsAsync(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql) + public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalarAsync(RawQuery.Create(sql), CancellationToken.None); + return databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters) + public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalarAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await databaseGateway.GetScalarAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken) + public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalarAsync(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalarAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetScalarAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 1a0f65a..0c6d142 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -7,228 +7,228 @@ namespace Shuttle.Core.Data { public static class QueryMapperExtensions { - public static async Task MapItem(this IQueryMapper queryMapper, string sql) + public static dynamic MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); } - - public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters) + + public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItemAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql) + public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemsAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapItemsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemsAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItemsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql) where T : new() + public static T MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjectAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static T MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjectAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql) where T : new() + public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectsAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapObjectsAsync(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectsAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapObjectsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql) where T : new() + public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRowAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); return await queryMapper.MapRowAsync(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql) where T : new() + public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowsAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters) where T : new() + public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowsAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapRowsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) where T : new() + public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowsAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task>> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) where T : new() + public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRowsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql) + public static T MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValueAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValueAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValueAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static T MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValueAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValueAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValueAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql) + public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValuesAsync(RawQuery.Create(sql), CancellationToken.None); + return queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters) + public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValuesAsync(RawQuery.Create(sql, parameters), CancellationToken.None); + return await queryMapper.MapValuesAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken) + public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValuesAsync(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); } - public static async Task> MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken) + public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValuesAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapValuesAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IAssembler.cs b/Shuttle.Core.Data/IAssembler.cs index ace8bbe..9203f0a 100644 --- a/Shuttle.Core.Data/IAssembler.cs +++ b/Shuttle.Core.Data/IAssembler.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Threading.Tasks; namespace Shuttle.Core.Data { - public interface IAssembler where T : class + public interface IAssembler where T : class { IEnumerable Assemble(MappedData data); + Task> AssembleAsync(MappedData data); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDataRepository.cs b/Shuttle.Core.Data/IDataRepository.cs index 34e423f..908317c 100644 --- a/Shuttle.Core.Data/IDataRepository.cs +++ b/Shuttle.Core.Data/IDataRepository.cs @@ -6,11 +6,11 @@ namespace Shuttle.Core.Data { public interface IDataRepository where T : class { - IEnumerable FetchItems(IQuery query); - T FetchItem(IQuery query); - MappedRow FetchMappedRow(IQuery query); - IEnumerable> FetchMappedRows(IQuery query); - bool Contains(IQuery query); + IEnumerable FetchItems(IQuery query, CancellationToken cancellationToken = default); + T FetchItem(IQuery query, CancellationToken cancellationToken = default); + MappedRow FetchMappedRow(IQuery query, CancellationToken cancellationToken = default); + IEnumerable> FetchMappedRows(IQuery query, CancellationToken cancellationToken = default); + bool Contains(IQuery query, CancellationToken cancellationToken = default); Task> FetchItemsAsync(IQuery query, CancellationToken cancellationToken = default); Task FetchItemAsync(IQuery query, CancellationToken cancellationToken = default); diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index 0531b6a..95af9c4 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -10,12 +10,12 @@ public interface IDatabaseGateway { event EventHandler DbCommandCreated; - IDataReader GetReader(IQuery query); - int Execute(IQuery query); - T GetScalar(IQuery query); - DataTable GetDataTable(IQuery query); - IEnumerable GetRows(IQuery query); - DataRow GetRow(IQuery query); + IDataReader GetReader(IQuery query, CancellationToken cancellationToken = default); + int Execute(IQuery query, CancellationToken cancellationToken = default); + T GetScalar(IQuery query, CancellationToken cancellationToken = default); + DataTable GetDataTable(IQuery query, CancellationToken cancellationToken = default); + IEnumerable GetRows(IQuery query, CancellationToken cancellationToken = default); + DataRow GetRow(IQuery query, CancellationToken cancellationToken = default); Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default); Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default); diff --git a/Shuttle.Core.Data/IQueryMapper.cs b/Shuttle.Core.Data/IQueryMapper.cs index 83eeceb..777145c 100644 --- a/Shuttle.Core.Data/IQueryMapper.cs +++ b/Shuttle.Core.Data/IQueryMapper.cs @@ -6,14 +6,14 @@ namespace Shuttle.Core.Data { public interface IQueryMapper { - MappedRow MapRow(IQuery query) where T : new(); - IEnumerable> MapRows(IQuery query) where T : new(); - T MapObject(IQuery query) where T : new(); - IEnumerable MapObjects(IQuery query) where T : new(); - T MapValue(IQuery query); - IEnumerable MapValues(IQuery query); - dynamic MapItem(IQuery query); - IEnumerable MapItems(IQuery query); + MappedRow MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new(); + IEnumerable> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new(); + T MapObject(IQuery query, CancellationToken cancellationToken = default) where T : new(); + IEnumerable MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new(); + T MapValue(IQuery query, CancellationToken cancellationToken = default); + IEnumerable MapValues(IQuery query, CancellationToken cancellationToken = default); + dynamic MapItem(IQuery query, CancellationToken cancellationToken = default); + IEnumerable MapItems(IQuery query, CancellationToken cancellationToken = default); Task> MapRowAsync(IQuery query, CancellationToken cancellationToken = default) where T : new(); Task>> MapRowsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new(); diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index f50b5ad..59ce17d 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -25,11 +25,11 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); } - public MappedRow MapRow(IQuery query) where T : new() + public MappedRow MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRow(_databaseGateway.GetRow(query)); + return _dataRowMapper.MapRow(_databaseGateway.GetRow(query, cancellationToken)); } public async Task> MapRowAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() @@ -39,11 +39,11 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return _dataRowMapper.MapRow(await _databaseGateway.GetRowAsync(query, cancellationToken)); } - public IEnumerable> MapRows(IQuery query) where T : new() + public IEnumerable> MapRows(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - return _dataRowMapper.MapRows(_databaseGateway.GetRows(query)); + return _dataRowMapper.MapRows(_databaseGateway.GetRows(query, cancellationToken)); } public async Task>> MapRowsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() @@ -53,11 +53,11 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return _dataRowMapper.MapRows(await _databaseGateway.GetRowsAsync(query, cancellationToken)); } - public T MapObject(IQuery query) where T : new() + public T MapObject(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query)) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { var columns = GetColumns(reader); @@ -86,13 +86,13 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return default; } - public IEnumerable MapObjects(IQuery query) where T : new() + public IEnumerable MapObjects(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); var result = new List(); - using (var reader = _databaseGateway.GetReader(query)) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { var columns = GetColumns(reader); @@ -123,11 +123,11 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe return result; } - public T MapValue(IQuery query) + public T MapValue(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query)) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { while (reader.Read()) { @@ -152,13 +152,13 @@ public async Task MapValueAsync(IQuery query, CancellationToken cancellati return default; } - public IEnumerable MapValues(IQuery query) + public IEnumerable MapValues(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - using (var reader = _databaseGateway.GetReader(query)) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { while (reader.Read()) { @@ -207,11 +207,11 @@ private static dynamic DynamicMap(IDataRecord record, Dictionary co return result; } - public dynamic MapItem(IQuery query) + public dynamic MapItem(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query)) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { var columns = GetColumns(reader); @@ -239,13 +239,13 @@ public async Task MapItemAsync(IQuery query, CancellationToken cancella return default; } - public IEnumerable MapItems(IQuery query) + public IEnumerable MapItems(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); var result = new List(); - using (var reader = _databaseGateway.GetReader(query)) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { var columns = GetColumns(reader); From 87093010a14a51f3cdf2e22ff008e0e1fea0e5d2 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 6 Jan 2024 11:30:32 +0200 Subject: [PATCH 14/37] - checkpoint --- .../DatabaseGatewayFixture.cs | 9 ++ .../Mapping/DataRowMapperFixture.cs | 18 ++-- .../Mapping/MappingFixture.cs | 12 +-- .../Mapping/QueryMapperFixture.cs | 16 ++-- Shuttle.Core.Data.Tests/RawQueryFixture.cs | 4 +- .../Extensions/DataRepositoryExtensions.cs | 60 ++++++------ .../Extensions/DatabaseGatewayExtensions.cs | 72 +++++++------- .../Extensions/QueryExtensions.cs | 30 ++++++ .../Extensions/QueryMapperExtensions.cs | 96 +++++++++---------- Shuttle.Core.Data/IQuery.cs | 1 + Shuttle.Core.Data/IQueryParameter.cs | 7 -- Shuttle.Core.Data/ProcedureQuery.cs | 9 +- Shuttle.Core.Data/RawQuery.cs | 31 +----- 13 files changed, 179 insertions(+), 186 deletions(-) create mode 100644 Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs create mode 100644 Shuttle.Core.Data/Extensions/QueryExtensions.cs delete mode 100644 Shuttle.Core.Data/IQueryParameter.cs diff --git a/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs b/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs new file mode 100644 index 0000000..3242a30 --- /dev/null +++ b/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace Shuttle.Core.Data.Tests; + +public class DatabaseGatewayFixture : MappingFixture +{ + [Test] + public +} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 6ea78f3..762b495 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -14,7 +14,7 @@ public async Task Should_be_able_to_perform_basic_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = RawQuery.Create(@" + var rowQuery = new RawQuery(@" select top 1 Id, Name, @@ -23,7 +23,7 @@ select top 1 BasicMapping "); - var rowsQuery = RawQuery.Create(@" + var rowsQuery = new RawQuery(@" select Id, Name, @@ -54,7 +54,7 @@ public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_ar var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = RawQuery.Create(@" + var rowQuery = new RawQuery(@" select top 1 Id, Name as NotMapped, @@ -63,7 +63,7 @@ Age as TheAge BasicMapping "); - var rowsQuery = RawQuery.Create(@" + var rowsQuery = new RawQuery(@" select Id, Name, @@ -94,14 +94,14 @@ public async Task Should_be_able_to_perform_value_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = RawQuery.Create(@" + var rowQuery = new RawQuery(@" select top 1 Id from BasicMapping "); - var rowsQuery = RawQuery.Create(@" + var rowsQuery = new RawQuery(@" select Id from @@ -137,8 +137,8 @@ public async Task Should_be_able_to_perform_dynamic_mapping() Id = @Id "; - var rowQuery = RawQuery.Create(rowSql).AddParameterValue(Columns.Id, id); - var rowsQuery = RawQuery.Create(@" + var rowQuery = new RawQuery(rowSql).AddParameterValue(Columns.Id, id); + var rowsQuery = new RawQuery(@" select Id, Name, @@ -157,7 +157,7 @@ public async Task Should_be_able_to_perform_dynamic_mapping() Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(RawQuery.Create(rowSql, new { Id = id }))); + item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(new RawQuery(rowSql), (object)(new { Id = id }))); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index c829b92..be2c73d 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -10,7 +10,7 @@ public async Task SetUp() { using (GetDatabaseContext()) { - await GetDatabaseGateway().ExecuteAsync(RawQuery.Create(@" + await GetDatabaseGateway().ExecuteAsync(new RawQuery(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( @@ -42,15 +42,7 @@ insert into BasicMapping 'E09D96E1-5401-4CB6-A871-092DA1C7248D', 'Name-1', 25 -) - -insert into BasicMapping -( - Id, - Name, - Age -) -values +), ( 'B5E0088E-4873-4244-9B91-1059E0383C3E', 'Name-2', diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index 96d995e..c4652cf 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -14,7 +14,7 @@ public async Task Should_be_able_to_perform_basic_mapping() { var mapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = new RawQuery(@" select top 1 Id, Name, @@ -23,7 +23,7 @@ select top 1 BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = new RawQuery(@" select Id, Name, @@ -53,7 +53,7 @@ public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_ar { var mapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = new RawQuery(@" select top 1 Id, Name as NotMapped, @@ -62,7 +62,7 @@ Age as TheAge BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = new RawQuery(@" select Id, Name, @@ -92,14 +92,14 @@ public void Should_be_able_to_perform_value_mapping() { var mapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = new RawQuery(@" select top 1 Id from BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = new RawQuery(@" select Id from @@ -122,7 +122,7 @@ public void Should_be_able_to_perform_dynamic_mapping() var databaseGateway = GetDatabaseGateway(); var queryMapper = GetQueryMapper(); - var queryRow = RawQuery.Create(@" + var queryRow = new RawQuery(@" select top 1 Id, Name, @@ -131,7 +131,7 @@ select top 1 BasicMapping "); - var queryRows = RawQuery.Create(@" + var queryRows = new RawQuery(@" select Id, Name, diff --git a/Shuttle.Core.Data.Tests/RawQueryFixture.cs b/Shuttle.Core.Data.Tests/RawQueryFixture.cs index ddf0f71..983d78d 100644 --- a/Shuttle.Core.Data.Tests/RawQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/RawQueryFixture.cs @@ -39,8 +39,8 @@ public void Should_be_able_to_create_a_query() const string sql = "select 1"; var query1 = new RawQuery(sql); - var query2 = RawQuery.Create(sql); - var query3 = RawQuery.Create("select {0}", 1); + var query2 = new RawQuery(sql); + var query3 = new RawQuery("select {0}", 1); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs index dc7797f..fc2c15b 100644 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs @@ -11,140 +11,140 @@ public static bool Contains(this IDataRepository dataRepository, string sq { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.Contains(RawQuery.Create(sql), cancellationToken); + return dataRepository.Contains(new RawQuery(sql), cancellationToken); } public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.ContainsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await dataRepository.ContainsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static bool Contains(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static bool Contains(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); + return dataRepository.Contains(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.ContainsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await dataRepository.ContainsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); } public static T FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchItem(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchItem(new RawQuery(sql), cancellationToken); } public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchItemAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static T FetchItem(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static T FetchItem(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.Contains(RawQuery.Create(sql, parameters), cancellationToken); + return dataRepository.FetchItem(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchItemAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchItems(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchItems(new RawQuery(sql), cancellationToken); } public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchItemsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchItems(RawQuery.Create(sql, parameters), cancellationToken); + return dataRepository.FetchItems(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchItemsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchItemsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchMappedRow(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchMappedRow(new RawQuery(sql), cancellationToken); } public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchMappedRowAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchMappedRow(RawQuery.Create(sql, parameters), cancellationToken); + return dataRepository.FetchMappedRow(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchMappedRowAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchMappedRows(RawQuery.Create(sql), cancellationToken); + return dataRepository.FetchMappedRows(new RawQuery(sql), cancellationToken); } public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchMappedRowsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return dataRepository.FetchMappedRows(RawQuery.Create(sql, parameters), cancellationToken); + return dataRepository.FetchMappedRows(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : class + public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class { Guard.AgainstNull(dataRepository, nameof(dataRepository)); - return await dataRepository.FetchMappedRowsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await dataRepository.FetchMappedRowsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs index 9c39920..5edfbb7 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs @@ -12,168 +12,168 @@ public static int Execute(this IDatabaseGateway databaseGateway, string sql, Can { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.Execute(RawQuery.Create(sql), cancellationToken); + return databaseGateway.Execute(new RawQuery(sql), cancellationToken); } public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.ExecuteAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await databaseGateway.ExecuteAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static int Execute(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static int Execute(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.Execute(RawQuery.Create(sql, parameters), cancellationToken); + return databaseGateway.Execute(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.ExecuteAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await databaseGateway.ExecuteAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetDataTable(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetDataTable(new RawQuery(sql), cancellationToken); } public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetDataTableAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetDataTable(RawQuery.Create(sql, parameters), cancellationToken); + return databaseGateway.GetDataTable(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetDataTableAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetDataTableAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IDataReader GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetReader(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetReader(new RawQuery(sql), cancellationToken); } public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetReaderAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await databaseGateway.GetReaderAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetReaderAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetReaderAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetRow(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetRow(new RawQuery(sql), cancellationToken); } public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetRowAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetRow(RawQuery.Create(sql, parameters), cancellationToken); + return databaseGateway.GetRow(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetRowAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetRows(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetRows(new RawQuery(sql), cancellationToken); } public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetRowsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), cancellationToken); + return databaseGateway.GetRows(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetRowsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetRowsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetScalar(RawQuery.Create(sql), cancellationToken); + return databaseGateway.GetScalar(new RawQuery(sql), cancellationToken); } public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalarAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetScalarAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return databaseGateway.GetScalar(RawQuery.Create(sql, parameters), cancellationToken); + return databaseGateway.GetScalar(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - return await databaseGateway.GetScalarAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await databaseGateway.GetScalarAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryExtensions.cs b/Shuttle.Core.Data/Extensions/QueryExtensions.cs new file mode 100644 index 0000000..e455de3 --- /dev/null +++ b/Shuttle.Core.Data/Extensions/QueryExtensions.cs @@ -0,0 +1,30 @@ +using System; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public static class QueryExtensions + { + public static IQuery AddParameters(this IQuery query, object parameters) + { + Guard.AgainstNull(query, nameof(query)); + + if (parameters != null) + { + foreach (var pi in (parameters).GetType().GetProperties()) + { + try + { + query.AddParameterValue(new Column(pi.Name, pi.PropertyType, Column.GetDbType(pi.PropertyType)), pi.GetValue(parameters)); + } + catch + { + throw new InvalidOperationException(string.Format(Resources.DynamicGetValueException, pi.Name)); + } + } + } + + return query; + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs index 0c6d142..67ab04f 100644 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs @@ -11,224 +11,224 @@ public static dynamic MapItem(this IQueryMapper queryMapper, string sql, Cancell { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapItem(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapItem(new RawQuery(sql), cancellationToken); } public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapItemAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static async Task MapItem(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task MapItem(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapItemAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapItemAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapItems(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapItems(new RawQuery(sql), cancellationToken); } public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapItemsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapItems(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapItems(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapItemsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapItemsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static T MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapObject(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapObject(new RawQuery(sql), cancellationToken); } public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapObjectAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static T MapObject(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static T MapObject(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapObject(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapObject(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapObjectAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapObjects(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapObjects(new RawQuery(sql), cancellationToken); } public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectsAsync(RawQuery.Create(sql), cancellationToken); + return await queryMapper.MapObjectsAsync(new RawQuery(sql), cancellationToken); } - public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapObjects(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapObjects(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapObjectsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapObjectsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapRow(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapRow(new RawQuery(sql), cancellationToken); } public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapRowAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapRow(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapRow(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowAsync(RawQuery.Create(sql, parameters), cancellationToken); + return await queryMapper.MapRowAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); } public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapRows(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapRows(new RawQuery(sql), cancellationToken); } public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowsAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapRowsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapRows(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapRows(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) where T : new() + public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapRowsAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapRowsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static T MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapValue(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapValue(new RawQuery(sql), cancellationToken); } public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValueAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapValueAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static T MapValue(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static T MapValue(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapValue(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapValue(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValueAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapValueAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapValues(RawQuery.Create(sql), cancellationToken); + return queryMapper.MapValues(new RawQuery(sql), cancellationToken); } public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValuesAsync(RawQuery.Create(sql), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapValuesAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); } - public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return queryMapper.MapValues(RawQuery.Create(sql, parameters), cancellationToken); + return queryMapper.MapValues(new RawQuery(sql).AddParameters(parameters), cancellationToken); } - public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, dynamic parameters, CancellationToken cancellationToken = default) + public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) { Guard.AgainstNull(queryMapper, nameof(queryMapper)); - return await queryMapper.MapValuesAsync(RawQuery.Create(sql, parameters), cancellationToken).ConfigureAwait(false); + return await queryMapper.MapValuesAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQuery.cs b/Shuttle.Core.Data/IQuery.cs index bd7773d..36ef840 100644 --- a/Shuttle.Core.Data/IQuery.cs +++ b/Shuttle.Core.Data/IQuery.cs @@ -5,5 +5,6 @@ namespace Shuttle.Core.Data public interface IQuery { void Prepare(IDbCommand command); + IQuery AddParameterValue(IColumn column, object value); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IQueryParameter.cs b/Shuttle.Core.Data/IQueryParameter.cs deleted file mode 100644 index 84ebcde..0000000 --- a/Shuttle.Core.Data/IQueryParameter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Shuttle.Core.Data -{ - public interface IQueryParameter : IQuery - { - IQueryParameter AddParameterValue(IColumn column, object value); - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/ProcedureQuery.cs b/Shuttle.Core.Data/ProcedureQuery.cs index e416c17..f84c77e 100644 --- a/Shuttle.Core.Data/ProcedureQuery.cs +++ b/Shuttle.Core.Data/ProcedureQuery.cs @@ -4,7 +4,7 @@ namespace Shuttle.Core.Data { - public class ProcedureQuery : IQueryParameter + public class ProcedureQuery : IQuery { private readonly Dictionary _parameterValues; private readonly string _procedure; @@ -30,7 +30,7 @@ public void Prepare(IDbCommand command) } } - public IQueryParameter AddParameterValue(IColumn column, object value) + public IQuery AddParameterValue(IColumn column, object value) { Guard.AgainstNull(column, nameof(column)); @@ -38,10 +38,5 @@ public IQueryParameter AddParameterValue(IColumn column, object value) return this; } - - public static IQueryParameter Create(string procedure) - { - return new ProcedureQuery(procedure); - } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/RawQuery.cs b/Shuttle.Core.Data/RawQuery.cs index 49b1c02..069c78f 100644 --- a/Shuttle.Core.Data/RawQuery.cs +++ b/Shuttle.Core.Data/RawQuery.cs @@ -5,7 +5,7 @@ namespace Shuttle.Core.Data { - public class RawQuery : IQueryParameter + public class RawQuery : IQuery { private readonly Dictionary _parameterValues; private readonly string _sql; @@ -29,7 +29,7 @@ public void Prepare(IDbCommand command) } } - public IQueryParameter AddParameterValue(IColumn column, object value) + public IQuery AddParameterValue(IColumn column, object value) { Guard.AgainstNull(column, nameof(column)); @@ -37,32 +37,5 @@ public IQueryParameter AddParameterValue(IColumn column, object value) return this; } - - public static IQueryParameter Create(string sql, params object[] args) - { - return new RawQuery(args != null && args.Length > 0 ? string.Format(sql, args) : sql); - } - - public static IQueryParameter Create(string sql, dynamic parameters) - { - var result = new RawQuery(sql); - - if (parameters != null) - { - foreach (var pi in ((object)parameters).GetType().GetProperties()) - { - try - { - result.AddParameterValue(new Column(pi.Name, pi.PropertyType, Column.GetDbType(pi.PropertyType)), pi.GetValue(parameters)); - } - catch - { - throw new InvalidOperationException(string.Format(Resources.DynamicGetValueException, pi.Name)); - } - } - } - - return result; - } } } \ No newline at end of file From d65e30de27504f59f93c28a7fa5cf6aeab7f7696 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 6 Jan 2024 11:37:56 +0200 Subject: [PATCH 15/37] - renamed `RawQuery` to `Query` - removed `Procedureuery` - removed extension methods that built a `RawQuery` from `string sql` --- .../DatabaseGatewayFixture.cs | 9 - .../Mapping/DataRowMapperFixture.cs | 18 +- .../Mapping/MappingFixture.cs | 2 +- .../Mapping/QueryMapperFixture.cs | 16 +- .../ProcedureQueryFixture.cs | 5 +- Shuttle.Core.Data.Tests/RawQueryFixture.cs | 8 +- .../Extensions/DataRepositoryExtensions.cs | 150 ----------- .../Extensions/DatabaseGatewayExtensions.cs | 179 -------------- .../Extensions/QueryExtensions.cs | 2 +- .../Extensions/QueryMapperExtensions.cs | 234 ------------------ Shuttle.Core.Data/IQuery.cs | 2 +- .../{ProcedureQuery.cs => Query.cs} | 18 +- Shuttle.Core.Data/RawQuery.cs | 41 --- 13 files changed, 33 insertions(+), 651 deletions(-) delete mode 100644 Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs delete mode 100644 Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs delete mode 100644 Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs delete mode 100644 Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs rename Shuttle.Core.Data/{ProcedureQuery.cs => Query.cs} (59%) delete mode 100644 Shuttle.Core.Data/RawQuery.cs diff --git a/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs b/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs deleted file mode 100644 index 3242a30..0000000 --- a/Shuttle.Core.Data.Tests/DatabaseGatewayFixture.cs +++ /dev/null @@ -1,9 +0,0 @@ -using NUnit.Framework; - -namespace Shuttle.Core.Data.Tests; - -public class DatabaseGatewayFixture : MappingFixture -{ - [Test] - public -} \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 762b495..594667b 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -14,7 +14,7 @@ public async Task Should_be_able_to_perform_basic_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = new RawQuery(@" + var rowQuery = new Query(@" select top 1 Id, Name, @@ -23,7 +23,7 @@ select top 1 BasicMapping "); - var rowsQuery = new RawQuery(@" + var rowsQuery = new Query(@" select Id, Name, @@ -54,7 +54,7 @@ public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_ar var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = new RawQuery(@" + var rowQuery = new Query(@" select top 1 Id, Name as NotMapped, @@ -63,7 +63,7 @@ Age as TheAge BasicMapping "); - var rowsQuery = new RawQuery(@" + var rowsQuery = new Query(@" select Id, Name, @@ -94,14 +94,14 @@ public async Task Should_be_able_to_perform_value_mapping() var databaseGateway = GetDatabaseGateway(); var dataRowMapper = GetDataRowMapper(); - var rowQuery = new RawQuery(@" + var rowQuery = new Query(@" select top 1 Id from BasicMapping "); - var rowsQuery = new RawQuery(@" + var rowsQuery = new Query(@" select Id from @@ -137,8 +137,8 @@ public async Task Should_be_able_to_perform_dynamic_mapping() Id = @Id "; - var rowQuery = new RawQuery(rowSql).AddParameterValue(Columns.Id, id); - var rowsQuery = new RawQuery(@" + var rowQuery = new Query(rowSql).AddParameter(Columns.Id, id); + var rowsQuery = new Query(@" select Id, Name, @@ -157,7 +157,7 @@ public async Task Should_be_able_to_perform_dynamic_mapping() Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(new RawQuery(rowSql), (object)(new { Id = id }))); + item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(new Query(rowSql).AddParameters(new { Id = id }))); Assert.IsNotNull(item); Assert.That(item.Id, Is.EqualTo(id)); diff --git a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs index be2c73d..4a625dc 100644 --- a/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/MappingFixture.cs @@ -10,7 +10,7 @@ public async Task SetUp() { using (GetDatabaseContext()) { - await GetDatabaseGateway().ExecuteAsync(new RawQuery(@" + await GetDatabaseGateway().ExecuteAsync(new Query(@" IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BasicMapping]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[BasicMapping]( diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index c4652cf..5e743f3 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -14,7 +14,7 @@ public async Task Should_be_able_to_perform_basic_mapping() { var mapper = GetQueryMapper(); - var queryRow = new RawQuery(@" + var queryRow = new Query(@" select top 1 Id, Name, @@ -23,7 +23,7 @@ select top 1 BasicMapping "); - var queryRows = new RawQuery(@" + var queryRows = new Query(@" select Id, Name, @@ -53,7 +53,7 @@ public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_ar { var mapper = GetQueryMapper(); - var queryRow = new RawQuery(@" + var queryRow = new Query(@" select top 1 Id, Name as NotMapped, @@ -62,7 +62,7 @@ Age as TheAge BasicMapping "); - var queryRows = new RawQuery(@" + var queryRows = new Query(@" select Id, Name, @@ -92,14 +92,14 @@ public void Should_be_able_to_perform_value_mapping() { var mapper = GetQueryMapper(); - var queryRow = new RawQuery(@" + var queryRow = new Query(@" select top 1 Id from BasicMapping "); - var queryRows = new RawQuery(@" + var queryRows = new Query(@" select Id from @@ -122,7 +122,7 @@ public void Should_be_able_to_perform_dynamic_mapping() var databaseGateway = GetDatabaseGateway(); var queryMapper = GetQueryMapper(); - var queryRow = new RawQuery(@" + var queryRow = new Query(@" select top 1 Id, Name, @@ -131,7 +131,7 @@ select top 1 BasicMapping "); - var queryRows = new RawQuery(@" + var queryRows = new Query(@" select Id, Name, diff --git a/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs b/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs index 5acb7f7..d51eeb1 100644 --- a/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/ProcedureQueryFixture.cs @@ -14,8 +14,7 @@ public void Should_be_able_to_create_a_query() { const string sql = "uspDoSomething"; - var query1 = new ProcedureQuery(sql); - var query2 = ProcedureQuery.Create(sql); + var query1 = new Query(sql, CommandType.StoredProcedure); } [Test] @@ -25,7 +24,7 @@ public void Should_be_able_prepare_a_query() var guid = Guid.NewGuid(); var mc = new Column("Id", DbType.Guid); - var query = new ProcedureQuery(sql).AddParameterValue(mc, guid); + var query = new Query(sql, CommandType.StoredProcedure).AddParameter(mc, guid); var dataParameterCollection = new Mock(); var command = new Mock(); diff --git a/Shuttle.Core.Data.Tests/RawQueryFixture.cs b/Shuttle.Core.Data.Tests/RawQueryFixture.cs index 983d78d..d6a379c 100644 --- a/Shuttle.Core.Data.Tests/RawQueryFixture.cs +++ b/Shuttle.Core.Data.Tests/RawQueryFixture.cs @@ -16,7 +16,7 @@ public void Should_be_able_prepare_a_query() var guid = Guid.NewGuid(); var mc = new Column("Id", DbType.Guid); - var query = new RawQuery(sql).AddParameterValue(mc, guid); + var query = new Query(sql).AddParameter(mc, guid); var dataParameterCollection = new Mock(); var command = new Mock(); @@ -36,11 +36,7 @@ public void Should_be_able_prepare_a_query() [Test] public void Should_be_able_to_create_a_query() { - const string sql = "select 1"; - - var query1 = new RawQuery(sql); - var query2 = new RawQuery(sql); - var query3 = new RawQuery("select {0}", 1); + Assert.That(() => new Query("select 1"), Throws.Nothing); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs b/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs deleted file mode 100644 index fc2c15b..0000000 --- a/Shuttle.Core.Data/Extensions/DataRepositoryExtensions.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public static class DataRepositoryExtensions - { - public static bool Contains(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.Contains(new RawQuery(sql), cancellationToken); - } - - public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.ContainsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static bool Contains(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.Contains(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task ContainsAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.ContainsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static T FetchItem(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchItem(new RawQuery(sql), cancellationToken); - } - - public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchItemAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static T FetchItem(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchItem(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task FetchItemAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchItemAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchItems(new RawQuery(sql), cancellationToken); - } - - public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchItemsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable FetchItems(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchItems(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> FetchItemsAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchItemsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchMappedRow(new RawQuery(sql), cancellationToken); - } - - public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchMappedRowAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static MappedRow FetchMappedRow(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchMappedRow(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> FetchMappedRowAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchMappedRowAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchMappedRows(new RawQuery(sql), cancellationToken); - } - - public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchMappedRowsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable> FetchMappedRows(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return dataRepository.FetchMappedRows(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task>> FetchMappedRowsAsync(this IDataRepository dataRepository, string sql, object parameters, CancellationToken cancellationToken = default) where T : class - { - Guard.AgainstNull(dataRepository, nameof(dataRepository)); - - return await dataRepository.FetchMappedRowsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs deleted file mode 100644 index 5edfbb7..0000000 --- a/Shuttle.Core.Data/Extensions/DatabaseGatewayExtensions.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System.Collections.Generic; -using System.Data; -using System.Threading; -using System.Threading.Tasks; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public static class DatabaseGatewayExtensions - { - public static int Execute(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.Execute(new RawQuery(sql), cancellationToken); - } - - public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.ExecuteAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static int Execute(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.Execute(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task ExecuteAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.ExecuteAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetDataTable(new RawQuery(sql), cancellationToken); - } - - public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetDataTableAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static DataTable GetDataTable(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetDataTable(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task GetDataTableAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetDataTableAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IDataReader GetReader(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetReader(new RawQuery(sql), cancellationToken); - } - - public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetReaderAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static async Task GetReader(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetReaderAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task GetReaderAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetReaderAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetRow(new RawQuery(sql), cancellationToken); - } - - public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetRowAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static DataRow GetRow(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetRow(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task GetRowAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetRowAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetRows(new RawQuery(sql), cancellationToken); - } - - public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetRowsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable GetRows(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetRows(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> GetRowsAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetRowsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetScalar(new RawQuery(sql), cancellationToken); - } - - public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetScalarAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static T GetScalar(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return databaseGateway.GetScalar(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task GetScalarAsync(this IDatabaseGateway databaseGateway, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); - - return await databaseGateway.GetScalarAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/QueryExtensions.cs b/Shuttle.Core.Data/Extensions/QueryExtensions.cs index e455de3..6dd49a1 100644 --- a/Shuttle.Core.Data/Extensions/QueryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/QueryExtensions.cs @@ -15,7 +15,7 @@ public static IQuery AddParameters(this IQuery query, object parameters) { try { - query.AddParameterValue(new Column(pi.Name, pi.PropertyType, Column.GetDbType(pi.PropertyType)), pi.GetValue(parameters)); + query.AddParameter(new Column(pi.Name, pi.PropertyType, Column.GetDbType(pi.PropertyType)), pi.GetValue(parameters)); } catch { diff --git a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs b/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs deleted file mode 100644 index 67ab04f..0000000 --- a/Shuttle.Core.Data/Extensions/QueryMapperExtensions.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public static class QueryMapperExtensions - { - public static dynamic MapItem(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapItem(new RawQuery(sql), cancellationToken); - } - - public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapItemAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static async Task MapItem(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapItemAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task MapItemAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapItemAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapItems(new RawQuery(sql), cancellationToken); - } - - public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapItemsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable MapItems(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapItems(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> MapItemsAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapItemsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static T MapObject(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapObject(new RawQuery(sql), cancellationToken); - } - - public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapObjectAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static T MapObject(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapObject(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task MapObjectAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapObjectAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapObjects(new RawQuery(sql), cancellationToken); - } - - public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapObjectsAsync(new RawQuery(sql), cancellationToken); - } - - public static IEnumerable MapObjects(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapObjects(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> MapObjectsAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapObjectsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapRow(new RawQuery(sql), cancellationToken); - } - - public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapRowAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static MappedRow MapRow(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapRow(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> MapRowAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapRowAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapRows(new RawQuery(sql), cancellationToken); - } - - public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapRowsAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable> MapRows(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapRows(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task>> MapRowsAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) where T : new() - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapRowsAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static T MapValue(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapValue(new RawQuery(sql), cancellationToken); - } - - public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapValueAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static T MapValue(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapValue(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task MapValueAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapValueAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapValues(new RawQuery(sql), cancellationToken); - } - - public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapValuesAsync(new RawQuery(sql), cancellationToken).ConfigureAwait(false); - } - - public static IEnumerable MapValues(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return queryMapper.MapValues(new RawQuery(sql).AddParameters(parameters), cancellationToken); - } - - public static async Task> MapValuesAsync(this IQueryMapper queryMapper, string sql, object parameters, CancellationToken cancellationToken = default) - { - Guard.AgainstNull(queryMapper, nameof(queryMapper)); - - return await queryMapper.MapValuesAsync(new RawQuery(sql).AddParameters(parameters), cancellationToken).ConfigureAwait(false); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/IQuery.cs b/Shuttle.Core.Data/IQuery.cs index 36ef840..784936e 100644 --- a/Shuttle.Core.Data/IQuery.cs +++ b/Shuttle.Core.Data/IQuery.cs @@ -5,6 +5,6 @@ namespace Shuttle.Core.Data public interface IQuery { void Prepare(IDbCommand command); - IQuery AddParameterValue(IColumn column, object value); + IQuery AddParameter(IColumn column, object value); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/ProcedureQuery.cs b/Shuttle.Core.Data/Query.cs similarity index 59% rename from Shuttle.Core.Data/ProcedureQuery.cs rename to Shuttle.Core.Data/Query.cs index f84c77e..bf35245 100644 --- a/Shuttle.Core.Data/ProcedureQuery.cs +++ b/Shuttle.Core.Data/Query.cs @@ -4,16 +4,16 @@ namespace Shuttle.Core.Data { - public class ProcedureQuery : IQuery + public class Query : IQuery { + private readonly CommandType _commandType; + private readonly string _commandText; private readonly Dictionary _parameterValues; - private readonly string _procedure; - public ProcedureQuery(string procedure) + public Query(string commandText, CommandType commandType = CommandType.Text) { - Guard.AgainstNullOrEmptyString(procedure, nameof(procedure)); - - _procedure = procedure; + _commandText = Guard.AgainstNullOrEmptyString(commandText, nameof(commandText)); + _commandType = commandType; _parameterValues = new Dictionary(); } @@ -21,8 +21,8 @@ public void Prepare(IDbCommand command) { Guard.AgainstNull(command, nameof(command)); - command.CommandText = _procedure; - command.CommandType = CommandType.StoredProcedure; + command.CommandText = _commandText; + command.CommandType = _commandType; foreach (var pair in _parameterValues) { @@ -30,7 +30,7 @@ public void Prepare(IDbCommand command) } } - public IQuery AddParameterValue(IColumn column, object value) + public IQuery AddParameter(IColumn column, object value) { Guard.AgainstNull(column, nameof(column)); diff --git a/Shuttle.Core.Data/RawQuery.cs b/Shuttle.Core.Data/RawQuery.cs deleted file mode 100644 index 069c78f..0000000 --- a/Shuttle.Core.Data/RawQuery.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public class RawQuery : IQuery - { - private readonly Dictionary _parameterValues; - private readonly string _sql; - - public RawQuery(string sql) - { - _sql = Guard.AgainstNullOrEmptyString(sql, nameof(sql)); - _parameterValues = new Dictionary(); - } - - public void Prepare(IDbCommand command) - { - Guard.AgainstNull(command, nameof(command)); - - command.CommandText = _sql; - command.CommandType = CommandType.Text; - - foreach (var pair in _parameterValues) - { - command.Parameters.Add(pair.Key.CreateDataParameter(command, pair.Value)); - } - } - - public IQuery AddParameterValue(IColumn column, object value) - { - Guard.AgainstNull(column, nameof(column)); - - _parameterValues.Add(column, value); - - return this; - } - } -} \ No newline at end of file From a719dd04c24411aa08e2b2655b45c4267e434b7a Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 6 Jan 2024 14:09:18 +0200 Subject: [PATCH 16/37] - sync/async fixture --- .../AssemblerExtensionsFixture.cs | 87 +++-- .../DataRepositoryFixture.cs | 312 ++++++++++------ .../DatabaseContextFixture.cs | 342 +++++++++--------- .../DatabaseContextServiceFixture.cs | 43 +-- .../Mapping/DataRowMapperFixture.cs | 230 ++++++++---- .../Mapping/QueryMapperFixture.cs | 186 ++++++---- Shuttle.Core.Data/.package/package.nuspec | 2 +- Shuttle.Core.Data/Properties/AssemblyInfo.cs | 2 +- 8 files changed, 692 insertions(+), 512 deletions(-) diff --git a/Shuttle.Core.Data.Tests/AssemblerExtensionsFixture.cs b/Shuttle.Core.Data.Tests/AssemblerExtensionsFixture.cs index 1296d2d..5b841e7 100644 --- a/Shuttle.Core.Data.Tests/AssemblerExtensionsFixture.cs +++ b/Shuttle.Core.Data.Tests/AssemblerExtensionsFixture.cs @@ -5,49 +5,48 @@ using NUnit.Framework; using Shuttle.Core.Data.Tests.Fakes; -namespace Shuttle.Core.Data.Tests +namespace Shuttle.Core.Data.Tests; + +[TestFixture] +public class AssemblerExtensionsFixture { - [TestFixture] - public class AssemblerExtensionsFixture - { - [Test] - public void Should_be_able_to_assemble_item() - { - var now = DateTime.Now; - var mappedData = new MappedData(); - - var orderTable = new DataTable(); - - orderTable.Columns.Add("OrderNumber", typeof(string)); - orderTable.Columns.Add("OrderDate", typeof(DateTime)); - - const string orderNumber = "ON-10"; - - mappedData.Add(new MappedRow(orderTable.Rows.Add(orderNumber, now), new Order(orderNumber, now))); - - var orderLineTable = new DataTable(); - - orderLineTable.Columns.Add("OrderNumber", typeof(string)); - orderLineTable.Columns.Add("ProductId", typeof(string)); - orderLineTable.Columns.Add("Quantity", typeof(int)); - orderLineTable.Columns.Add("UnitCost", typeof(double)); - - mappedData.Add(new List> - { - new MappedRow(orderLineTable.Rows.Add(orderNumber, "SKU-1", 5, 10), new OrderLine("SKU-1", 5, 10)), - new MappedRow(orderLineTable.Rows.Add(orderNumber, "SKU-2", 1, 65), new OrderLine("SKU-2", 1, 65)), - new MappedRow(orderLineTable.Rows.Add(orderNumber, "SKU-3", 10, 10.5), new OrderLine("SKU-3", 10, 10.5)) - }); - - var order = new OrderAssembler().AssembleItem(mappedData); - - Assert.AreEqual(orderNumber, order.OrderNumber); - Assert.AreEqual(now, order.OrderDate); - Assert.AreEqual(3, order.Lines.Count()); - Assert.AreEqual(50, order.Lines.ElementAt(0).TotalCost()); - Assert.AreEqual(65, order.Lines.ElementAt(1).TotalCost()); - Assert.AreEqual(105, order.Lines.ElementAt(2).TotalCost()); - Assert.AreEqual(220, order.Total()); - } - } + [Test] + public void Should_be_able_to_assemble_item() + { + var now = DateTime.Now; + var mappedData = new MappedData(); + + var orderTable = new DataTable(); + + orderTable.Columns.Add("OrderNumber", typeof(string)); + orderTable.Columns.Add("OrderDate", typeof(DateTime)); + + const string orderNumber = "ON-10"; + + mappedData.Add(new MappedRow(orderTable.Rows.Add(orderNumber, now), new Order(orderNumber, now))); + + var orderLineTable = new DataTable(); + + orderLineTable.Columns.Add("OrderNumber", typeof(string)); + orderLineTable.Columns.Add("ProductId", typeof(string)); + orderLineTable.Columns.Add("Quantity", typeof(int)); + orderLineTable.Columns.Add("UnitCost", typeof(double)); + + mappedData.Add(new List> + { + new(orderLineTable.Rows.Add(orderNumber, "SKU-1", 5, 10), new OrderLine("SKU-1", 5, 10)), + new(orderLineTable.Rows.Add(orderNumber, "SKU-2", 1, 65), new OrderLine("SKU-2", 1, 65)), + new(orderLineTable.Rows.Add(orderNumber, "SKU-3", 10, 10.5), new OrderLine("SKU-3", 10, 10.5)) + }); + + var order = new OrderAssembler().AssembleItem(mappedData); + + Assert.AreEqual(orderNumber, order.OrderNumber); + Assert.AreEqual(now, order.OrderDate); + Assert.AreEqual(3, order.Lines.Count()); + Assert.AreEqual(50, order.Lines.ElementAt(0).TotalCost()); + Assert.AreEqual(65, order.Lines.ElementAt(1).TotalCost()); + Assert.AreEqual(105, order.Lines.ElementAt(2).TotalCost()); + Assert.AreEqual(220, order.Total()); + } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index f5c7e1e..9a0c59a 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -6,123 +6,199 @@ using Moq; using NUnit.Framework; -namespace Shuttle.Core.Data.Tests -{ - [TestFixture] - public class DataRepositoryFixture : Fixture - { - [Test] - public async Task Should_be_able_to_fetch_all_items() - { - var gateway = new Mock(); - var mapper = new Mock>(); - var query = new Mock(); - var dataRow = new DataTable().NewRow(); - var anObject = new object(); - - gateway.Setup(m => m.GetRowsAsync(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); - mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); - - var repository = new DataRepository(gateway.Object, mapper.Object); - - var result = (await repository.FetchItemsAsync(query.Object)).ToList(); - - Assert.IsNotNull(result); - Assert.AreEqual(1, result.Count); - Assert.AreSame(anObject, result[0]); - } - - [Test] - public async Task Should_be_able_to_fetch_a_single_item() - { - var gateway = new Mock(); - var mapper = new Mock>(); - var query = new Mock(); - var dataRow = new DataTable().NewRow(); - var anObject = new object(); - - gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); - mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); - - var repository = new DataRepository(gateway.Object, mapper.Object); - - var result = await repository.FetchItemAsync(query.Object); - - Assert.IsNotNull(result); - Assert.AreSame(anObject, result); - } - - [Test] - public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() - { - var gateway = new Mock(); - var query = new Mock(); - - gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync((DataRow) null); - - var repository = new DataRepository(gateway.Object, new Mock>().Object); - - var result = await repository.FetchItemAsync(query.Object); - - Assert.IsNull(result); - } - - [Test] - public async Task Should_be_able_to_call_contains() - { - var gateway = new Mock(); - var query = new Mock(); - - gateway.Setup(m => m.GetScalarAsync(query.Object, CancellationToken.None)).ReturnsAsync(1); - - var repository = new DataRepository(gateway.Object, new Mock>().Object); - - Assert.That(await repository.ContainsAsync(query.Object), Is.True); - } - - [Test] - public async Task Should_be_able_to_fetch_mapped_rows() - { - var gateway = new Mock(); - var mapper = new Mock>(); - var query = new Mock(); - var dataRow = new DataTable().NewRow(); - var anObject = new object(); - var mappedRow = new MappedRow(dataRow, anObject); - - gateway.Setup(m => m.GetRowsAsync(query.Object, CancellationToken.None)).ReturnsAsync(new List {dataRow}); - mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); - - var repository = new DataRepository(gateway.Object, mapper.Object); - - var result = (await repository.FetchMappedRowsAsync(query.Object)).ToList(); - - Assert.IsNotNull(result); - Assert.AreEqual(1, result.Count); - Assert.AreSame(dataRow, result[0].Row); - Assert.AreSame(anObject, result[0].Result); - } - - [Test] - public async Task Should_be_able_to_fetch_a_single_row() - { - var gateway = new Mock(); - var mapper = new Mock>(); - var query = new Mock(); - var dataRow = new DataTable().NewRow(); - var anObject = new object(); - var mappedRow = new MappedRow(dataRow, anObject); - - gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); - mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); +namespace Shuttle.Core.Data.Tests; - var repository = new DataRepository(gateway.Object, mapper.Object); - - var result = await repository.FetchMappedRowAsync(query.Object); - - Assert.IsNotNull(result); - Assert.AreSame(dataRow, result.Row); - Assert.AreSame(anObject, result.Result); - } - } +[TestFixture] +public class DataRepositoryFixture : Fixture +{ + [Test] + public void Should_be_able_to_fetch_all_items() + { + Should_be_able_to_fetch_all_items_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_fetch_all_items_async() + { + await Should_be_able_to_fetch_all_items_async(false); + } + + private async Task Should_be_able_to_fetch_all_items_async(bool sync) + { + var gateway = new Mock(); + var mapper = new Mock>(); + var query = new Mock(); + var dataRow = new DataTable().NewRow(); + var anObject = new object(); + + gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).Returns(new List { dataRow }); + gateway.Setup(m => m.GetRowsAsync(query.Object, CancellationToken.None)).ReturnsAsync(new List { dataRow }); + mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); + + var repository = new DataRepository(gateway.Object, mapper.Object); + + var result = (sync + ? repository.FetchItems(query.Object) + : await repository.FetchItemsAsync(query.Object)).ToList(); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.Count); + Assert.AreSame(anObject, result[0]); + } + + [Test] + public void Should_be_able_to_fetch_a_single_item() + { + Should_be_able_to_fetch_a_single_item_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_fetch_a_single_item_async() + { + await Should_be_able_to_fetch_a_single_item_async(false); + } + + private async Task Should_be_able_to_fetch_a_single_item_async(bool sync) + { + var gateway = new Mock(); + var mapper = new Mock>(); + var query = new Mock(); + var dataRow = new DataTable().NewRow(); + var anObject = new object(); + + gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + mapper.Setup(m => m.Map(It.IsAny())).Returns(new MappedRow(dataRow, anObject)); + + var repository = new DataRepository(gateway.Object, mapper.Object); + + var result = await repository.FetchItemAsync(query.Object); + + Assert.IsNotNull(result); + Assert.AreSame(anObject, result); + } + + [Test] + public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() + { + Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found_async() + { + await Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found_async(false); + } + + private async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found_async(bool sync) + { + var gateway = new Mock(); + var query = new Mock(); + + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).Returns((DataRow)null); + gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync((DataRow)null); + + var repository = new DataRepository(gateway.Object, new Mock>().Object); + + var result = sync + ? repository.FetchItem(query.Object) + : await repository.FetchItemAsync(query.Object); + + Assert.IsNull(result); + } + + [Test] + public void Should_be_able_to_call_contains() + { + Should_be_able_to_call_contains_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_call_contains_async() + { + await Should_be_able_to_call_contains_async(false); + } + + public async Task Should_be_able_to_call_contains_async(bool sync) + { + var gateway = new Mock(); + var query = new Mock(); + + gateway.Setup(m => m.GetScalar(query.Object, CancellationToken.None)).Returns(1); + gateway.Setup(m => m.GetScalarAsync(query.Object, CancellationToken.None)).ReturnsAsync(1); + + var repository = new DataRepository(gateway.Object, new Mock>().Object); + + Assert.That(sync ? repository.Contains(query.Object) : await repository.ContainsAsync(query.Object), Is.True); + } + + [Test] + public void Should_be_able_to_fetch_mapped_rows() + { + Should_be_able_to_fetch_mapped_rows_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_fetch_mapped_rows_async() + { + await Should_be_able_to_fetch_mapped_rows_async(false); + } + + private async Task Should_be_able_to_fetch_mapped_rows_async(bool sync) + { + var gateway = new Mock(); + var mapper = new Mock>(); + var query = new Mock(); + var dataRow = new DataTable().NewRow(); + var anObject = new object(); + var mappedRow = new MappedRow(dataRow, anObject); + + gateway.Setup(m => m.GetRows(query.Object, CancellationToken.None)).Returns(new List { dataRow }); + gateway.Setup(m => m.GetRowsAsync(query.Object, CancellationToken.None)).ReturnsAsync(new List { dataRow }); + mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); + + var repository = new DataRepository(gateway.Object, mapper.Object); + + var result = (sync ? repository.FetchMappedRows(query.Object) : await repository.FetchMappedRowsAsync(query.Object)).ToList(); + + Assert.IsNotNull(result); + Assert.AreEqual(1, result.Count); + Assert.AreSame(dataRow, result[0].Row); + Assert.AreSame(anObject, result[0].Result); + } + + [Test] + public async Task Should_be_able_to_fetch_a_single_row() + { + Should_be_able_to_fetch_a_single_row_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_fetch_a_single_row_async() + { + await Should_be_able_to_fetch_a_single_row_async(false); + } + + private async Task Should_be_able_to_fetch_a_single_row_async(bool sync) + { + var gateway = new Mock(); + var mapper = new Mock>(); + var query = new Mock(); + var dataRow = new DataTable().NewRow(); + var anObject = new object(); + var mappedRow = new MappedRow(dataRow, anObject); + + gateway.Setup(m => m.GetRow(query.Object, CancellationToken.None)).Returns(dataRow); + gateway.Setup(m => m.GetRowAsync(query.Object, CancellationToken.None)).ReturnsAsync(dataRow); + mapper.Setup(m => m.Map(It.IsAny())).Returns(mappedRow); + + var repository = new DataRepository(gateway.Object, mapper.Object); + + var result = sync + ? repository.FetchMappedRow(query.Object) + : await repository.FetchMappedRowAsync(query.Object); + + Assert.IsNotNull(result); + Assert.AreSame(dataRow, result.Row); + Assert.AreSame(anObject, result.Result); + } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index f2b3894..0ec98bb 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -1,173 +1,187 @@ using System; -using System.Data; using System.Data.Common; using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Moq; using NUnit.Framework; -namespace Shuttle.Core.Data.Tests +namespace Shuttle.Core.Data.Tests; + +[TestFixture] +public class DatabaseContextFixture : Fixture { - [TestFixture] - public class DatabaseContextFixture : Fixture - { - [Test] - public void Should_not_be_able_to_create_an_invalid_connection() - { - Assert.Throws(() => - { - using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) - { - } - }); - } - - [Test] - public void Should_not_be_able_to_create_a_non_existent_connection() - { - Assert.Throws(() => - { - using (var databaseContext = new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object, new DatabaseContextService())) - { - databaseContext.Connection.Open(); - } - }); - } - - [Test] - public void Should_be_able_to_create_a_valid_connection() - { - using ( - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - } - } - - [Test] - public void Should_be_able_to_begin_and_commit_a_transaction() - { - using ( - var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - connection.BeginTransaction(); - connection.CommitTransaction(); - } - } - - [Test] - public async Task Should_be_able_to_begin_and_commit_a_transaction_async() - { - using ( - var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - await connection.BeginTransactionAsync(); - await connection.CommitTransactionAsync(); - } - } - - [Test] - public void Should_be_able_to_begin_and_rollback_a_transaction() - { - using ( - var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - connection.BeginTransaction(); - } - } - - [Test] - public async Task Should_be_able_to_begin_and_rollback_a_transaction_async() - { - using ( - var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - await connection.BeginTransactionAsync(); - } - } - - [Test] - public void Should_be_able_to_call_commit_without_a_transaction() - { - using ( - var connection = - new DatabaseContext("System.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - connection.CommitTransaction(); - } - } - - [Test] - public async Task Should_be_able_to_call_commit_without_a_transaction_async() - { - using ( - var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - await connection.CommitTransactionAsync(); - } - } - - [Test] - public void Should_be_able_to_call_dispose_more_than_once() - { - using ( - var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - connection.Dispose(); - connection.Dispose(); - } - } - - [Test] - public void Should_be_able_to_create_a_command() - { - var dbCommandFactory = new Mock(); - var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); - var query = new Mock(); - var dbCommand = new Mock(); - - dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); - - using ( - var connection = new DatabaseContext("System.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) - { - connection.CreateCommand(query.Object); - } - - dbCommandFactory.VerifyAll(); - } - - [Test] - public async Task Should_be_able_to_create_a_command_async() - { - var dbCommandFactory = new Mock(); - var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); - var query = new Mock(); - var dbCommand = new Mock(); - - dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); - - using ( - var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) - { - await connection.CreateCommandAsync(query.Object); - } - - dbCommandFactory.VerifyAll(); - } - } + [Test] + public void Should_not_be_able_to_create_an_invalid_connection() + { + Assert.Throws(() => + { + using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) + { + } + }); + } + + [Test] + public void Should_not_be_able_to_create_a_non_existent_connection() + { + Assert.Throws(() => + { + using (var databaseContext = new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), + new Mock().Object, new DatabaseContextService())) + { + databaseContext.Connection.Open(); + } + }); + } + + [Test] + public void Should_be_able_to_create_a_valid_connection() + { + using ( + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + } + } + + [Test] + public void Should_be_able_to_begin_and_commit_a_transaction() + { + Should_be_able_to_begin_and_commit_a_transaction_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_begin_and_commit_a_transaction_async() + { + await Should_be_able_to_begin_and_commit_a_transaction_async(false); + } + + private async Task Should_be_able_to_begin_and_commit_a_transaction_async(bool sync) + { + using ( + var connection = + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + if (sync) + { + connection.BeginTransaction(); + connection.CommitTransaction(); + } + else + { + await connection.BeginTransactionAsync(); + await connection.CommitTransactionAsync(); + } + } + } + + [Test] + public void Should_be_able_to_begin_and_rollback_a_transaction() + { + Should_be_able_to_begin_and_rollback_a_transaction_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_begin_and_rollback_a_transaction_async() + { + await Should_be_able_to_begin_and_rollback_a_transaction_async(false); + } + + private async Task Should_be_able_to_begin_and_rollback_a_transaction_async(bool sync) + { + using ( + var connection = + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + if (sync) + { + connection.BeginTransaction(); + } + else + { + await connection.BeginTransactionAsync(); + } + } + } + + [Test] + public void Should_be_able_to_call_commit_without_a_transaction() + { + Should_be_able_to_call_commit_without_a_transaction_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_call_commit_without_a_transaction_async() + { + await Should_be_able_to_call_commit_without_a_transaction_async(false); + } + + private async Task Should_be_able_to_call_commit_without_a_transaction_async(bool sync) + { + using ( + var connection = + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + if (sync) + { + connection.CommitTransaction(); + } + else + { + await connection.CommitTransactionAsync(); + } + } + } + + [Test] + public void Should_be_able_to_call_dispose_more_than_once() + { + using ( + var connection = + new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new Mock().Object, new DatabaseContextService())) + { + connection.Dispose(); + connection.Dispose(); + } + } + + [Test] + public void Should_be_able_to_create_a_command() + { + Should_be_able_to_create_a_command_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_create_a_command_async() + { + await Should_be_able_to_create_a_command_async(false); + } + + private async Task Should_be_able_to_create_a_command_async(bool sync) + { + var dbCommandFactory = new Mock(); + var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); + var query = new Mock(); + var dbCommand = new Mock(); + + dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); + + using (var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) + { + if (sync) + { + connection.CreateCommand(query.Object); + } + else + { + await connection.CreateCommandAsync(query.Object); + } + } + + dbCommandFactory.VerifyAll(); + } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index d0eb758..861c942 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -9,34 +9,6 @@ public class DatabaseContextServiceFixture : Fixture { [Test] public void Should_be_able_to_use_different_contexts() - { - var cache = new DatabaseContextService(); - - var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, cache).WithName("mock-1"); - - Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - - var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, cache).WithName("mock-2"); - - Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - - using (cache.Use("mock-1")) - { - Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - } - - Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - - using (cache.Use(context1)) - { - Assert.That(cache.Current.Key, Is.EqualTo(context1.Key)); - } - - Assert.That(cache.Current.Key, Is.EqualTo(context2.Key)); - } - - [Test] - public async Task Should_be_able_to_use_different_contexts_async() { var service = new DatabaseContextService(); @@ -52,7 +24,8 @@ public async Task Should_be_able_to_use_different_contexts_async() { Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - await AssertContextFlow(service, "mock-1"); + Assert.That(service.Current, Is.Not.Null); + Assert.That(service.Current.Name, Is.EqualTo(service.Current.Name)); } Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); @@ -64,16 +37,4 @@ public async Task Should_be_able_to_use_different_contexts_async() Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); } - - private async Task AssertContextFlow(IDatabaseContextService service, string name) - { - Assert.That(service.Current, Is.Not.Null); - Assert.That(service.Current.Name, Is.EqualTo(name)); - - await Task.Run(() => - { - Assert.That(service.Current, Is.Not.Null); - Assert.That(service.Current.Name, Is.EqualTo(name)); - }); - } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs index 594667b..c12868e 100644 --- a/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/DataRowMapperFixture.cs @@ -3,18 +3,29 @@ using System.Threading.Tasks; using NUnit.Framework; -namespace Shuttle.Core.Data.Tests +namespace Shuttle.Core.Data.Tests; + +[TestFixture] +public class DataRowMapperFixture : MappingFixture { - [TestFixture] - public class DataRowMapperFixture : MappingFixture + [Test] + public void Should_be_able_to_perform_basic_mapping() { - [Test] - public async Task Should_be_able_to_perform_basic_mapping() - { - var databaseGateway = GetDatabaseGateway(); - var dataRowMapper = GetDataRowMapper(); + Should_be_able_to_perform_basic_mapping_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_perform_basic_mapping_async() + { + await Should_be_able_to_perform_basic_mapping_async(false); + } - var rowQuery = new Query(@" + private async Task Should_be_able_to_perform_basic_mapping_async(bool sync) + { + var databaseGateway = GetDatabaseGateway(); + var dataRowMapper = GetDataRowMapper(); + + var rowQuery = new Query(@" select top 1 Id, Name, @@ -23,7 +34,7 @@ select top 1 BasicMapping "); - var rowsQuery = new Query(@" + var rowsQuery = new Query(@" select Id, Name, @@ -32,29 +43,49 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) - { - var item = dataRowMapper.MapObject(await databaseGateway.GetRowAsync(rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRowsAsync(rowsQuery)); - - Assert.IsNotNull(item); - Assert.AreEqual(2, items.Count()); + using (GetDatabaseContext()) + { + var item = sync + ? dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery)) + : dataRowMapper.MapObject(await databaseGateway.GetRowAsync(rowQuery)); + + var items = sync + ? dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery)) + : dataRowMapper.MapObjects(await databaseGateway.GetRowsAsync(rowsQuery)); + + Assert.IsNotNull(item); + Assert.AreEqual(2, items.Count()); + + var mappedRow = sync + ? dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery)) + : dataRowMapper.MapRow(await databaseGateway.GetRowAsync(rowQuery)); + var mappedRows = sync + ? dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery)) + : dataRowMapper.MapRows(await databaseGateway.GetRowsAsync(rowsQuery)); + + Assert.IsNotNull(mappedRow); + Assert.AreEqual(2, mappedRows.Count()); + } + } - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRowAsync(rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRowsAsync(rowsQuery)); + [Test] + public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() + { + Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async(true).GetAwaiter().GetResult(); + } - Assert.IsNotNull(mappedRow); - Assert.AreEqual(2, mappedRows.Count()); - } - } + [Test] + public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async() + { + await Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async(false); + } - [Test] - public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() - { - var databaseGateway = GetDatabaseGateway(); - var dataRowMapper = GetDataRowMapper(); + private async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async(bool sync) + { + var databaseGateway = GetDatabaseGateway(); + var dataRowMapper = GetDataRowMapper(); - var rowQuery = new Query(@" + var rowQuery = new Query(@" select top 1 Id, Name as NotMapped, @@ -63,7 +94,7 @@ Age as TheAge BasicMapping "); - var rowsQuery = new Query(@" + var rowsQuery = new Query(@" select Id, Name, @@ -72,61 +103,99 @@ Age as TheAge BasicMapping "); - using (GetDatabaseContext()) - { - var item = dataRowMapper.MapObject(await databaseGateway.GetRowAsync(rowQuery)); - var items = dataRowMapper.MapObjects(await databaseGateway.GetRowsAsync(rowsQuery)); + using (GetDatabaseContext()) + { + var item = sync + ? dataRowMapper.MapObject(databaseGateway.GetRow(rowQuery)) + : dataRowMapper.MapObject(await databaseGateway.GetRowAsync(rowQuery)); + + var items = sync + ? dataRowMapper.MapObjects(databaseGateway.GetRows(rowsQuery)) + : dataRowMapper.MapObjects(await databaseGateway.GetRowsAsync(rowsQuery)); - Assert.IsNotNull(item); - Assert.AreEqual(2, items.Count()); + Assert.IsNotNull(item); + Assert.AreEqual(2, items.Count()); - var mappedRow = dataRowMapper.MapRow(await databaseGateway.GetRowAsync(rowQuery)); - var mappedRows = dataRowMapper.MapRows(await databaseGateway.GetRowsAsync(rowsQuery)); + var mappedRow = sync + ? dataRowMapper.MapRow(databaseGateway.GetRow(rowQuery)) + : dataRowMapper.MapRow(await databaseGateway.GetRowAsync(rowQuery)); - Assert.IsNotNull(mappedRow); - Assert.AreEqual(2, mappedRows.Count()); - } + var mappedRows = sync + ? dataRowMapper.MapRows(databaseGateway.GetRows(rowsQuery)) + : dataRowMapper.MapRows(await databaseGateway.GetRowsAsync(rowsQuery)); + + Assert.IsNotNull(mappedRow); + Assert.AreEqual(2, mappedRows.Count()); } + } - [Test] - public async Task Should_be_able_to_perform_value_mapping() - { - var databaseGateway = GetDatabaseGateway(); - var dataRowMapper = GetDataRowMapper(); + [Test] + public void Should_be_able_to_perform_value_mapping() + { + Should_be_able_to_perform_value_mapping_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_perform_value_mapping_async() + { + await Should_be_able_to_perform_value_mapping_async(false); + } - var rowQuery = new Query(@" + private async Task Should_be_able_to_perform_value_mapping_async(bool sync) + { + var databaseGateway = GetDatabaseGateway(); + var dataRowMapper = GetDataRowMapper(); + + var rowQuery = new Query(@" select top 1 Id from BasicMapping "); - var rowsQuery = new Query(@" + var rowsQuery = new Query(@" select Id from BasicMapping "); - using (GetDatabaseContext()) - { - var value = dataRowMapper.MapValue(await databaseGateway.GetRowAsync(rowQuery)); - var values = dataRowMapper.MapValues(await databaseGateway.GetRowsAsync(rowsQuery)); + using (GetDatabaseContext()) + { + var value = sync + ? dataRowMapper.MapValue(databaseGateway.GetRow(rowQuery)) + : dataRowMapper.MapValue(await databaseGateway.GetRowAsync(rowQuery)); + + var values = sync + ? dataRowMapper.MapValues(databaseGateway.GetRows(rowsQuery)) + : dataRowMapper.MapValues(await databaseGateway.GetRowsAsync(rowsQuery)); - Assert.IsNotNull(value); - Assert.AreEqual(2, values.Count()); - } + Assert.IsNotNull(value); + Assert.AreEqual(2, values.Count()); } + } - [Test] - public async Task Should_be_able_to_perform_dynamic_mapping() - { - var id = new Guid("B5E0088E-4873-4244-9B91-1059E0383C3E"); + [Test] + public void Should_be_able_to_perform_dynamic_mapping() + { + Should_be_able_to_perform_dynamic_mapping_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_perform_dynamic_mapping_async() + { + await Should_be_able_to_perform_dynamic_mapping_async(false); + } - var databaseGateway = GetDatabaseGateway(); - var dataRowMapper = GetDataRowMapper(); - var rowSql = @" + private async Task Should_be_able_to_perform_dynamic_mapping_async(bool sync) + { + var id = new Guid("B5E0088E-4873-4244-9B91-1059E0383C3E"); + + var databaseGateway = GetDatabaseGateway(); + var dataRowMapper = GetDataRowMapper(); + + var rowSql = @" select Id, Name, @@ -136,9 +205,9 @@ public async Task Should_be_able_to_perform_dynamic_mapping() where Id = @Id "; - - var rowQuery = new Query(rowSql).AddParameter(Columns.Id, id); - var rowsQuery = new Query(@" + + var rowQuery = new Query(rowSql).AddParameter(Columns.Id, id); + var rowsQuery = new Query(@" select Id, Name, @@ -147,23 +216,28 @@ public async Task Should_be_able_to_perform_dynamic_mapping() BasicMapping "); - using (GetDatabaseContext()) - { - var item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(rowQuery)); + using (GetDatabaseContext()) + { + var item = sync + ? dataRowMapper.MapItem(databaseGateway.GetRow(rowQuery)) + : dataRowMapper.MapItem(await databaseGateway.GetRowAsync(rowQuery)); + + Assert.IsNotNull(item); - Assert.IsNotNull(item); + var items = sync + ? dataRowMapper.MapItems(databaseGateway.GetRows(rowsQuery)) + : dataRowMapper.MapItems(await databaseGateway.GetRowsAsync(rowsQuery)); - var items = dataRowMapper.MapItems(await databaseGateway.GetRowsAsync(rowsQuery)); - - Assert.AreEqual(2, items.Count()); + Assert.AreEqual(2, items.Count()); - item = dataRowMapper.MapItem(await databaseGateway.GetRowAsync(new Query(rowSql).AddParameters(new { Id = id }))); + item = sync + ? dataRowMapper.MapItem(databaseGateway.GetRow(new Query(rowSql).AddParameters(new { Id = id }))) + : dataRowMapper.MapItem(await databaseGateway.GetRowAsync(new Query(rowSql).AddParameters(new { Id = id }))); - Assert.IsNotNull(item); - Assert.That(item.Id, Is.EqualTo(id)); - Assert.That(item.Name, Is.EqualTo("Name-2")); - Assert.That(item.Age, Is.EqualTo(50)); - } + Assert.IsNotNull(item); + Assert.That(item.Id, Is.EqualTo(id)); + Assert.That(item.Name, Is.EqualTo("Name-2")); + Assert.That(item.Age, Is.EqualTo(50)); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index 5e743f3..ffc4888 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -1,20 +1,30 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; using NUnit.Framework; -namespace Shuttle.Core.Data.Tests +namespace Shuttle.Core.Data.Tests; + +[TestFixture] +public class QueryMapperFixture : MappingFixture { - [TestFixture] - public class QueryMapperFixture : MappingFixture + [Test] + public void Should_be_able_to_perform_basic_mapping() { - [Test] - public async Task Should_be_able_to_perform_basic_mapping() - { - var mapper = GetQueryMapper(); + Should_be_able_to_perform_basic_mapping_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_perform_basic_mapping_async() + { + await Should_be_able_to_perform_basic_mapping_async(false); + } - var queryRow = new Query(@" + private async Task Should_be_able_to_perform_basic_mapping_async(bool sync) + { + var mapper = GetQueryMapper(); + + var queryRow = new Query(@" select top 1 Id, Name, @@ -23,7 +33,7 @@ select top 1 BasicMapping "); - var queryRows = new Query(@" + var queryRows = new Query(@" select Id, Name, @@ -32,28 +42,49 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) - { - var item = await mapper.MapObjectAsync(queryRow); - var items = await mapper.MapObjectsAsync(queryRows); + using (GetDatabaseContext()) + { + var item = sync + ? mapper.MapObject(queryRow) + : await mapper.MapObjectAsync(queryRow); + + var items = sync + ? mapper.MapObjects(queryRows) + : await mapper.MapObjectsAsync(queryRows); + + Assert.IsNotNull(item); + Assert.AreEqual(2, items.Count()); - Assert.IsNotNull(item); - Assert.AreEqual(2, items.Count()); + var mappedRow = sync + ? mapper.MapRow(queryRow) + : await mapper.MapRowAsync(queryRow); - var mappedRow = await mapper.MapRowAsync(queryRow); - var mappedRows = await mapper.MapRowsAsync(queryRows); + var mappedRows = sync + ? mapper.MapRows(queryRows) + : await mapper.MapRowsAsync(queryRows); - Assert.IsNotNull(mappedRow); - Assert.AreEqual(2, mappedRows.Count()); - } + Assert.IsNotNull(mappedRow); + Assert.AreEqual(2, mappedRows.Count()); } + } - [Test] - public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() - { - var mapper = GetQueryMapper(); + [Test] + public void Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing() + { + Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async() + { + await Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async(false); + } - var queryRow = new Query(@" + private async Task Should_be_able_to_perform_basic_mapping_even_though_columns_are_missing_async(bool sync) + { + var mapper = GetQueryMapper(); + + var queryRow = new Query(@" select top 1 Id, Name as NotMapped, @@ -62,7 +93,7 @@ Age as TheAge BasicMapping "); - var queryRows = new Query(@" + var queryRows = new Query(@" select Id, Name, @@ -71,58 +102,84 @@ Age as TheAge BasicMapping "); - using (GetDatabaseContext()) - { - var item = await mapper.MapObjectAsync(queryRow); - var items = await mapper.MapObjectsAsync(queryRows); + using (GetDatabaseContext()) + { + var item = await mapper.MapObjectAsync(queryRow); + var items = await mapper.MapObjectsAsync(queryRows); - Assert.IsNotNull(item); - Assert.AreEqual(2, items.Count()); + Assert.IsNotNull(item); + Assert.AreEqual(2, items.Count()); - var mappedRow = mapper.MapRowAsync(queryRow).Result; - var mappedRows = await mapper.MapRowsAsync(queryRows); + var mappedRow = mapper.MapRowAsync(queryRow).Result; + var mappedRows = await mapper.MapRowsAsync(queryRows); - Assert.IsNotNull(mappedRow); - Assert.AreEqual(2, mappedRows.Count()); - } + Assert.IsNotNull(mappedRow); + Assert.AreEqual(2, mappedRows.Count()); } + } - [Test] - public void Should_be_able_to_perform_value_mapping() - { - var mapper = GetQueryMapper(); + [Test] + public void Should_be_able_to_perform_value_mapping() + { + Should_be_able_to_perform_value_mapping_async(true).GetAwaiter().GetResult(); + } - var queryRow = new Query(@" + [Test] + public async Task Should_be_able_to_perform_value_mapping_async() + { + await Should_be_able_to_perform_value_mapping_async(false); + } + + private async Task Should_be_able_to_perform_value_mapping_async(bool sync) + { + var mapper = GetQueryMapper(); + + var queryRow = new Query(@" select top 1 Id from BasicMapping "); - var queryRows = new Query(@" + var queryRows = new Query(@" select Id from BasicMapping "); - using (GetDatabaseContext()) - { - var value = mapper.MapValueAsync(queryRow).Result; - var values = mapper.MapValuesAsync(queryRows).Result; + using (GetDatabaseContext()) + { + var value = sync + ? mapper.MapValue(queryRow) + : await mapper.MapValueAsync(queryRow); + + var values = sync + ? mapper.MapValues(queryRows) + : await mapper.MapValuesAsync(queryRows); - Assert.IsNotNull(value); - Assert.AreEqual(2, values.Count()); - } + Assert.IsNotNull(value); + Assert.AreEqual(2, values.Count()); } + } - [Test] - public void Should_be_able_to_perform_dynamic_mapping() - { - var databaseGateway = GetDatabaseGateway(); - var queryMapper = GetQueryMapper(); + [Test] + public void Should_be_able_to_perform_dynamic_mapping() + { + Should_be_able_to_perform_dynamic_mapping_async(true).GetAwaiter().GetResult(); + } + + [Test] + public async Task Should_be_able_to_perform_dynamic_mapping_async() + { + await Should_be_able_to_perform_dynamic_mapping_async(false); + } - var queryRow = new Query(@" + public async Task Should_be_able_to_perform_dynamic_mapping_async(bool sync) + { + var queryMapper = GetQueryMapper(); + + var queryRow = new Query(@" select top 1 Id, Name, @@ -131,7 +188,7 @@ select top 1 BasicMapping "); - var queryRows = new Query(@" + var queryRows = new Query(@" select Id, Name, @@ -140,14 +197,13 @@ select top 1 BasicMapping "); - using (GetDatabaseContext()) - { - var item = queryMapper.MapItemAsync(queryRow).Result; - var items = queryMapper.MapItemsAsync(queryRows).Result; + using (GetDatabaseContext()) + { + var item = queryMapper.MapItemAsync(queryRow).Result; + var items = queryMapper.MapItemsAsync(queryRows).Result; - Assert.IsNotNull(item); - Assert.AreEqual(2, items.Count()); - } + Assert.IsNotNull(item); + Assert.AreEqual(2, items.Count()); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index 6c3febd..5d64e04 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -13,7 +13,7 @@ https://github.com/Shuttle/Shuttle.Core.Data Provides an abstraction over ADO.NET. - Copyright (c) 2023, Eben Roux + Copyright (c) 2024, Eben Roux orm micro-orm microorm data-access diff --git a/Shuttle.Core.Data/Properties/AssemblyInfo.cs b/Shuttle.Core.Data/Properties/AssemblyInfo.cs index bd08bee..c4e6e9e 100644 --- a/Shuttle.Core.Data/Properties/AssemblyInfo.cs +++ b/Shuttle.Core.Data/Properties/AssemblyInfo.cs @@ -14,7 +14,7 @@ #endif [assembly: AssemblyVersion("15.0.0.0")] -[assembly: AssemblyCopyright("Copyright (c) 2023, Eben Roux")] +[assembly: AssemblyCopyright("Copyright (c) 2024, Eben Roux")] [assembly: AssemblyProduct("Shuttle.Core.Data")] [assembly: AssemblyCompany("Eben Roux")] [assembly: AssemblyConfiguration("Release")] From 2586ac437a5d0de9d9d5d3fd77a0a7a87fa00336 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 7 Jan 2024 08:21:04 +0200 Subject: [PATCH 17/37] - database context refactoring --- .../DataRepositoryFixture.cs | 4 +- .../DatabaseContextFactoryFixture.cs | 6 --- .../DatabaseContextFixture.cs | 24 +++------ .../DatabaseContextServiceFixture.cs | 4 +- .../Mapping/QueryMapperFixture.cs | 9 +++- .../Shuttle.Core.Data.Tests.csproj | 10 ++-- .../DatabaseContextFactoryOptions.cs | 2 - Shuttle.Core.Data/DatabaseContext.cs | 32 ++++++++---- .../DatabaseContextCreatedEventArgs.cs | 15 ++++++ Shuttle.Core.Data/DatabaseContextFactory.cs | 48 ++++++++---------- Shuttle.Core.Data/DatabaseContextService.cs | 29 +++-------- Shuttle.Core.Data/DatabaseGateway.cs | 8 --- .../DbCommandCreatedEventArgs.cs | 6 +-- Shuttle.Core.Data/DbCommandFactory.cs | 5 ++ .../DatabaseContextFactoryExtensions.cs | 29 +---------- .../DatabaseContextServiceExtensions.cs | 50 ------------------- Shuttle.Core.Data/IDatabaseContext.cs | 12 +++-- Shuttle.Core.Data/IDatabaseContextFactory.cs | 10 ++-- Shuttle.Core.Data/IDatabaseContextService.cs | 2 +- Shuttle.Core.Data/IDatabaseGateway.cs | 3 -- Shuttle.Core.Data/IDbCommandFactory.cs | 5 +- Shuttle.Core.Data/IDbConnectionFactory.cs | 2 +- Shuttle.Core.Data/Shuttle.Core.Data.csproj | 2 +- Shuttle.Core.Data/TransactionEventArgs.cs | 16 ++++++ 24 files changed, 133 insertions(+), 200 deletions(-) create mode 100644 Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs create mode 100644 Shuttle.Core.Data/TransactionEventArgs.cs diff --git a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs index 9a0c59a..69522f4 100644 --- a/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DataRepositoryFixture.cs @@ -78,7 +78,7 @@ private async Task Should_be_able_to_fetch_a_single_item_async(bool sync) } [Test] - public async Task Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() + public void Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found() { Should_be_able_to_get_default_when_fetching_a_single_item_that_is_not_found_async(true).GetAwaiter().GetResult(); } @@ -167,7 +167,7 @@ private async Task Should_be_able_to_fetch_mapped_rows_async(bool sync) } [Test] - public async Task Should_be_able_to_fetch_a_single_row() + public void Should_be_able_to_fetch_a_single_row() { Should_be_able_to_fetch_a_single_row_async(true).GetAwaiter().GetResult(); } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index 05239c3..d2ef80a 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -40,17 +40,11 @@ public void Should_be_able_to_check_connection_availability() Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.True); Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.True); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.True); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.True); databaseContextFactory.Setup(m => m.Create()).Throws(new Exception()); databaseContextFactory.Setup(m => m.Create(It.IsAny())).Throws(new Exception()); - databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); - databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.False); Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.False); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.False); - Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.False); } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index 0ec98bb..25bf606 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -15,7 +15,7 @@ public void Should_not_be_able_to_create_an_invalid_connection() { Assert.Throws(() => { - using (new DatabaseContext("Microsoft .Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) + using (new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) { } }); @@ -26,7 +26,7 @@ public void Should_not_be_able_to_create_a_non_existent_connection() { Assert.Throws(() => { - using (var databaseContext = new DatabaseContext("Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), + using (var databaseContext = new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), new Mock().Object, new DatabaseContextService())) { databaseContext.Connection.Open(); @@ -34,16 +34,6 @@ public void Should_not_be_able_to_create_a_non_existent_connection() }); } - [Test] - public void Should_be_able_to_create_a_valid_connection() - { - using ( - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) - { - } - } - [Test] public void Should_be_able_to_begin_and_commit_a_transaction() { @@ -60,7 +50,7 @@ private async Task Should_be_able_to_begin_and_commit_a_transaction_async(bool s { using ( var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { if (sync) @@ -92,7 +82,7 @@ private async Task Should_be_able_to_begin_and_rollback_a_transaction_async(bool { using ( var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { if (sync) @@ -122,7 +112,7 @@ private async Task Should_be_able_to_call_commit_without_a_transaction_async(boo { using ( var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { if (sync) @@ -141,7 +131,7 @@ public void Should_be_able_to_call_dispose_more_than_once() { using ( var connection = - new DatabaseContext("Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), + new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), new Mock().Object, new DatabaseContextService())) { connection.Dispose(); @@ -170,7 +160,7 @@ private async Task Should_be_able_to_create_a_command_async(bool sync) dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); - using (var connection = new DatabaseContext("Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) + using (var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) { if (sync) { diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index 861c942..9781292 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -12,11 +12,11 @@ public void Should_be_able_to_use_different_contexts() { var service = new DatabaseContextService(); - var context1 = new DatabaseContext("mock-1", new Mock().Object, new Mock().Object, service).WithName("mock-1"); + var context1 = new DatabaseContext("mock-1", "provider-name", new Mock().Object, new Mock().Object, service); Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - var context2 = new DatabaseContext("mock-2", new Mock().Object, new Mock().Object, service).WithName("mock-2"); + var context2 = new DatabaseContext("mock-2", "provider-name", new Mock().Object, new Mock().Object, service); Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); diff --git a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs index ffc4888..6ea7675 100644 --- a/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs +++ b/Shuttle.Core.Data.Tests/Mapping/QueryMapperFixture.cs @@ -199,8 +199,13 @@ select top 1 using (GetDatabaseContext()) { - var item = queryMapper.MapItemAsync(queryRow).Result; - var items = queryMapper.MapItemsAsync(queryRows).Result; + var item = sync + ? queryMapper.MapItem(queryRow) + : await queryMapper.MapItemAsync(queryRow); + + var items = sync + ? queryMapper.MapItems(queryRows) + : await queryMapper.MapItemsAsync(queryRows); Assert.IsNotNull(item); Assert.AreEqual(2, items.Count()); diff --git a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj index b5f184c..6b75f14 100644 --- a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj +++ b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj @@ -17,11 +17,11 @@ - - - - - + + + + + diff --git a/Shuttle.Core.Data/Configuration/DatabaseContextFactoryOptions.cs b/Shuttle.Core.Data/Configuration/DatabaseContextFactoryOptions.cs index 2183405..f01aefb 100644 --- a/Shuttle.Core.Data/Configuration/DatabaseContextFactoryOptions.cs +++ b/Shuttle.Core.Data/Configuration/DatabaseContextFactoryOptions.cs @@ -3,7 +3,5 @@ namespace Shuttle.Core.Data public class DatabaseContextFactoryOptions { public string DefaultConnectionStringName { get; set; } - public string DefaultProviderName { get; set; } - public string DefaultConnectionString { get; set; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 3105073..221c350 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -2,7 +2,9 @@ using System.Data; using System.Data.Common; using System.Threading.Tasks; +using System.Transactions; using Shuttle.Core.Contract; +using IsolationLevel = System.Data.IsolationLevel; namespace Shuttle.Core.Data { @@ -14,8 +16,9 @@ public class DatabaseContext : IDatabaseContext private bool _disposed; private readonly IDatabaseContext _activeContext; - public DatabaseContext(string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) + public DatabaseContext(string name, string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) { + Name = Guard.AgainstNullOrEmptyString(name, nameof(name)); _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); _dispose = true; @@ -30,24 +33,24 @@ public DatabaseContext(string providerName, IDbConnection dbConnection, IDbComma _databaseContextService.Add(this); } + public event EventHandler Operation; + public event EventHandler TransactionStarted; + public event EventHandler TransactionCommitted; + public event EventHandler TransactionRolledBack; + public Guid Key { get; } - public string Name { get; private set; } = string.Empty; + public string Name { get; } + public int Depth { get; private set; } public IDbTransaction Transaction { get; private set; } public string ProviderName { get; } public IDbConnection Connection { get; private set; } - public IDatabaseContext WithName(string name) - { - Name = name; - - return this; - } - public IDatabaseContext Suppressed() { - return new DatabaseContext(ProviderName, Connection, _dbCommandFactory, _databaseContextService) + return new DatabaseContext(Name, ProviderName, Connection, _dbCommandFactory, _databaseContextService) { Transaction = Transaction, + Depth = Depth + 1, _dispose = false }; } @@ -72,7 +75,9 @@ public async Task CreateCommandAsync(IQuery query) private async Task CreateCommandAsync(IQuery query, bool sync) { var command = _dbCommandFactory.Create(sync ? GetOpenConnectionAsync(true).GetAwaiter().GetResult() : await GetOpenConnectionAsync(false).ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); + command.Transaction = Transaction; + return command; } @@ -121,6 +126,8 @@ private async Task BeginTransactionAsync(IsolationLevel isolat Transaction = await (await GetOpenConnectionAsync(false).ConfigureAwait(false)).BeginTransactionAsync(isolationLevel); } + TransactionStarted?.Invoke(this, new TransactionEventArgs(Transaction)); + return this; } @@ -150,6 +157,8 @@ private async Task CommitTransactionAsync(bool sync) await ((DbTransaction)Transaction).CommitAsync(); } + TransactionCommitted?.Invoke(this, new TransactionEventArgs(Transaction)); + Transaction = null; } @@ -179,12 +188,15 @@ protected virtual void Dispose(bool disposing) if (HasTransaction) { Transaction.Rollback(); + + TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); } Connection.Dispose(); } Connection = null; + Transaction = null; _disposed = true; } } diff --git a/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs b/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs new file mode 100644 index 0000000..2ffb5b6 --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs @@ -0,0 +1,15 @@ +using System; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public class DatabaseContextEventArgs : EventArgs + { + public IDatabaseContext DatabaseContext { get; } + + public DatabaseContextEventArgs(IDatabaseContext databaseContext) + { + DatabaseContext = Guard.AgainstNull(databaseContext, nameof(databaseContext)); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 833fafb..10c2c68 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -10,9 +10,7 @@ public class DatabaseContextFactory : IDatabaseContextFactory private readonly IOptionsMonitor _connectionStringOptions; private readonly DataAccessOptions _dataAccessOptions; - public DatabaseContextFactory(IOptionsMonitor connectionStringOptions, IOptions dataAccessOptions, - IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory, - IDatabaseContextService databaseContextService) + public DatabaseContextFactory(IOptionsMonitor connectionStringOptions, IOptions dataAccessOptions, IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) { Guard.AgainstNull(dataAccessOptions, nameof(dataAccessOptions)); @@ -24,8 +22,13 @@ public DatabaseContextFactory(IOptionsMonitor connectio DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } + public event EventHandler DatabaseContextCreated; + public event EventHandler DatabaseContextSuppressed; + public IDatabaseContext Create(string name) { + Guard.AgainstNullOrEmptyString(name, nameof(name)); + var connectionStringOptions = _connectionStringOptions.Get(name); if (connectionStringOptions == null || string.IsNullOrEmpty(connectionStringOptions.Name)) @@ -33,26 +36,22 @@ public IDatabaseContext Create(string name) throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, name)); } - var databaseContext = Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString); + if (DatabaseContextService.Contains(name)) + { + var databaseContext = DatabaseContextService.Get(name).Suppressed(); - return databaseContext.WithName(name); - } + DatabaseContextSuppressed?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); - public IDatabaseContext Create(string providerName, string connectionString) - { - return DatabaseContextService.ContainsConnectionString(connectionString) - ? DatabaseContextService.GetConnectionString(connectionString).Suppressed() - : new DatabaseContext(providerName, (DbConnection)DbConnectionFactory.Create(providerName, connectionString), - DbCommandFactory, DatabaseContextService); - } + return databaseContext; + } + else + { + var databaseContext = new DatabaseContext(name, connectionStringOptions.ProviderName, (DbConnection)DbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), DbCommandFactory, DatabaseContextService); - public IDatabaseContext Create(string providerName, DbConnection dbConnection) - { - Guard.AgainstNull(dbConnection, nameof(dbConnection)); + DatabaseContextCreated?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); - return DatabaseContextService.ContainsConnectionString(dbConnection.ConnectionString) - ? DatabaseContextService.GetConnectionString(dbConnection.ConnectionString).Suppressed() - : new DatabaseContext(providerName, dbConnection, DbCommandFactory, DatabaseContextService); + return databaseContext; + } } public IDbConnectionFactory DbConnectionFactory { get; } @@ -61,17 +60,12 @@ public IDatabaseContext Create(string providerName, DbConnection dbConnection) public IDatabaseContext Create() { - if (!string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName)) - { - return Create(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName); - } - - if (!string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName) && !string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionString)) + if (string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName)) { - return Create(_dataAccessOptions.DatabaseContextFactory.DefaultProviderName, _dataAccessOptions.DatabaseContextFactory.DefaultConnectionString); + throw new InvalidOperationException(Resources.DatabaseContextFactoryOptionsException); } - throw new InvalidOperationException(Resources.DatabaseContextFactoryOptionsException); + return Create(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 2a869f8..6dcd6ef 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -60,13 +60,6 @@ public ActiveDatabaseContext Use(IDatabaseContext context) return new ActiveDatabaseContext(this, current); } - public IDatabaseContext Find(Predicate match) - { - Guard.AgainstNull(match, nameof(match)); - - return GetAmbientData().DatabaseContexts.Find(match); - } - public void Add(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); @@ -100,26 +93,16 @@ public void Remove(IDatabaseContext context) GetAmbientData().DatabaseContexts.Remove(candidate); } - private IDatabaseContext Find(IDatabaseContext context) + public IDatabaseContext Find(Predicate match) { - return Find(candidate => candidate.Key.Equals(context.Key)); + Guard.AgainstNull(match, nameof(match)); + + return GetAmbientData().DatabaseContexts.Find(match); } - public ActiveDatabaseContext Use(string name) + private IDatabaseContext Find(IDatabaseContext context) { - Guard.AgainstNullOrEmptyString(name, nameof(name)); - - var current = GetAmbientData().Current; - - GetAmbientData().Current = GetAmbientData().DatabaseContexts.Find(candidate => - candidate.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - if (GetAmbientData().Current == null) - { - throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); - } - - return new ActiveDatabaseContext(this, current); + return Find(candidate => candidate.Key.Equals(context.Key)); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index bfd933f..35e71d0 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -75,8 +75,6 @@ private async Task GetRowAsync(IQuery query, CancellationToken cancella return table.Rows[0]; } - public event EventHandler DbCommandCreated; - public IDataReader GetReader(IQuery query, CancellationToken cancellationToken = default) { return GetReaderAsync(query, cancellationToken, true).GetAwaiter().GetResult(); @@ -93,8 +91,6 @@ private async Task GetReaderAsync(IQuery query, CancellationToken c using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { - DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); } } @@ -115,8 +111,6 @@ private async Task ExecuteAsync(IQuery query, CancellationToken cancellatio using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { - DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - return sync ? command.ExecuteNonQuery() : await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } } @@ -137,8 +131,6 @@ public async Task GetScalarAsync(IQuery query, CancellationToken cancellat using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { - DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); - var scalar = sync ? command.ExecuteScalar() : await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); return scalar != null && scalar != DBNull.Value ? (T)scalar : default; diff --git a/Shuttle.Core.Data/DbCommandCreatedEventArgs.cs b/Shuttle.Core.Data/DbCommandCreatedEventArgs.cs index 1d97ccd..c8635aa 100644 --- a/Shuttle.Core.Data/DbCommandCreatedEventArgs.cs +++ b/Shuttle.Core.Data/DbCommandCreatedEventArgs.cs @@ -6,11 +6,11 @@ namespace Shuttle.Core.Data { public class DbCommandCreatedEventArgs : EventArgs { - public IDbCommand Command { get; } + public IDbCommand DbCommand { get; } - public DbCommandCreatedEventArgs(IDbCommand command) + public DbCommandCreatedEventArgs(IDbCommand dbCommand) { - Command = Guard.AgainstNull(command, nameof(command)); + DbCommand = Guard.AgainstNull(dbCommand, nameof(dbCommand)); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DbCommandFactory.cs b/Shuttle.Core.Data/DbCommandFactory.cs index 983f05b..f018758 100644 --- a/Shuttle.Core.Data/DbCommandFactory.cs +++ b/Shuttle.Core.Data/DbCommandFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Data; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; @@ -15,6 +16,8 @@ public DbCommandFactory(IOptions options) _commandTimeout = Guard.AgainstNull(options.Value, nameof(options.Value)).CommandTimeout; } + public event EventHandler DbCommandCreated; + public IDbCommand Create(IDbConnection connection, IQuery query) { var command = Guard.AgainstNull(connection, nameof(connection)).CreateCommand(); @@ -23,6 +26,8 @@ public IDbCommand Create(IDbConnection connection, IQuery query) Guard.AgainstNull(query, nameof(query)).Prepare(command); + DbCommandCreated?.Invoke(this, new DbCommandCreatedEventArgs(command)); + return command; } } diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs index b31767c..c424977 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextFactoryExtensions.cs @@ -21,8 +21,7 @@ public static bool IsAvailable(this IDatabaseContextFactory databaseContextFacto }, cancellationToken, retries, secondsBetweenRetries); } - public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string name, - CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) + public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string name, CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) { Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); @@ -34,32 +33,6 @@ public static bool IsAvailable(this IDatabaseContextFactory databaseContextFacto }, cancellationToken, retries, secondsBetweenRetries); } - public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string providerName, IDbConnection dbConnection, - CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) - { - Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); - - return IsAvailable(() => - { - using (databaseContextFactory.Create(providerName, (DbConnection)dbConnection)) - { - } - }, cancellationToken, retries, secondsBetweenRetries); - } - - public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string providerName, string connectionString, - CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) - { - Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); - - return IsAvailable(() => - { - using (databaseContextFactory.Create(providerName, connectionString)) - { - } - }, cancellationToken, retries, secondsBetweenRetries); - } - private static bool IsAvailable(Action action, CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) { var attempt = 0; diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs index b4d7185..f3a0e7c 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -22,14 +22,6 @@ public static bool Contains(this IDatabaseContextService databaseContextService, return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) != null; } - public static bool ContainsConnectionString(this IDatabaseContextService databaseContextService, string connectionString) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); - - return databaseContextService.FindConnectionString(connectionString) != null; - } - public static IDatabaseContext Get(this IDatabaseContextService databaseContextService, string name) { Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); @@ -38,11 +30,6 @@ public static IDatabaseContext Get(this IDatabaseContextService databaseContextS return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); } - public static IDatabaseContext GetConnectionString(this IDatabaseContextService databaseContextService, string connectionString) - { - return databaseContextService.FindConnectionString(connectionString) ?? throw new Exception(Resources.DatabaseContextNotFoundException); - } - public static ActiveDatabaseContext Use(this IDatabaseContextService databaseContextService, string name) { Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); @@ -50,42 +37,5 @@ public static ActiveDatabaseContext Use(this IDatabaseContextService databaseCon return databaseContextService.Use(databaseContextService.Get(name)); } - - public static IDatabaseContext FindConnectionString(this IDatabaseContextService databaseContextService, string connectionString) - { - Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - Guard.AgainstNullOrEmptyString(connectionString, nameof(connectionString)); - - var result = databaseContextService.Find(candidate => - candidate.Connection.ConnectionString.Equals(connectionString, - StringComparison.OrdinalIgnoreCase)); - - if (result == null) - { - var matchDbConnectionStringBuilder = new DbConnectionStringBuilder - { - ConnectionString = connectionString.ToLowerInvariant() - }; - - matchDbConnectionStringBuilder.Remove("password"); - matchDbConnectionStringBuilder.Remove("pwd"); - - result = databaseContextService.Find(candidate => - { - var candidateDbConnectionStringBuilder = new DbConnectionStringBuilder - { - ConnectionString = candidate.Connection.ConnectionString - }; - - candidateDbConnectionStringBuilder.Remove("password"); - candidateDbConnectionStringBuilder.Remove("pwd"); - - return candidateDbConnectionStringBuilder.ConnectionString.Equals(matchDbConnectionStringBuilder.ConnectionString, - StringComparison.OrdinalIgnoreCase); - }); - } - - return result; - } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index 6a3d43d..ed9e5f8 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,14 +1,21 @@ using System; using System.Data; -using System.Data.Common; using System.Threading.Tasks; +using System.Transactions; +using Shuttle.Core.Contract; +using IsolationLevel = System.Data.IsolationLevel; namespace Shuttle.Core.Data { public interface IDatabaseContext : IDisposable { - Guid Key { get; } + event EventHandler Operation; + event EventHandler TransactionStarted; + event EventHandler TransactionCommitted; + + Guid Key { get; } string Name { get; } + int Depth { get; } IDbTransaction Transaction { get; } IDbConnection Connection { get; } @@ -22,7 +29,6 @@ public interface IDatabaseContext : IDisposable Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified); void CommitTransaction(); Task CommitTransactionAsync(); - IDatabaseContext WithName(string name); IDatabaseContext Suppressed(); IDatabaseContext SuppressDispose(); } diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index ca31692..1a5a7fa 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -1,13 +1,13 @@ -using System.Data.Common; -using System.Threading.Tasks; +using System; namespace Shuttle.Core.Data { public interface IDatabaseContextFactory { - IDatabaseContext Create(string name); - IDatabaseContext Create(string providerName, string connectionString); - IDatabaseContext Create(string providerName, DbConnection dbConnection); + event EventHandler DatabaseContextCreated; + event EventHandler DatabaseContextSuppressed; + + IDatabaseContext Create(string name); IDatabaseContext Create(); IDbConnectionFactory DbConnectionFactory { get; } diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs index 792df3f..122d1e2 100644 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -7,8 +7,8 @@ public interface IDatabaseContextService bool HasCurrent { get; } IDatabaseContext Current { get; } ActiveDatabaseContext Use(IDatabaseContext context); - IDatabaseContext Find(Predicate match); void Add(IDatabaseContext context); void Remove(IDatabaseContext context); + IDatabaseContext Find(Predicate match); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index 95af9c4..a8b50fb 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Data; using System.Threading; @@ -8,8 +7,6 @@ namespace Shuttle.Core.Data { public interface IDatabaseGateway { - event EventHandler DbCommandCreated; - IDataReader GetReader(IQuery query, CancellationToken cancellationToken = default); int Execute(IQuery query, CancellationToken cancellationToken = default); T GetScalar(IQuery query, CancellationToken cancellationToken = default); diff --git a/Shuttle.Core.Data/IDbCommandFactory.cs b/Shuttle.Core.Data/IDbCommandFactory.cs index 43dcc97..758ba03 100644 --- a/Shuttle.Core.Data/IDbCommandFactory.cs +++ b/Shuttle.Core.Data/IDbCommandFactory.cs @@ -1,9 +1,12 @@ +using System; using System.Data; namespace Shuttle.Core.Data { - public interface IDbCommandFactory + public interface IDbCommandFactory { + event EventHandler DbCommandCreated; + IDbCommand Create(IDbConnection connection, IQuery query); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDbConnectionFactory.cs b/Shuttle.Core.Data/IDbConnectionFactory.cs index 9e02610..fc43e84 100644 --- a/Shuttle.Core.Data/IDbConnectionFactory.cs +++ b/Shuttle.Core.Data/IDbConnectionFactory.cs @@ -3,7 +3,7 @@ namespace Shuttle.Core.Data { - public interface IDbConnectionFactory + public interface IDbConnectionFactory { event EventHandler DbConnectionCreated; diff --git a/Shuttle.Core.Data/Shuttle.Core.Data.csproj b/Shuttle.Core.Data/Shuttle.Core.Data.csproj index d1199ef..8a70c80 100644 --- a/Shuttle.Core.Data/Shuttle.Core.Data.csproj +++ b/Shuttle.Core.Data/Shuttle.Core.Data.csproj @@ -19,7 +19,7 @@ - + diff --git a/Shuttle.Core.Data/TransactionEventArgs.cs b/Shuttle.Core.Data/TransactionEventArgs.cs new file mode 100644 index 0000000..443fc88 --- /dev/null +++ b/Shuttle.Core.Data/TransactionEventArgs.cs @@ -0,0 +1,16 @@ +using System; +using System.Data; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public class TransactionEventArgs : EventArgs + { + public TransactionEventArgs(IDbTransaction transaction) + { + Transaction = Guard.AgainstNull(transaction, nameof(transaction)); + } + + public IDbTransaction Transaction { get; } + } +} \ No newline at end of file From 7fd0a3908a8b6b9257b65651e514834188cbb869 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 7 Jan 2024 08:28:21 +0200 Subject: [PATCH 18/37] - removed `Operation` event --- Shuttle.Core.Data/.package/package.nuspec | 2 +- Shuttle.Core.Data/DatabaseContext.cs | 2 -- Shuttle.Core.Data/IDatabaseContext.cs | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index 5d64e04..cb914c4 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -21,7 +21,7 @@ - + diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 221c350..b4833e4 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -2,7 +2,6 @@ using System.Data; using System.Data.Common; using System.Threading.Tasks; -using System.Transactions; using Shuttle.Core.Contract; using IsolationLevel = System.Data.IsolationLevel; @@ -33,7 +32,6 @@ public DatabaseContext(string name, string providerName, IDbConnection dbConnect _databaseContextService.Add(this); } - public event EventHandler Operation; public event EventHandler TransactionStarted; public event EventHandler TransactionCommitted; public event EventHandler TransactionRolledBack; diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index ed9e5f8..75887c6 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,15 +1,12 @@ using System; using System.Data; using System.Threading.Tasks; -using System.Transactions; -using Shuttle.Core.Contract; using IsolationLevel = System.Data.IsolationLevel; namespace Shuttle.Core.Data { public interface IDatabaseContext : IDisposable { - event EventHandler Operation; event EventHandler TransactionStarted; event EventHandler TransactionCommitted; From d948e2535e46352ee6ba830b8afaa502e18a83a2 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 7 Jan 2024 10:21:54 +0200 Subject: [PATCH 19/37] - reader lock --- Shuttle.Core.Data/DatabaseContext.cs | 7 +++++-- Shuttle.Core.Data/DatabaseGateway.cs | 14 ++++++++++++-- Shuttle.Core.Data/IDatabaseContext.cs | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index b4833e4..edfb73d 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -34,6 +34,7 @@ public DatabaseContext(string name, string providerName, IDbConnection dbConnect public event EventHandler TransactionStarted; public event EventHandler TransactionCommitted; + public event EventHandler Disposed; public event EventHandler TransactionRolledBack; public Guid Key { get; } @@ -176,12 +177,12 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - if (_disposed || !_dispose) + if (_disposed) { return; } - if (disposing) + if (_dispose && disposing) { if (HasTransaction) { @@ -196,6 +197,8 @@ protected virtual void Dispose(bool disposing) Connection = null; Transaction = null; _disposed = true; + + Disposed?.Invoke(this, EventArgs.Empty); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 35e71d0..c9a1c10 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -11,6 +11,7 @@ namespace Shuttle.Core.Data { public class DatabaseGateway : IDatabaseGateway { + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private readonly IDatabaseContextService _databaseContextService; public DatabaseGateway(IDatabaseContextService databaseContextService) @@ -89,9 +90,18 @@ private async Task GetReaderAsync(IQuery query, CancellationToken c { Guard.AgainstNull(query, nameof(query)); - using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) + await _lock.WaitAsync(CancellationToken.None).ConfigureAwait(false); + + try + { + using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) + { + return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + } + } + finally { - return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + _lock.Release(); } } diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index 75887c6..a517bb4 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,6 +1,7 @@ using System; using System.Data; using System.Threading.Tasks; +using Shuttle.Core.Contract; using IsolationLevel = System.Data.IsolationLevel; namespace Shuttle.Core.Data @@ -9,6 +10,7 @@ public interface IDatabaseContext : IDisposable { event EventHandler TransactionStarted; event EventHandler TransactionCommitted; + event EventHandler Disposed; Guid Key { get; } string Name { get; } From d6a61b620c6a5b112cd1e05a666fceb7da9efb5f Mon Sep 17 00:00:00 2001 From: Eben Date: Mon, 8 Jan 2024 07:53:36 +0200 Subject: [PATCH 20/37] - reference counting DatabaseContext --- Shuttle.Core.Data.Tests/AsyncFixture.cs | 55 +++++ Shuttle.Core.Data/DataRepository.cs | 2 +- Shuttle.Core.Data/DatabaseContext.cs | 64 ++--- Shuttle.Core.Data/DatabaseContextFactory.cs | 6 +- Shuttle.Core.Data/DatabaseGateway.cs | 38 +-- Shuttle.Core.Data/IDatabaseContext.cs | 7 +- Shuttle.Core.Data/IDatabaseContextFactory.cs | 2 +- Shuttle.Core.Data/QueryMapper.cs | 225 +++++++++++++----- .../ServiceCollectionExtensions.cs | 2 + 9 files changed, 275 insertions(+), 126 deletions(-) create mode 100644 Shuttle.Core.Data.Tests/AsyncFixture.cs rename Shuttle.Core.Data/{Configuration => }/ServiceCollectionExtensions.cs (93%) diff --git a/Shuttle.Core.Data.Tests/AsyncFixture.cs b/Shuttle.Core.Data.Tests/AsyncFixture.cs new file mode 100644 index 0000000..59180d8 --- /dev/null +++ b/Shuttle.Core.Data.Tests/AsyncFixture.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Shuttle.Core.Data.Tests; + +public class AsyncFixture : MappingFixture +{ + private readonly Query _rowsQuery = new(@" +select + Id, + Name, + Age +from + BasicMapping +"); + + [Test] + public void Should_be_able_to_use_the_same_database_context_across_tasks_async() + { + var tasks = new List(); + + using (GetDatabaseContext()) + { + for (int i = 0; i < 10; i++) + { + tasks.Add(GetDatabaseGateway().GetRowsAsync(_rowsQuery)); + } + + Task.WaitAll(tasks.ToArray()); + } + } + + [Test] + public async Task Should_be_able_to_use_the_same_database_context_across_synchronized_tasks_async() + { + using (GetDatabaseContext()) + { + await GetRowsAsync(0); + } + } + + private async Task GetRowsAsync(int depth) + { + _ = await GetDatabaseGateway().GetRowsAsync(_rowsQuery); + + if (depth < 5) + { + using (GetDatabaseContext()) + { + await GetRowsAsync(depth + 1); + } + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DataRepository.cs b/Shuttle.Core.Data/DataRepository.cs index e923f5f..ef42a49 100644 --- a/Shuttle.Core.Data/DataRepository.cs +++ b/Shuttle.Core.Data/DataRepository.cs @@ -26,7 +26,7 @@ public async Task> FetchItemsAsync(IQuery query, CancellationToke { var rows = await _databaseGateway.GetRowsAsync(query, cancellationToken); - return (IEnumerable)await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); + return await Task.FromResult(rows.MappedRowsUsing(_dataRowMapper).Select(row => row.Result)).ConfigureAwait(false); } public T FetchItem(IQuery query, CancellationToken cancellationToken = default) diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index edfb73d..e2cf49b 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -11,7 +11,6 @@ public class DatabaseContext : IDatabaseContext { private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; - private bool _dispose; private bool _disposed; private readonly IDatabaseContext _activeContext; @@ -20,7 +19,6 @@ public DatabaseContext(string name, string providerName, IDbConnection dbConnect Name = Guard.AgainstNullOrEmptyString(name, nameof(name)); _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - _dispose = true; Key = Guid.NewGuid(); @@ -35,32 +33,16 @@ public DatabaseContext(string name, string providerName, IDbConnection dbConnect public event EventHandler TransactionStarted; public event EventHandler TransactionCommitted; public event EventHandler Disposed; + public event EventHandler DisposeIgnored; public event EventHandler TransactionRolledBack; public Guid Key { get; } public string Name { get; } - public int Depth { get; private set; } + public int ReferenceCount { get; private set; } = 1; public IDbTransaction Transaction { get; private set; } public string ProviderName { get; } public IDbConnection Connection { get; private set; } - public IDatabaseContext Suppressed() - { - return new DatabaseContext(Name, ProviderName, Connection, _dbCommandFactory, _databaseContextService) - { - Transaction = Transaction, - Depth = Depth + 1, - _dispose = false - }; - } - - public IDatabaseContext SuppressDispose() - { - _dispose = false; - - return this; - } - public IDbCommand CreateCommand(IQuery query) { return CreateCommandAsync(query, true).GetAwaiter().GetResult(); @@ -140,6 +122,13 @@ public async Task CommitTransactionAsync() await CommitTransactionAsync(false).ConfigureAwait(false); } + public IDatabaseContext Referenced() + { + ReferenceCount++; + + return this; + } + private async Task CommitTransactionAsync(bool sync) { if (!HasTransaction) @@ -163,37 +152,36 @@ private async Task CommitTransactionAsync(bool sync) public void Dispose() { - _databaseContextService.Remove(this); - - if (_activeContext != null && _databaseContextService.Contains(_activeContext)) + if (_disposed) { - _databaseContextService.Use(_activeContext); + return; } - Dispose(true); - - GC.SuppressFinalize(this); - } + ReferenceCount--; - protected virtual void Dispose(bool disposing) - { - if (_disposed) + if (ReferenceCount > 0) { + DisposeIgnored?.Invoke(this, EventArgs.Empty); + return; } - if (_dispose && disposing) + _databaseContextService.Remove(this); + + if (_activeContext != null && _databaseContextService.Contains(_activeContext)) { - if (HasTransaction) - { - Transaction.Rollback(); + _databaseContextService.Use(_activeContext); + } - TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); - } + if (HasTransaction) + { + Transaction.Rollback(); - Connection.Dispose(); + TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); } + Connection.Dispose(); + Connection = null; Transaction = null; _disposed = true; diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 10c2c68..add0ab2 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -23,7 +23,7 @@ public DatabaseContextFactory(IOptionsMonitor connectio } public event EventHandler DatabaseContextCreated; - public event EventHandler DatabaseContextSuppressed; + public event EventHandler DatabaseContextReferenced; public IDatabaseContext Create(string name) { @@ -38,9 +38,9 @@ public IDatabaseContext Create(string name) if (DatabaseContextService.Contains(name)) { - var databaseContext = DatabaseContextService.Get(name).Suppressed(); + var databaseContext = DatabaseContextService.Get(name).Referenced(); - DatabaseContextSuppressed?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); + DatabaseContextReferenced?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); return databaseContext; } diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index c9a1c10..0fd9015 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -6,17 +6,19 @@ using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class DatabaseGateway : IDatabaseGateway { - private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private readonly ISynchronizationService _synchronizationService; private readonly IDatabaseContextService _databaseContextService; - public DatabaseGateway(IDatabaseContextService databaseContextService) + public DatabaseGateway(IDatabaseContextService databaseContextService, ISynchronizationService synchronizationService) { _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + _synchronizationService = synchronizationService; } public DataTable GetDataTable(IQuery query, CancellationToken cancellationToken = default) @@ -33,14 +35,23 @@ private async Task GetDataTableAsync(IQuery query, CancellationToken { Guard.AgainstNull(query, nameof(query)); - var results = new DataTable(); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - using (var reader = sync ? GetReader(query, cancellationToken) : await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) + try { - results.Load(reader); - } + var results = new DataTable(); + + using (var reader = sync ? GetReader(query, cancellationToken) : await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) + { + results.Load(reader); + } - return results; + return results; + } + finally + { + _synchronizationService.Release("GetReader"); + } } public IEnumerable GetRows(IQuery query, CancellationToken cancellationToken = default) @@ -90,18 +101,9 @@ private async Task GetReaderAsync(IQuery query, CancellationToken c { Guard.AgainstNull(query, nameof(query)); - await _lock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - - try - { - using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) - { - return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); - } - } - finally + using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) { - _lock.Release(); + return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); } } diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index a517bb4..c210a74 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,7 +1,6 @@ using System; using System.Data; using System.Threading.Tasks; -using Shuttle.Core.Contract; using IsolationLevel = System.Data.IsolationLevel; namespace Shuttle.Core.Data @@ -11,10 +10,11 @@ public interface IDatabaseContext : IDisposable event EventHandler TransactionStarted; event EventHandler TransactionCommitted; event EventHandler Disposed; + event EventHandler DisposeIgnored; Guid Key { get; } string Name { get; } - int Depth { get; } + int ReferenceCount { get; } IDbTransaction Transaction { get; } IDbConnection Connection { get; } @@ -28,7 +28,6 @@ public interface IDatabaseContext : IDisposable Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified); void CommitTransaction(); Task CommitTransactionAsync(); - IDatabaseContext Suppressed(); - IDatabaseContext SuppressDispose(); + IDatabaseContext Referenced(); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index 1a5a7fa..88deb07 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -5,7 +5,7 @@ namespace Shuttle.Core.Data public interface IDatabaseContextFactory { event EventHandler DatabaseContextCreated; - event EventHandler DatabaseContextSuppressed; + event EventHandler DatabaseContextReferenced; IDatabaseContext Create(string name); IDatabaseContext Create(); diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index 59ce17d..04019ed 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -8,21 +8,24 @@ using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class QueryMapper : IQueryMapper { + private readonly ISynchronizationService _synchronizationService; private readonly object _lock = new object(); private readonly Dictionary _properties = new Dictionary(); private readonly IDatabaseGateway _databaseGateway; private readonly IDataRowMapper _dataRowMapper; - public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMapper) + public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMapper, ISynchronizationService synchronizationService) { _databaseGateway = Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); + _synchronizationService = Guard.AgainstNull(synchronizationService, nameof(synchronizationService)); } public MappedRow MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() @@ -57,15 +60,24 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) - { - var columns = GetColumns(reader); + _synchronizationService.Wait("GetReader", cancellationToken); - if (reader.Read()) + try + { + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - return Map(GetPropertyInfo(), reader, columns); + var columns = GetColumns(reader); + + if (reader.Read()) + { + return Map(GetPropertyInfo(), reader, columns); + } } } + finally + { + _synchronizationService.Release("GetReader"); + } return default; } @@ -74,13 +86,22 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - var columns = GetColumns(reader); + try + { + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - if (await reader.ReadAsync(cancellationToken)) + var columns = GetColumns(reader); + + if (await reader.ReadAsync(cancellationToken)) + { + return Map(GetPropertyInfo(), reader, columns); + } + } + finally { - return Map(GetPropertyInfo(), reader, columns); + _synchronizationService.Release("GetReader"); } return default; @@ -90,50 +111,77 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - var result = new List(); + _synchronizationService.Wait("GetReader", cancellationToken); - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) + try { - var columns = GetColumns(reader); + var result = new List(); - while (reader.Read()) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - result.Add(Map(GetPropertyInfo(), reader, columns)); + var columns = GetColumns(reader); + + while (reader.Read()) + { + result.Add(Map(GetPropertyInfo(), reader, columns)); + } } - } - return result; + return result; + } + finally + { + _synchronizationService.Release("GetReader"); + } } public async Task> MapObjectsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - var result = new List(); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); + + try + { + var result = new List(); + + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + var columns = GetColumns(reader); - var columns = GetColumns(reader); + while (await reader.ReadAsync(cancellationToken)) + { + result.Add(Map(GetPropertyInfo(), reader, columns)); + } - while (await reader.ReadAsync(cancellationToken)) + return result; + } + finally { - result.Add(Map(GetPropertyInfo(), reader, columns)); + _synchronizationService.Release("GetReader"); } - - return result; } public T MapValue(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) + _synchronizationService.Wait("GetReader", cancellationToken); + + try { - while (reader.Read()) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - return MapRowValue(reader); + while (reader.Read()) + { + return MapRowValue(reader); + } } } + finally + { + _synchronizationService.Release("GetReader"); + } return default; } @@ -142,11 +190,20 @@ public async Task MapValueAsync(IQuery query, CancellationToken cancellati { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); + + try + { + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - while (await reader.ReadAsync(cancellationToken)) + while (await reader.ReadAsync(cancellationToken)) + { + return MapRowValue(reader); + } + } + finally { - return MapRowValue(reader); + _synchronizationService.Release("GetReader"); } return default; @@ -158,18 +215,27 @@ public IEnumerable MapValues(IQuery query, CancellationToken cancellationT var result = new List(); - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) + _synchronizationService.Wait("GetReader", cancellationToken); + + try { - while (reader.Read()) + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - var value = MapRowValue(reader); - - if (value != null) + while (reader.Read()) { - result.Add(value); + var value = MapRowValue(reader); + + if (value != null) + { + result.Add(value); + } } } } + finally + { + _synchronizationService.Release("GetReader"); + } return result; } @@ -178,21 +244,30 @@ public async Task> MapValuesAsync(IQuery query, CancellationTo { Guard.AgainstNull(query, nameof(query)); - var result = new List(); - - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - while (await reader.ReadAsync(cancellationToken)) + try { - var value = MapRowValue(reader); + var result = new List(); + + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - if (value != null) + while (await reader.ReadAsync(cancellationToken)) { - result.Add(value); + var value = MapRowValue(reader); + + if (value != null) + { + result.Add(value); + } } - } - return result; + return result; + } + finally + { + _synchronizationService.Release("GetReader"); + } } private static dynamic DynamicMap(IDataRecord record, Dictionary columns) @@ -211,15 +286,24 @@ public dynamic MapItem(IQuery query, CancellationToken cancellationToken = defau { Guard.AgainstNull(query, nameof(query)); - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) - { - var columns = GetColumns(reader); + _synchronizationService.Wait("GetReader", cancellationToken); - if (reader.Read()) + try + { + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - return DynamicMap(reader, columns); + var columns = GetColumns(reader); + + if (reader.Read()) + { + return DynamicMap(reader, columns); + } } } + finally + { + _synchronizationService.Release("GetReader"); + } return default; } @@ -228,12 +312,22 @@ public async Task MapItemAsync(IQuery query, CancellationToken cancella { Guard.AgainstNull(query, nameof(query)); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - var columns = GetColumns(reader); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - if (await reader.ReadAsync(cancellationToken)) + try { - return DynamicMap(reader, columns); + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + + var columns = GetColumns(reader); + + if (await reader.ReadAsync(cancellationToken)) + { + return DynamicMap(reader, columns); + } + } + finally + { + _synchronizationService.Release("GetReader"); } return default; @@ -262,18 +356,27 @@ public async Task> MapItemsAsync(IQuery query, Cancellation { Guard.AgainstNull(query, nameof(query)); - var result = new List(); + await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + try + { + var result = new List(); - var columns = GetColumns(reader); + await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - while (await reader.ReadAsync(cancellationToken)) + var columns = GetColumns(reader); + + while (await reader.ReadAsync(cancellationToken)) + { + result.Add(DynamicMap(reader, columns)); + } + + return result; + } + finally { - result.Add(DynamicMap(reader, columns)); + _synchronizationService.Release("GetReader"); } - - return result; } private IEnumerable GetPropertyInfo() diff --git a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/ServiceCollectionExtensions.cs similarity index 93% rename from Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs rename to Shuttle.Core.Data/ServiceCollectionExtensions.cs index 7ea4f0e..bba605f 100644 --- a/Shuttle.Core.Data/Configuration/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data { @@ -26,6 +27,7 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, }); services.TryAddScoped(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); From 57f76e36ab3e961b06837dd7da01996541aaa79a Mon Sep 17 00:00:00 2001 From: Eben Date: Mon, 8 Jan 2024 08:08:59 +0200 Subject: [PATCH 21/37] - locking GetOpenConnectionAsync() --- Shuttle.Core.Data/DatabaseContext.cs | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index e2cf49b..57fd1f4 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -1,6 +1,7 @@ using System; using System.Data; using System.Data.Common; +using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; using IsolationLevel = System.Data.IsolationLevel; @@ -9,6 +10,7 @@ namespace Shuttle.Core.Data { public class DatabaseContext : IDatabaseContext { + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; private bool _disposed; @@ -64,19 +66,28 @@ private async Task CreateCommandAsync(IQuery query, bool sync) private async Task GetOpenConnectionAsync(bool sync) { - if (Connection.State == ConnectionState.Closed) + await _lock.WaitAsync(CancellationToken.None).ConfigureAwait(false); + + try { - if (sync) - { - ((DbConnection)Connection).Open(); - } - else + if (Connection.State != ConnectionState.Open) { - await ((DbConnection)Connection).OpenAsync().ConfigureAwait(false); + if (sync) + { + ((DbConnection)Connection).Open(); + } + else + { + await ((DbConnection)Connection).OpenAsync().ConfigureAwait(false); + } } - } - return (DbConnection)Connection; + return (DbConnection)Connection; + } + finally + { + _lock.Release(); + } } public bool HasTransaction => Transaction != null; From c9a1c7d63f22236f14468c6558a0a1efd34d2900 Mon Sep 17 00:00:00 2001 From: Eben Date: Thu, 11 Jan 2024 18:00:58 +0200 Subject: [PATCH 22/37] - blocking refactoring --- Shuttle.Core.Data.Tests/AsyncFixture.cs | 9 +- .../DatabaseContextFactoryFixture.cs | 10 +- .../DatabaseContextFixture.cs | 38 +-- .../DatabaseContextServiceFixture.cs | 8 +- Shuttle.Core.Data/ActiveDatabaseContext.cs | 2 +- Shuttle.Core.Data/BlockedDbCommand.cs | 63 +++++ Shuttle.Core.Data/BlockedDbConnection.cs | 24 ++ Shuttle.Core.Data/BlockedDbDataReader.cs | 25 ++ Shuttle.Core.Data/DatabaseContext.cs | 181 +++++++------- Shuttle.Core.Data/DatabaseContextFactory.cs | 33 +-- Shuttle.Core.Data/DatabaseContextService.cs | 32 ++- Shuttle.Core.Data/DatabaseGateway.cs | 37 +-- .../DatabaseContextServiceExtensions.cs | 2 +- Shuttle.Core.Data/IDatabaseContext.cs | 12 +- Shuttle.Core.Data/IDatabaseContextFactory.cs | 1 - Shuttle.Core.Data/IDatabaseContextService.cs | 7 +- Shuttle.Core.Data/IDatabaseGateway.cs | 4 +- Shuttle.Core.Data/QueryMapper.cs | 226 +++++------------- Shuttle.Core.Data/Resources.Designer.cs | 30 +-- Shuttle.Core.Data/Resources.resx | 17 +- .../ServiceCollectionExtensions.cs | 3 +- 21 files changed, 370 insertions(+), 394 deletions(-) create mode 100644 Shuttle.Core.Data/BlockedDbCommand.cs create mode 100644 Shuttle.Core.Data/BlockedDbConnection.cs create mode 100644 Shuttle.Core.Data/BlockedDbDataReader.cs diff --git a/Shuttle.Core.Data.Tests/AsyncFixture.cs b/Shuttle.Core.Data.Tests/AsyncFixture.cs index 59180d8..d80d6a0 100644 --- a/Shuttle.Core.Data.Tests/AsyncFixture.cs +++ b/Shuttle.Core.Data.Tests/AsyncFixture.cs @@ -42,14 +42,11 @@ public async Task Should_be_able_to_use_the_same_database_context_across_synchro private async Task GetRowsAsync(int depth) { - _ = await GetDatabaseGateway().GetRowsAsync(_rowsQuery); - if (depth < 5) { - using (GetDatabaseContext()) - { - await GetRowsAsync(depth + 1); - } + await GetRowsAsync(depth + 1); } + + _ = await GetDatabaseGateway().GetRowsAsync(_rowsQuery); } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index d2ef80a..a5ad932 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -19,17 +19,13 @@ public void Should_be_able_to_create_a_database_context() } [Test] - public void Should_be_able_to_get_an_existing_database_context() + public void Should_not_not_be_able_to_create_another_context_with_the_same_name_as_an_existing_context() { var factory = GetDatabaseContextFactory(); - using (var context = factory.Create(DefaultConnectionStringName)) - using (var existingContext = factory.Create(DefaultConnectionStringName)) + using (factory.Create(DefaultConnectionStringName)) { - Assert.IsNotNull(context); - Assert.IsNotNull(existingContext); - - Assert.AreSame(existingContext.Connection, context.Connection); + Assert.That(()=> factory.Create(DefaultConnectionStringName), Throws.InvalidOperationException); } } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index 25bf606..f302673 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -1,9 +1,11 @@ using System; using System.Data.Common; +using System.Threading; using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Moq; using NUnit.Framework; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data.Tests; @@ -15,7 +17,7 @@ public void Should_not_be_able_to_create_an_invalid_connection() { Assert.Throws(() => { - using (new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) + using (new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { } }); @@ -27,9 +29,9 @@ public void Should_not_be_able_to_create_a_non_existent_connection() Assert.Throws(() => { using (var databaseContext = new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object, new DatabaseContextService())) + new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { - databaseContext.Connection.Open(); + databaseContext.CreateCommand(new Query("select 1")); } }); } @@ -51,7 +53,7 @@ private async Task Should_be_able_to_begin_and_commit_a_transaction_async(bool s using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { if (sync) { @@ -83,7 +85,7 @@ private async Task Should_be_able_to_begin_and_rollback_a_transaction_async(bool using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { if (sync) { @@ -113,7 +115,7 @@ private async Task Should_be_able_to_call_commit_without_a_transaction_async(boo using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { if (sync) { @@ -132,7 +134,7 @@ public void Should_be_able_to_call_dispose_more_than_once() using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService())) + new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { connection.Dispose(); connection.Dispose(); @@ -141,17 +143,6 @@ public void Should_be_able_to_call_dispose_more_than_once() [Test] public void Should_be_able_to_create_a_command() - { - Should_be_able_to_create_a_command_async(true).GetAwaiter().GetResult(); - } - - [Test] - public async Task Should_be_able_to_create_a_command_async() - { - await Should_be_able_to_create_a_command_async(false); - } - - private async Task Should_be_able_to_create_a_command_async(bool sync) { var dbCommandFactory = new Mock(); var dbConnection = GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString); @@ -160,16 +151,9 @@ private async Task Should_be_able_to_create_a_command_async(bool sync) dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); - using (var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) + using (var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) { - if (sync) - { - connection.CreateCommand(query.Object); - } - else - { - await connection.CreateCommandAsync(query.Object); - } + connection.CreateCommand(query.Object); } dbCommandFactory.VerifyAll(); diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index 9781292..a61bde6 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -1,5 +1,5 @@ using System.Data.Common; -using System.Threading.Tasks; +using System.Threading; using Moq; using NUnit.Framework; @@ -12,11 +12,11 @@ public void Should_be_able_to_use_different_contexts() { var service = new DatabaseContextService(); - var context1 = new DatabaseContext("mock-1", "provider-name", new Mock().Object, new Mock().Object, service); + var context1 = new DatabaseContext("mock-1", "provider-name", new Mock().Object, new Mock().Object, service, new SemaphoreSlim(1, 1)); Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - var context2 = new DatabaseContext("mock-2", "provider-name", new Mock().Object, new Mock().Object, service); + var context2 = new DatabaseContext("mock-2", "provider-name", new Mock().Object, new Mock().Object, service, new SemaphoreSlim(1, 1)); Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); @@ -30,7 +30,7 @@ public void Should_be_able_to_use_different_contexts() Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - using (service.Use(context1)) + using (service.Activate(context1)) { Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); } diff --git a/Shuttle.Core.Data/ActiveDatabaseContext.cs b/Shuttle.Core.Data/ActiveDatabaseContext.cs index b80b230..71ba265 100644 --- a/Shuttle.Core.Data/ActiveDatabaseContext.cs +++ b/Shuttle.Core.Data/ActiveDatabaseContext.cs @@ -21,7 +21,7 @@ public void Dispose() return; } - _databaseContextService.Use(_current); + _databaseContextService.Activate(_current); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/BlockedDbCommand.cs b/Shuttle.Core.Data/BlockedDbCommand.cs new file mode 100644 index 0000000..8423d5c --- /dev/null +++ b/Shuttle.Core.Data/BlockedDbCommand.cs @@ -0,0 +1,63 @@ +using System; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; +using Shuttle.Core.Contract; +using Shuttle.Core.Threading; + +namespace Shuttle.Core.Data +{ + public class BlockedDbCommand : IDisposable + { + private readonly SemaphoreSlim _dbDataReaderLock; + private readonly DbCommand _dbCommand; + private readonly IBlockingSemaphore _blockingSemaphore; + + public BlockedDbCommand(DbCommand dbCommand, IBlockingSemaphore blockingSemaphore, SemaphoreSlim dbDataReaderLock) + { + _dbCommand = Guard.AgainstNull(dbCommand, nameof(dbCommand)); + _blockingSemaphore = Guard.AgainstNull(blockingSemaphore, nameof(blockingSemaphore)); + _dbDataReaderLock = Guard.AgainstNull(dbDataReaderLock, nameof(dbDataReaderLock)); + } + + public void Dispose() + { + _dbCommand.Dispose(); + _blockingSemaphore.Release(); + } + + public BlockedDbDataReader ExecuteReader() + { + _dbDataReaderLock.Wait(CancellationToken.None); + + return new BlockedDbDataReader(_dbCommand.ExecuteReader(), new BlockingSemaphoreSlim(_dbDataReaderLock)); + } + + public async Task ExecuteReaderAsync(CancellationToken cancellationToken) + { + await _dbDataReaderLock.WaitAsync(cancellationToken); + + return new BlockedDbDataReader(await _dbCommand.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false), new BlockingSemaphoreSlim(_dbDataReaderLock)); + } + + public int ExecuteNonQuery() + { + return _dbCommand.ExecuteNonQuery(); + } + + public async Task ExecuteNonQueryAsync(CancellationToken cancellationToken) + { + return await _dbCommand.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + } + + public object ExecuteScalar() + { + return _dbCommand.ExecuteScalar(); + } + + public async Task ExecuteScalarAsync(CancellationToken cancellationToken) + { + return await _dbCommand.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/BlockedDbConnection.cs b/Shuttle.Core.Data/BlockedDbConnection.cs new file mode 100644 index 0000000..49f5b33 --- /dev/null +++ b/Shuttle.Core.Data/BlockedDbConnection.cs @@ -0,0 +1,24 @@ +using System; +using System.Data; +using Shuttle.Core.Contract; +using Shuttle.Core.Threading; + +namespace Shuttle.Core.Data +{ + public class BlockedDbConnection : IDisposable + { + public IDbConnection DbConnection { get; } + private readonly IBlockingSemaphore _blockingSemaphore; + + public BlockedDbConnection(IDbConnection dbConnection, IBlockingSemaphore blockingSemaphore) + { + DbConnection = Guard.AgainstNull(dbConnection, nameof(dbConnection)); + _blockingSemaphore = Guard.AgainstNull(blockingSemaphore, nameof(blockingSemaphore)); + } + + public void Dispose() + { + _blockingSemaphore.Release(); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/BlockedDbDataReader.cs b/Shuttle.Core.Data/BlockedDbDataReader.cs new file mode 100644 index 0000000..87d009d --- /dev/null +++ b/Shuttle.Core.Data/BlockedDbDataReader.cs @@ -0,0 +1,25 @@ +using System; +using System.Data.Common; +using Shuttle.Core.Contract; +using Shuttle.Core.Threading; + +namespace Shuttle.Core.Data +{ + public class BlockedDbDataReader : IDisposable + { + private readonly IBlockingSemaphore _blockingSemaphore; + public DbDataReader DbDataReader { get; } + + public BlockedDbDataReader(DbDataReader dbDataReader, IBlockingSemaphore blockingSemaphore) + { + DbDataReader = Guard.AgainstNull(dbDataReader, nameof(dbDataReader)); + _blockingSemaphore = Guard.AgainstNull(blockingSemaphore, nameof(blockingSemaphore)); + } + + public void Dispose() + { + DbDataReader.Dispose(); + _blockingSemaphore.Release(); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index 57fd1f4..bf63c48 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -4,104 +4,136 @@ using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; -using IsolationLevel = System.Data.IsolationLevel; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class DatabaseContext : IDatabaseContext { - private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _semaphore; + private bool _disposed; + private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; - private bool _disposed; - private readonly IDatabaseContext _activeContext; + private readonly SemaphoreSlim _dbCommandLock = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _dbDataReaderLock = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _dbConnectionLock = new SemaphoreSlim(1, 1); + private readonly IDbConnection _dbConnection; - public DatabaseContext(string name, string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) + public DatabaseContext(string name, string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService, SemaphoreSlim semaphore) { Name = Guard.AgainstNullOrEmptyString(name, nameof(name)); _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + _semaphore= Guard.AgainstNull(semaphore, nameof(semaphore)); Key = Guid.NewGuid(); ProviderName = Guard.AgainstNullOrEmptyString(providerName, "providerName"); - Connection = Guard.AgainstNull(dbConnection, nameof(dbConnection)); - - _activeContext = databaseContextService.HasCurrent ? databaseContextService.Current : null; + _dbConnection = Guard.AgainstNull(dbConnection, nameof(dbConnection)); _databaseContextService.Add(this); } public event EventHandler TransactionStarted; public event EventHandler TransactionCommitted; - public event EventHandler Disposed; - public event EventHandler DisposeIgnored; public event EventHandler TransactionRolledBack; + public event EventHandler Disposed; public Guid Key { get; } public string Name { get; } - public int ReferenceCount { get; private set; } = 1; public IDbTransaction Transaction { get; private set; } public string ProviderName { get; } - public IDbConnection Connection { get; private set; } - public IDbCommand CreateCommand(IQuery query) + private void GuardDisposed() { - return CreateCommandAsync(query, true).GetAwaiter().GetResult(); - } + if (!_disposed) + { + return; + } - public async Task CreateCommandAsync(IQuery query) - { - return await CreateCommandAsync(query, false).ConfigureAwait(false); + throw new ObjectDisposedException(nameof(DatabaseContext)); } - private async Task CreateCommandAsync(IQuery query, bool sync) + public BlockedDbCommand CreateCommand(IQuery query) { - var command = _dbCommandFactory.Create(sync ? GetOpenConnectionAsync(true).GetAwaiter().GetResult() : await GetOpenConnectionAsync(false).ConfigureAwait(false), Guard.AgainstNull(query, nameof(query))); - + GuardDisposed(); + + _dbCommandLock.Wait(CancellationToken.None); + + var command = _dbCommandFactory.Create(GetOpenConnectionAsync(true).GetAwaiter().GetResult(), Guard.AgainstNull(query, nameof(query))); + command.Transaction = Transaction; - return command; + return new BlockedDbCommand((DbCommand)command, new BlockingSemaphoreSlim(_dbCommandLock), _dbDataReaderLock); } - private async Task GetOpenConnectionAsync(bool sync) + public BlockedDbConnection GetBlockedDbConnection() { - await _lock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - - try - { - if (Connection.State != ConnectionState.Open) - { - if (sync) - { - ((DbConnection)Connection).Open(); - } - else - { - await ((DbConnection)Connection).OpenAsync().ConfigureAwait(false); - } - } + _dbConnectionLock.Wait(CancellationToken.None); - return (DbConnection)Connection; - } - finally - { - _lock.Release(); - } + return new BlockedDbConnection(GetOpenConnectionAsync(true).GetAwaiter().GetResult(), new BlockingSemaphoreSlim(_dbConnectionLock)); } public bool HasTransaction => Transaction != null; public IDatabaseContext BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { + GuardDisposed(); + return BeginTransactionAsync(isolationLevel, true).GetAwaiter().GetResult(); } public async Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { + GuardDisposed(); + return await BeginTransactionAsync(isolationLevel, false).ConfigureAwait(false); } + public void CommitTransaction() + { + GuardDisposed(); + + CommitTransactionAsync(true).GetAwaiter().GetResult(); + } + + public async Task CommitTransactionAsync() + { + await CommitTransactionAsync(false).ConfigureAwait(false); + } + + public void Dispose() + { + if (_disposed) + { + return; + } + + _semaphore.Wait(); + + try + { + _databaseContextService.Remove(this); + + if (HasTransaction) + { + Transaction.Rollback(); + + TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); + } + + _dbConnection.Dispose(); + _disposed = true; + } + finally + { + _semaphore.Release(); + } + + Disposed?.Invoke(this, EventArgs.Empty); + } + private async Task BeginTransactionAsync(IsolationLevel isolationLevel, bool sync) { if (HasTransaction || System.Transactions.Transaction.Current != null) @@ -123,23 +155,6 @@ private async Task BeginTransactionAsync(IsolationLevel isolat return this; } - public void CommitTransaction() - { - CommitTransactionAsync(true).GetAwaiter().GetResult(); - } - - public async Task CommitTransactionAsync() - { - await CommitTransactionAsync(false).ConfigureAwait(false); - } - - public IDatabaseContext Referenced() - { - ReferenceCount++; - - return this; - } - private async Task CommitTransactionAsync(bool sync) { if (!HasTransaction) @@ -161,43 +176,21 @@ private async Task CommitTransactionAsync(bool sync) Transaction = null; } - public void Dispose() + private async Task GetOpenConnectionAsync(bool sync) { - if (_disposed) - { - return; - } - - ReferenceCount--; - - if (ReferenceCount > 0) - { - DisposeIgnored?.Invoke(this, EventArgs.Empty); - - return; - } - - _databaseContextService.Remove(this); - - if (_activeContext != null && _databaseContextService.Contains(_activeContext)) + if (_dbConnection.State != ConnectionState.Open) { - _databaseContextService.Use(_activeContext); - } - - if (HasTransaction) - { - Transaction.Rollback(); - - TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); + if (sync) + { + ((DbConnection)_dbConnection).Open(); + } + else + { + await ((DbConnection)_dbConnection).OpenAsync().ConfigureAwait(false); + } } - Connection.Dispose(); - - Connection = null; - Transaction = null; - _disposed = true; - - Disposed?.Invoke(this, EventArgs.Empty); + return (DbConnection)_dbConnection; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index add0ab2..f94ed6d 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -1,12 +1,15 @@ using System; using System.Data.Common; +using System.Threading; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; +using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class DatabaseContextFactory : IDatabaseContextFactory { + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1,1); private readonly IOptionsMonitor _connectionStringOptions; private readonly DataAccessOptions _dataAccessOptions; @@ -23,35 +26,37 @@ public DatabaseContextFactory(IOptionsMonitor connectio } public event EventHandler DatabaseContextCreated; - public event EventHandler DatabaseContextReferenced; public IDatabaseContext Create(string name) { Guard.AgainstNullOrEmptyString(name, nameof(name)); - var connectionStringOptions = _connectionStringOptions.Get(name); + _lock.Wait(); - if (connectionStringOptions == null || string.IsNullOrEmpty(connectionStringOptions.Name)) + try { - throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, name)); - } + var connectionStringOptions = _connectionStringOptions.Get(name); - if (DatabaseContextService.Contains(name)) - { - var databaseContext = DatabaseContextService.Get(name).Referenced(); + if (connectionStringOptions == null || string.IsNullOrEmpty(connectionStringOptions.Name)) + { + throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, name)); + } - DatabaseContextReferenced?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); + if (DatabaseContextService.Contains(name)) + { + throw new InvalidOperationException(string.Format(Resources.DuplicateDatabaseContextException, name)); + } - return databaseContext; - } - else - { - var databaseContext = new DatabaseContext(name, connectionStringOptions.ProviderName, (DbConnection)DbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), DbCommandFactory, DatabaseContextService); + var databaseContext = new DatabaseContext(name, connectionStringOptions.ProviderName, (DbConnection)DbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), DbCommandFactory, DatabaseContextService, _lock); DatabaseContextCreated?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); return databaseContext; } + finally + { + _lock.Release(); + } } public IDbConnectionFactory DbConnectionFactory { get; } diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 6dcd6ef..8fcb7bf 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Shuttle.Core.Contract; using Shuttle.Core.Threading; @@ -12,11 +13,9 @@ public class DatabaseContextService : IDatabaseContextService private class AmbientData { public readonly List DatabaseContexts = new List(); - public IDatabaseContext Current = null; + public IDatabaseContext ActiveDatabaseContext = null; } - public bool HasCurrent => GetAmbientData().Current != null; - private AmbientData GetAmbientData() { var result = AmbientContext.GetData(AmbientDataKey) as AmbientData; @@ -35,28 +34,35 @@ public IDatabaseContext Current { get { - if (GetAmbientData().Current == null) + if (GetAmbientData().ActiveDatabaseContext == null) { throw new InvalidOperationException(Resources.DatabaseContextMissing); } - return GetAmbientData().Current; + return GetAmbientData().ActiveDatabaseContext; } } - public ActiveDatabaseContext Use(IDatabaseContext context) + public ActiveDatabaseContext Activate(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); - var current = GetAmbientData().Current; + var current = GetAmbientData().ActiveDatabaseContext; - GetAmbientData().Current = GetAmbientData().DatabaseContexts.Find(candidate => candidate.Key.Equals(context.Key)); + if (current != null && current.Name.Equals(context.Name)) + { + throw new Exception(string.Format(Resources.DatabaseContextAlreadyActiveException, context.Name)); + } - if (GetAmbientData().Current == null) + var active = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(context.Name, StringComparison.InvariantCultureIgnoreCase)); + + if (active == null) { - throw new Exception(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key)); + throw new Exception(string.Format(Resources.DatabaseContextNotFoundException, context.Name)); } + GetAmbientData().ActiveDatabaseContext = active; + return new ActiveDatabaseContext(this, current); } @@ -71,7 +77,7 @@ public void Add(IDatabaseContext context) GetAmbientData().DatabaseContexts.Add(context); - Use(context); + Activate(context); } public void Remove(IDatabaseContext context) @@ -85,9 +91,9 @@ public void Remove(IDatabaseContext context) return; } - if (GetAmbientData().Current != null && candidate.Key.Equals(GetAmbientData().Current.Key)) + if (GetAmbientData().ActiveDatabaseContext != null && candidate.Key.Equals(GetAmbientData().ActiveDatabaseContext.Key)) { - GetAmbientData().Current = null; + GetAmbientData().ActiveDatabaseContext = null; } GetAmbientData().DatabaseContexts.Remove(candidate); diff --git a/Shuttle.Core.Data/DatabaseGateway.cs b/Shuttle.Core.Data/DatabaseGateway.cs index 0fd9015..071d3c7 100644 --- a/Shuttle.Core.Data/DatabaseGateway.cs +++ b/Shuttle.Core.Data/DatabaseGateway.cs @@ -1,24 +1,20 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Linq; using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; -using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class DatabaseGateway : IDatabaseGateway { - private readonly ISynchronizationService _synchronizationService; private readonly IDatabaseContextService _databaseContextService; - public DatabaseGateway(IDatabaseContextService databaseContextService, ISynchronizationService synchronizationService) + public DatabaseGateway(IDatabaseContextService databaseContextService) { _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - _synchronizationService = synchronizationService; } public DataTable GetDataTable(IQuery query, CancellationToken cancellationToken = default) @@ -35,23 +31,14 @@ private async Task GetDataTableAsync(IQuery query, CancellationToken { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); + var results = new DataTable(); - try + using (var reader = sync ? GetReader(query, cancellationToken) : await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) { - var results = new DataTable(); - - using (var reader = sync ? GetReader(query, cancellationToken) : await GetReaderAsync(query, cancellationToken).ConfigureAwait(false)) - { - results.Load(reader); - } - - return results; - } - finally - { - _synchronizationService.Release("GetReader"); + results.Load(reader.DbDataReader); } + + return results; } public IEnumerable GetRows(IQuery query, CancellationToken cancellationToken = default) @@ -87,21 +74,21 @@ private async Task GetRowAsync(IQuery query, CancellationToken cancella return table.Rows[0]; } - public IDataReader GetReader(IQuery query, CancellationToken cancellationToken = default) + public BlockedDbDataReader GetReader(IQuery query, CancellationToken cancellationToken = default) { return GetReaderAsync(query, cancellationToken, true).GetAwaiter().GetResult(); } - public async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default) + public async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default) { return await GetReaderAsync(query, cancellationToken, false).ConfigureAwait(false); } - private async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken, bool sync) + private async Task GetReaderAsync(IQuery query, CancellationToken cancellationToken, bool sync) { Guard.AgainstNull(query, nameof(query)); - using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) + using (var command = _databaseContextService.Current.CreateCommand(query)) { return sync ? command.ExecuteReader() : await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); } @@ -121,7 +108,7 @@ private async Task ExecuteAsync(IQuery query, CancellationToken cancellatio { Guard.AgainstNull(query, nameof(query)); - using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) + using (var command = _databaseContextService.Current.CreateCommand(query)) { return sync ? command.ExecuteNonQuery() : await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); } @@ -141,7 +128,7 @@ public async Task GetScalarAsync(IQuery query, CancellationToken cancellat { Guard.AgainstNull(query, nameof(query)); - using (var command = (DbCommand)(sync ? _databaseContextService.Current.CreateCommand(query) : await _databaseContextService.Current.CreateCommandAsync(query).ConfigureAwait(false))) + using (var command = _databaseContextService.Current.CreateCommand(query)) { var scalar = sync ? command.ExecuteScalar() : await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs index f3a0e7c..2cc4f6a 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -35,7 +35,7 @@ public static ActiveDatabaseContext Use(this IDatabaseContextService databaseCon Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextService.Use(databaseContextService.Get(name)); + return databaseContextService.Activate(databaseContextService.Get(name)); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index c210a74..25b978a 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Threading; using System.Threading.Tasks; using IsolationLevel = System.Data.IsolationLevel; @@ -9,17 +10,15 @@ public interface IDatabaseContext : IDisposable { event EventHandler TransactionStarted; event EventHandler TransactionCommitted; + event EventHandler TransactionRolledBack; event EventHandler Disposed; - event EventHandler DisposeIgnored; Guid Key { get; } string Name { get; } - int ReferenceCount { get; } - IDbTransaction Transaction { get; } - IDbConnection Connection { get; } - IDbCommand CreateCommand(IQuery query); - Task CreateCommandAsync(IQuery query); + IDbTransaction Transaction { get; } + BlockedDbCommand CreateCommand(IQuery query); + BlockedDbConnection GetBlockedDbConnection(); bool HasTransaction { get; } string ProviderName { get; } @@ -28,6 +27,5 @@ public interface IDatabaseContext : IDisposable Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified); void CommitTransaction(); Task CommitTransactionAsync(); - IDatabaseContext Referenced(); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index 88deb07..b0c7480 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -5,7 +5,6 @@ namespace Shuttle.Core.Data public interface IDatabaseContextFactory { event EventHandler DatabaseContextCreated; - event EventHandler DatabaseContextReferenced; IDatabaseContext Create(string name); IDatabaseContext Create(); diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs index 122d1e2..457f429 100644 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -1,14 +1,15 @@ using System; +using System.Data; +using System.Data.Common; namespace Shuttle.Core.Data { public interface IDatabaseContextService { - bool HasCurrent { get; } - IDatabaseContext Current { get; } - ActiveDatabaseContext Use(IDatabaseContext context); + ActiveDatabaseContext Activate(IDatabaseContext context); void Add(IDatabaseContext context); void Remove(IDatabaseContext context); IDatabaseContext Find(Predicate match); + IDatabaseContext Current { get; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseGateway.cs b/Shuttle.Core.Data/IDatabaseGateway.cs index a8b50fb..4b2883a 100644 --- a/Shuttle.Core.Data/IDatabaseGateway.cs +++ b/Shuttle.Core.Data/IDatabaseGateway.cs @@ -7,14 +7,14 @@ namespace Shuttle.Core.Data { public interface IDatabaseGateway { - IDataReader GetReader(IQuery query, CancellationToken cancellationToken = default); + BlockedDbDataReader GetReader(IQuery query, CancellationToken cancellationToken = default); int Execute(IQuery query, CancellationToken cancellationToken = default); T GetScalar(IQuery query, CancellationToken cancellationToken = default); DataTable GetDataTable(IQuery query, CancellationToken cancellationToken = default); IEnumerable GetRows(IQuery query, CancellationToken cancellationToken = default); DataRow GetRow(IQuery query, CancellationToken cancellationToken = default); - Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default); + Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default); Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default); Task GetScalarAsync(IQuery query, CancellationToken cancellationToken = default); Task GetDataTableAsync(IQuery query, CancellationToken cancellationToken = default); diff --git a/Shuttle.Core.Data/QueryMapper.cs b/Shuttle.Core.Data/QueryMapper.cs index 04019ed..d5622f5 100644 --- a/Shuttle.Core.Data/QueryMapper.cs +++ b/Shuttle.Core.Data/QueryMapper.cs @@ -8,24 +8,21 @@ using System.Threading; using System.Threading.Tasks; using Shuttle.Core.Contract; -using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class QueryMapper : IQueryMapper { - private readonly ISynchronizationService _synchronizationService; private readonly object _lock = new object(); private readonly Dictionary _properties = new Dictionary(); private readonly IDatabaseGateway _databaseGateway; private readonly IDataRowMapper _dataRowMapper; - public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMapper, ISynchronizationService synchronizationService) + public QueryMapper(IDatabaseContextService databaseContextService, IDatabaseGateway databaseGateway, IDataRowMapper dataRowMapper) { _databaseGateway = Guard.AgainstNull(databaseGateway, nameof(databaseGateway)); _dataRowMapper = Guard.AgainstNull(dataRowMapper, nameof(dataRowMapper)); - _synchronizationService = Guard.AgainstNull(synchronizationService, nameof(synchronizationService)); } public MappedRow MapRow(IQuery query, CancellationToken cancellationToken = default) where T : new() @@ -60,24 +57,15 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - _synchronizationService.Wait("GetReader", cancellationToken); - - try + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) - { - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - if (reader.Read()) - { - return Map(GetPropertyInfo(), reader, columns); - } + if (reader.DbDataReader.Read()) + { + return Map(GetPropertyInfo(), reader.DbDataReader, columns); } } - finally - { - _synchronizationService.Release("GetReader"); - } return default; } @@ -86,22 +74,13 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); + using var reader = await _databaseGateway.GetReaderAsync(query, cancellationToken); - try - { - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - if (await reader.ReadAsync(cancellationToken)) - { - return Map(GetPropertyInfo(), reader, columns); - } - } - finally + if (await reader.DbDataReader.ReadAsync(cancellationToken)) { - _synchronizationService.Release("GetReader"); + return Map(GetPropertyInfo(), reader.DbDataReader, columns); } return default; @@ -111,77 +90,50 @@ public QueryMapper(IDatabaseGateway databaseGateway, IDataRowMapper dataRowMappe { Guard.AgainstNull(query, nameof(query)); - _synchronizationService.Wait("GetReader", cancellationToken); - - try - { var result = new List(); using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - while (reader.Read()) + while (reader.DbDataReader.Read()) { - result.Add(Map(GetPropertyInfo(), reader, columns)); + result.Add(Map(GetPropertyInfo(), reader.DbDataReader, columns)); } } return result; - } - finally - { - _synchronizationService.Release("GetReader"); - } } public async Task> MapObjectsAsync(IQuery query, CancellationToken cancellationToken = default) where T : new() { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - - try - { - var result = new List(); + var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + using var reader = await _databaseGateway.GetReaderAsync(query, cancellationToken); - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - while (await reader.ReadAsync(cancellationToken)) - { - result.Add(Map(GetPropertyInfo(), reader, columns)); - } - - return result; - } - finally + while (await reader.DbDataReader.ReadAsync(cancellationToken)) { - _synchronizationService.Release("GetReader"); + result.Add(Map(GetPropertyInfo(), reader.DbDataReader, columns)); } + + return result; } public T MapValue(IQuery query, CancellationToken cancellationToken = default) { Guard.AgainstNull(query, nameof(query)); - _synchronizationService.Wait("GetReader", cancellationToken); - - try + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) + while (reader.DbDataReader.Read()) { - while (reader.Read()) - { - return MapRowValue(reader); - } + return MapRowValue(reader); } } - finally - { - _synchronizationService.Release("GetReader"); - } return default; } @@ -190,20 +142,11 @@ public async Task MapValueAsync(IQuery query, CancellationToken cancellati { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); + using var reader = await _databaseGateway.GetReaderAsync(query, cancellationToken); - try + while (await reader.DbDataReader.ReadAsync(cancellationToken)) { - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); - - while (await reader.ReadAsync(cancellationToken)) - { - return MapRowValue(reader); - } - } - finally - { - _synchronizationService.Release("GetReader"); + return MapRowValue(reader); } return default; @@ -215,27 +158,18 @@ public IEnumerable MapValues(IQuery query, CancellationToken cancellationT var result = new List(); - _synchronizationService.Wait("GetReader", cancellationToken); - - try + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) + while (reader.DbDataReader.Read()) { - while (reader.Read()) - { - var value = MapRowValue(reader); + var value = MapRowValue(reader); - if (value != null) - { - result.Add(value); - } + if (value != null) + { + result.Add(value); } } } - finally - { - _synchronizationService.Release("GetReader"); - } return result; } @@ -244,30 +178,21 @@ public async Task> MapValuesAsync(IQuery query, CancellationTo { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); + var result = new List(); - try - { - var result = new List(); + using var reader = await _databaseGateway.GetReaderAsync(query, cancellationToken); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + while (await reader.DbDataReader.ReadAsync(cancellationToken)) + { + var value = MapRowValue(reader); - while (await reader.ReadAsync(cancellationToken)) + if (value != null) { - var value = MapRowValue(reader); - - if (value != null) - { - result.Add(value); - } + result.Add(value); } - - return result; - } - finally - { - _synchronizationService.Release("GetReader"); } + + return result; } private static dynamic DynamicMap(IDataRecord record, Dictionary columns) @@ -286,24 +211,15 @@ public dynamic MapItem(IQuery query, CancellationToken cancellationToken = defau { Guard.AgainstNull(query, nameof(query)); - _synchronizationService.Wait("GetReader", cancellationToken); - - try + using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - using (var reader = _databaseGateway.GetReader(query, cancellationToken)) - { - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - if (reader.Read()) - { - return DynamicMap(reader, columns); - } + if (reader.DbDataReader.Read()) + { + return DynamicMap(reader.DbDataReader, columns); } } - finally - { - _synchronizationService.Release("GetReader"); - } return default; } @@ -312,22 +228,13 @@ public async Task MapItemAsync(IQuery query, CancellationToken cancella { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - - try - { - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + using var reader = await _databaseGateway.GetReaderAsync(query, cancellationToken); - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - if (await reader.ReadAsync(cancellationToken)) - { - return DynamicMap(reader, columns); - } - } - finally + if (await reader.DbDataReader.ReadAsync(cancellationToken)) { - _synchronizationService.Release("GetReader"); + return DynamicMap(reader.DbDataReader, columns); } return default; @@ -341,11 +248,11 @@ public IEnumerable MapItems(IQuery query, CancellationToken cancellatio using (var reader = _databaseGateway.GetReader(query, cancellationToken)) { - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - while (reader.Read()) + while (reader.DbDataReader.Read()) { - result.Add(DynamicMap(reader, columns)); + result.Add(DynamicMap(reader.DbDataReader, columns)); } } @@ -356,27 +263,18 @@ public async Task> MapItemsAsync(IQuery query, Cancellation { Guard.AgainstNull(query, nameof(query)); - await _synchronizationService.WaitAsync("GetReader", cancellationToken).ConfigureAwait(false); - - try - { - var result = new List(); + var result = new List(); - await using var reader = (DbDataReader)await _databaseGateway.GetReaderAsync(query, cancellationToken); + using var reader = await _databaseGateway.GetReaderAsync(query, cancellationToken); - var columns = GetColumns(reader); + var columns = GetColumns(reader.DbDataReader); - while (await reader.ReadAsync(cancellationToken)) - { - result.Add(DynamicMap(reader, columns)); - } - - return result; - } - finally + while (await reader.DbDataReader.ReadAsync(cancellationToken)) { - _synchronizationService.Release("GetReader"); + result.Add(DynamicMap(reader.DbDataReader, columns)); } + + return result; } private IEnumerable GetPropertyInfo() @@ -434,11 +332,11 @@ private static Dictionary GetColumns(IDataRecord record) return Enumerable.Range(0, record.FieldCount).ToDictionary(item => record.GetName(item) ?? $"__missing_column_name:{item}", item => item); } - private static T MapRowValue(IDataReader dataReader) + private static T MapRowValue(BlockedDbDataReader reader) { var underlyingSystemType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); - var value = dataReader.GetValue(0); + var value = reader.DbDataReader.GetValue(0); return value == null ? default diff --git a/Shuttle.Core.Data/Resources.Designer.cs b/Shuttle.Core.Data/Resources.Designer.cs index 2a25fb8..4d397d3 100644 --- a/Shuttle.Core.Data/Resources.Designer.cs +++ b/Shuttle.Core.Data/Resources.Designer.cs @@ -79,20 +79,20 @@ public static string ConnectionStringMissingException { } /// - /// Looks up a localized string similar to DatabaseContextFactoryOptions have not been configured. Cannot call 'Create()' directly.. + /// Looks up a localized string similar to Database context with name '{0}' is already active.. /// - public static string DatabaseContextFactoryOptionsException { + public static string DatabaseContextAlreadyActiveException { get { - return ResourceManager.GetString("DatabaseContextFactoryOptionsException", resourceCulture); + return ResourceManager.GetString("DatabaseContextAlreadyActiveException", resourceCulture); } } /// - /// Looks up a localized string similar to Attempt to retrieve non-existent database context with key '{0}'.. + /// Looks up a localized string similar to DatabaseContextFactoryOptions have not been configured. Cannot call 'Create()' directly.. /// - public static string DatabaseContextKeyNotFoundException { + public static string DatabaseContextFactoryOptionsException { get { - return ResourceManager.GetString("DatabaseContextKeyNotFoundException", resourceCulture); + return ResourceManager.GetString("DatabaseContextFactoryOptionsException", resourceCulture); } } @@ -108,15 +108,6 @@ public static string DatabaseContextMissing { /// /// Looks up a localized string similar to Attempt to retrieve non-existent database context with name '{0}'.. /// - public static string DatabaseContextNameNotFoundException { - get { - return ResourceManager.GetString("DatabaseContextNameNotFoundException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Attempt to retrieve non-existent database context.. - /// public static string DatabaseContextNotFoundException { get { return ResourceManager.GetString("DatabaseContextNotFoundException", resourceCulture); @@ -141,6 +132,15 @@ public static string DbTypeMappingException { } } + /// + /// Looks up a localized string similar to A database context with name '{0}' has already been created.. + /// + public static string DuplicateDatabaseContextException { + get { + return ResourceManager.GetString("DuplicateDatabaseContextException", resourceCulture); + } + } + /// /// Looks up a localized string similar to There is already a database context with key '{0}' in the IDatabaseContextCache.. /// diff --git a/Shuttle.Core.Data/Resources.resx b/Shuttle.Core.Data/Resources.resx index 3ddfc61..91ebd11 100644 --- a/Shuttle.Core.Data/Resources.resx +++ b/Shuttle.Core.Data/Resources.resx @@ -124,20 +124,13 @@ Could not create a connection from provider factory '{0}'. {0} = provider name - - Attempt to retrieve non-existent database context. - DatabaseContextFactoryOptions have not been configured. Cannot call 'Create()' directly. - - Attempt to retrieve non-existent database context with key '{0}'. - {0} = database context key - There is no active database context. Use the database context factory to create a context. - + Attempt to retrieve non-existent database context with name '{0}'. {0} = database context name @@ -179,4 +172,12 @@ Could not retrieve the value for property `{0}` from `parameters`. {0} = property name + + A database context with name '{0}' has already been created. + {0} = name + + + Database context with name '{0}' is already active. + {0} = database context name + \ No newline at end of file diff --git a/Shuttle.Core.Data/ServiceCollectionExtensions.cs b/Shuttle.Core.Data/ServiceCollectionExtensions.cs index bba605f..b8ecdba 100644 --- a/Shuttle.Core.Data/ServiceCollectionExtensions.cs +++ b/Shuttle.Core.Data/ServiceCollectionExtensions.cs @@ -26,8 +26,7 @@ public static IServiceCollection AddDataAccess(this IServiceCollection services, options.DatabaseContextFactory = dataAccessBuilder.Options.DatabaseContextFactory; }); - services.TryAddScoped(); - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); From de6b7eefc1a2f2f2c2e34d7e858282e56bb560dc Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 12 Jan 2024 08:36:02 +0200 Subject: [PATCH 23/37] - removed database context semaphore --- .../DatabaseContextFixture.cs | 15 +++--- .../DatabaseContextServiceFixture.cs | 5 +- Shuttle.Core.Data/.package/package.nuspec | 2 +- Shuttle.Core.Data/DatabaseContext.cs | 54 ++++++++----------- Shuttle.Core.Data/DatabaseContextFactory.cs | 2 +- Shuttle.Core.Data/Properties/AssemblyInfo.cs | 4 +- 6 files changed, 34 insertions(+), 48 deletions(-) diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs index f302673..6b84928 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFixture.cs @@ -1,6 +1,5 @@ using System; using System.Data.Common; -using System.Threading; using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Moq; @@ -17,7 +16,7 @@ public void Should_not_be_able_to_create_an_invalid_connection() { Assert.Throws(() => { - using (new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + using (new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("```"), new Mock().Object, new DatabaseContextService())) { } }); @@ -29,7 +28,7 @@ public void Should_not_be_able_to_create_a_non_existent_connection() Assert.Throws(() => { using (var databaseContext = new DatabaseContext("context", "Microsoft.Data.SqlClient", new SqlConnection("data source=.;initial catalog=idontexist;integrated security=sspi"), - new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + new Mock().Object, new DatabaseContextService())) { databaseContext.CreateCommand(new Query("select 1")); } @@ -53,7 +52,7 @@ private async Task Should_be_able_to_begin_and_commit_a_transaction_async(bool s using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + new Mock().Object, new DatabaseContextService())) { if (sync) { @@ -85,7 +84,7 @@ private async Task Should_be_able_to_begin_and_rollback_a_transaction_async(bool using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + new Mock().Object, new DatabaseContextService())) { if (sync) { @@ -115,7 +114,7 @@ private async Task Should_be_able_to_call_commit_without_a_transaction_async(boo using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + new Mock().Object, new DatabaseContextService())) { if (sync) { @@ -134,7 +133,7 @@ public void Should_be_able_to_call_dispose_more_than_once() using ( var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", GetDbConnectionFactory().Create(DefaultProviderName, DefaultConnectionString), - new Mock().Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + new Mock().Object, new DatabaseContextService())) { connection.Dispose(); connection.Dispose(); @@ -151,7 +150,7 @@ public void Should_be_able_to_create_a_command() dbCommandFactory.Setup(m => m.Create(dbConnection, query.Object)).Returns(dbCommand.Object); - using (var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService(), new SemaphoreSlim(1, 1))) + using (var connection = new DatabaseContext("context", "Microsoft.Data.SqlClient", dbConnection, dbCommandFactory.Object, new DatabaseContextService())) { connection.CreateCommand(query.Object); } diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index a61bde6..ce218db 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -1,5 +1,4 @@ using System.Data.Common; -using System.Threading; using Moq; using NUnit.Framework; @@ -12,11 +11,11 @@ public void Should_be_able_to_use_different_contexts() { var service = new DatabaseContextService(); - var context1 = new DatabaseContext("mock-1", "provider-name", new Mock().Object, new Mock().Object, service, new SemaphoreSlim(1, 1)); + var context1 = new DatabaseContext("mock-1", "provider-name", new Mock().Object, new Mock().Object, service); Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - var context2 = new DatabaseContext("mock-2", "provider-name", new Mock().Object, new Mock().Object, service, new SemaphoreSlim(1, 1)); + var context2 = new DatabaseContext("mock-2", "provider-name", new Mock().Object, new Mock().Object, service); Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index cb914c4..026d4ac 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -3,7 +3,7 @@ Shuttle.Core.Data - 15.0.0 + 15.0.1 Eben Roux Eben Roux BSD-3-Clause diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index bf63c48..b288642 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -10,22 +10,19 @@ namespace Shuttle.Core.Data { public class DatabaseContext : IDatabaseContext { - private readonly SemaphoreSlim _semaphore; - private bool _disposed; - private readonly IDatabaseContextService _databaseContextService; private readonly IDbCommandFactory _dbCommandFactory; private readonly SemaphoreSlim _dbCommandLock = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim _dbDataReaderLock = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim _dbConnectionLock = new SemaphoreSlim(1, 1); private readonly IDbConnection _dbConnection; + private readonly SemaphoreSlim _dbConnectionLock = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _dbDataReaderLock = new SemaphoreSlim(1, 1); + private bool _disposed; - public DatabaseContext(string name, string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService, SemaphoreSlim semaphore) + public DatabaseContext(string name, string providerName, IDbConnection dbConnection, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) { Name = Guard.AgainstNullOrEmptyString(name, nameof(name)); _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - _semaphore= Guard.AgainstNull(semaphore, nameof(semaphore)); Key = Guid.NewGuid(); @@ -45,16 +42,6 @@ public DatabaseContext(string name, string providerName, IDbConnection dbConnect public IDbTransaction Transaction { get; private set; } public string ProviderName { get; } - private void GuardDisposed() - { - if (!_disposed) - { - return; - } - - throw new ObjectDisposedException(nameof(DatabaseContext)); - } - public BlockedDbCommand CreateCommand(IQuery query) { GuardDisposed(); @@ -110,27 +97,18 @@ public void Dispose() return; } - _semaphore.Wait(); + _databaseContextService.Remove(this); - try + if (HasTransaction) { - _databaseContextService.Remove(this); + Transaction.Rollback(); - if (HasTransaction) - { - Transaction.Rollback(); - - TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); - } - - _dbConnection.Dispose(); - _disposed = true; - } - finally - { - _semaphore.Release(); + TransactionRolledBack?.Invoke(this, new TransactionEventArgs(Transaction)); } + _dbConnection.Dispose(); + _disposed = true; + Disposed?.Invoke(this, EventArgs.Empty); } @@ -192,5 +170,15 @@ private async Task GetOpenConnectionAsync(bool sync) return (DbConnection)_dbConnection; } + + private void GuardDisposed() + { + if (!_disposed) + { + return; + } + + throw new ObjectDisposedException(nameof(DatabaseContext)); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index f94ed6d..0c48631 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -47,7 +47,7 @@ public IDatabaseContext Create(string name) throw new InvalidOperationException(string.Format(Resources.DuplicateDatabaseContextException, name)); } - var databaseContext = new DatabaseContext(name, connectionStringOptions.ProviderName, (DbConnection)DbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), DbCommandFactory, DatabaseContextService, _lock); + var databaseContext = new DatabaseContext(name, connectionStringOptions.ProviderName, (DbConnection)DbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), DbCommandFactory, DatabaseContextService); DatabaseContextCreated?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); diff --git a/Shuttle.Core.Data/Properties/AssemblyInfo.cs b/Shuttle.Core.Data/Properties/AssemblyInfo.cs index c4e6e9e..6ad27a0 100644 --- a/Shuttle.Core.Data/Properties/AssemblyInfo.cs +++ b/Shuttle.Core.Data/Properties/AssemblyInfo.cs @@ -13,10 +13,10 @@ [assembly: AssemblyTitle(".NET Standard")] #endif -[assembly: AssemblyVersion("15.0.0.0")] +[assembly: AssemblyVersion("15.0.1.0")] [assembly: AssemblyCopyright("Copyright (c) 2024, Eben Roux")] [assembly: AssemblyProduct("Shuttle.Core.Data")] [assembly: AssemblyCompany("Eben Roux")] [assembly: AssemblyConfiguration("Release")] -[assembly: AssemblyInformationalVersion("15.0.0")] +[assembly: AssemblyInformationalVersion("15.0.1")] [assembly: ComVisible(false)] \ No newline at end of file From 78f55f0da7704720658aa98ec41444558b36242f Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 12 Jan 2024 08:47:21 +0200 Subject: [PATCH 24/37] - removed database context semaphore --- Shuttle.Core.Data/DatabaseContextFactory.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 0c48631..5db7e3a 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -3,7 +3,6 @@ using System.Threading; using Microsoft.Extensions.Options; using Shuttle.Core.Contract; -using Shuttle.Core.Threading; namespace Shuttle.Core.Data { From e3698b956840ad2a74596538ffe48ef2b08bb45f Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 12 Jan 2024 08:59:15 +0200 Subject: [PATCH 25/37] - removed `ActiveDatabaseContext` --- .../DatabaseContextServiceFixture.cs | 19 +++++++------ Shuttle.Core.Data/ActiveDatabaseContext.cs | 27 ------------------- Shuttle.Core.Data/DatabaseContextService.cs | 4 +-- .../DatabaseContextServiceExtensions.cs | 4 +-- Shuttle.Core.Data/IDatabaseContextService.cs | 4 +-- 5 files changed, 13 insertions(+), 45 deletions(-) delete mode 100644 Shuttle.Core.Data/ActiveDatabaseContext.cs diff --git a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs index ce218db..781f62c 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextServiceFixture.cs @@ -19,20 +19,19 @@ public void Should_be_able_to_use_different_contexts() Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - using (service.Use("mock-1")) - { - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + service.Activate("mock-1"); - Assert.That(service.Current, Is.Not.Null); - Assert.That(service.Current.Name, Is.EqualTo(service.Current.Name)); - } + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + + service.Activate("mock-2"); Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); - using (service.Activate(context1)) - { - Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); - } + service.Activate(context1); + + Assert.That(service.Current.Key, Is.EqualTo(context1.Key)); + + service.Activate(context2); Assert.That(service.Current.Key, Is.EqualTo(context2.Key)); } diff --git a/Shuttle.Core.Data/ActiveDatabaseContext.cs b/Shuttle.Core.Data/ActiveDatabaseContext.cs deleted file mode 100644 index 71ba265..0000000 --- a/Shuttle.Core.Data/ActiveDatabaseContext.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Shuttle.Core.Contract; - -namespace Shuttle.Core.Data -{ - public class ActiveDatabaseContext : IDisposable - { - private readonly IDatabaseContextService _databaseContextService; - private readonly IDatabaseContext _current; - - public ActiveDatabaseContext(IDatabaseContextService databaseContextService, IDatabaseContext current) - { - _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); - _current = current; - } - - public void Dispose() - { - if (_current == null) - { - return; - } - - _databaseContextService.Activate(_current); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 8fcb7bf..9068edd 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -43,7 +43,7 @@ public IDatabaseContext Current } } - public ActiveDatabaseContext Activate(IDatabaseContext context) + public void Activate(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); @@ -62,8 +62,6 @@ public ActiveDatabaseContext Activate(IDatabaseContext context) } GetAmbientData().ActiveDatabaseContext = active; - - return new ActiveDatabaseContext(this, current); } public void Add(IDatabaseContext context) diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs index 2cc4f6a..56fdb9c 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -30,12 +30,12 @@ public static IDatabaseContext Get(this IDatabaseContextService databaseContextS return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); } - public static ActiveDatabaseContext Use(this IDatabaseContextService databaseContextService, string name) + public static void Activate(this IDatabaseContextService databaseContextService, string name) { Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextService.Activate(databaseContextService.Get(name)); + databaseContextService.Activate(databaseContextService.Get(name)); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs index 457f429..883a3e4 100644 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -1,12 +1,10 @@ using System; -using System.Data; -using System.Data.Common; namespace Shuttle.Core.Data { public interface IDatabaseContextService { - ActiveDatabaseContext Activate(IDatabaseContext context); + void Activate(IDatabaseContext context); void Add(IDatabaseContext context); void Remove(IDatabaseContext context); IDatabaseContext Find(Predicate match); From 022b8a7eda202ad654c3d7c6204e3a92306d1c25 Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 12 Jan 2024 11:21:46 +0200 Subject: [PATCH 26/37] - added locking on database context --- .../Shuttle.Core.Data.Tests.csproj | 3 +- Shuttle.Core.Data/DatabaseContextService.cs | 123 ++++++++++++++---- 2 files changed, 98 insertions(+), 28 deletions(-) diff --git a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj index 6b75f14..4def117 100644 --- a/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj +++ b/Shuttle.Core.Data.Tests/Shuttle.Core.Data.Tests.csproj @@ -17,11 +17,12 @@ - + + diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 9068edd..f0aeccb 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Shuttle.Core.Contract; using Shuttle.Core.Threading; @@ -8,6 +9,7 @@ namespace Shuttle.Core.Data { public class DatabaseContextService : IDatabaseContextService { + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private const string AmbientDataKey = "__DatabaseContextService-AmbientData__"; private class AmbientData @@ -34,12 +36,21 @@ public IDatabaseContext Current { get { - if (GetAmbientData().ActiveDatabaseContext == null) + _lock.Wait(); + + try { - throw new InvalidOperationException(Resources.DatabaseContextMissing); - } + if (GetAmbientData().ActiveDatabaseContext == null) + { + throw new InvalidOperationException(Resources.DatabaseContextMissing); + } - return GetAmbientData().ActiveDatabaseContext; + return GetAmbientData().ActiveDatabaseContext; + } + finally + { + _lock.Release(); + } } } @@ -47,33 +58,51 @@ public void Activate(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); - var current = GetAmbientData().ActiveDatabaseContext; + _lock.Wait(); - if (current != null && current.Name.Equals(context.Name)) + try { - throw new Exception(string.Format(Resources.DatabaseContextAlreadyActiveException, context.Name)); - } + var current = GetAmbientData().ActiveDatabaseContext; - var active = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(context.Name, StringComparison.InvariantCultureIgnoreCase)); + if (current != null && current.Name.Equals(context.Name)) + { + throw new Exception(string.Format(Resources.DatabaseContextAlreadyActiveException, context.Name)); + } + + var active = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(context.Name, StringComparison.InvariantCultureIgnoreCase)); + + if (active == null) + { + throw new Exception(string.Format(Resources.DatabaseContextNotFoundException, context.Name)); + } - if (active == null) + GetAmbientData().ActiveDatabaseContext = active; + } + finally { - throw new Exception(string.Format(Resources.DatabaseContextNotFoundException, context.Name)); + _lock.Release(); } - - GetAmbientData().ActiveDatabaseContext = active; } public void Add(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); - if (Find(context) != null) + _lock.Wait(); + + try { - throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); - } + if (Find(context) != null) + { + throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); + } - GetAmbientData().DatabaseContexts.Add(context); + GetAmbientData().DatabaseContexts.Add(context); + } + finally + { + _lock.Release(); + } Activate(context); } @@ -82,31 +111,71 @@ public void Remove(IDatabaseContext context) { Guard.AgainstNull(context, nameof(context)); - var candidate = Find(context); + _lock.Wait(); - if (candidate == null) + try { - return; - } + var candidate = Find(context); + + if (candidate == null) + { + return; + } - if (GetAmbientData().ActiveDatabaseContext != null && candidate.Key.Equals(GetAmbientData().ActiveDatabaseContext.Key)) + if (GetAmbientData().ActiveDatabaseContext != null && candidate.Key.Equals(GetAmbientData().ActiveDatabaseContext.Key)) + { + GetAmbientData().ActiveDatabaseContext = null; + } + + GetAmbientData().DatabaseContexts.Remove(candidate); + } + finally { - GetAmbientData().ActiveDatabaseContext = null; + _lock.Release(); } - - GetAmbientData().DatabaseContexts.Remove(candidate); } public IDatabaseContext Find(Predicate match) { Guard.AgainstNull(match, nameof(match)); - return GetAmbientData().DatabaseContexts.Find(match); + _lock.Wait(); + + try + { + return GetAmbientData().DatabaseContexts.Find(match); + } + finally + { + _lock.Release(); + } + } + + private IDatabaseContext Find(Predicate match, bool locked) + { + Guard.AgainstNull(match, nameof(match)); + + if (locked) + { + _lock.Wait(); + } + + try + { + return GetAmbientData().DatabaseContexts.Find(match); + } + finally + { + if (locked) + { + _lock.Release(); + } + } } private IDatabaseContext Find(IDatabaseContext context) { - return Find(candidate => candidate.Key.Equals(context.Key)); + return Find(candidate => candidate.Key.Equals(context.Key), false); } } } \ No newline at end of file From e378a70b8553c812407ea2d624cf7ec437f11308 Mon Sep 17 00:00:00 2001 From: Eben Date: Fri, 12 Jan 2024 15:14:11 +0200 Subject: [PATCH 27/37] - throwing exception when attempting to remove non-existent database context from database context service --- Shuttle.Core.Data/DatabaseContextService.cs | 66 +++++++------------ .../DatabaseContextServiceExtensions.cs | 2 +- Shuttle.Core.Data/Resources.Designer.cs | 22 +++---- Shuttle.Core.Data/Resources.resx | 10 +-- 4 files changed, 42 insertions(+), 58 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index f0aeccb..58c67bc 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -9,28 +9,8 @@ namespace Shuttle.Core.Data { public class DatabaseContextService : IDatabaseContextService { - private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private const string AmbientDataKey = "__DatabaseContextService-AmbientData__"; - - private class AmbientData - { - public readonly List DatabaseContexts = new List(); - public IDatabaseContext ActiveDatabaseContext = null; - } - - private AmbientData GetAmbientData() - { - var result = AmbientContext.GetData(AmbientDataKey) as AmbientData; - - if (result == null) - { - result = new AmbientData(); - - AmbientContext.SetData(AmbientDataKey, result); - } - - return result; - } + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); public IDatabaseContext Current { @@ -73,7 +53,7 @@ public void Activate(IDatabaseContext context) if (active == null) { - throw new Exception(string.Format(Resources.DatabaseContextNotFoundException, context.Name)); + throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, context.Name)); } GetAmbientData().ActiveDatabaseContext = active; @@ -92,11 +72,11 @@ public void Add(IDatabaseContext context) try { - if (Find(context) != null) + if (Find(candidate => candidate.Name.Equals(context.Name), false) != null) { - throw new Exception(string.Format(Resources.DuplicateDatabaseContextKeyException, context.Key)); + throw new Exception(string.Format(Resources.DuplicateDatabaseContextException, context.Name)); } - + GetAmbientData().DatabaseContexts.Add(context); } finally @@ -115,11 +95,11 @@ public void Remove(IDatabaseContext context) try { - var candidate = Find(context); + var candidate = Find(candidate => candidate.Key.Equals(context.Key), false); if (candidate == null) { - return; + throw new InvalidOperationException(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key, context.Name)); } if (GetAmbientData().ActiveDatabaseContext != null && candidate.Key.Equals(GetAmbientData().ActiveDatabaseContext.Key)) @@ -137,18 +117,7 @@ public void Remove(IDatabaseContext context) public IDatabaseContext Find(Predicate match) { - Guard.AgainstNull(match, nameof(match)); - - _lock.Wait(); - - try - { - return GetAmbientData().DatabaseContexts.Find(match); - } - finally - { - _lock.Release(); - } + return Find(match, true); } private IDatabaseContext Find(Predicate match, bool locked) @@ -173,9 +142,24 @@ private IDatabaseContext Find(Predicate match, bool locked) } } - private IDatabaseContext Find(IDatabaseContext context) + private AmbientData GetAmbientData() { - return Find(candidate => candidate.Key.Equals(context.Key), false); + var result = AmbientContext.GetData(AmbientDataKey) as AmbientData; + + if (result == null) + { + result = new AmbientData(); + + AmbientContext.SetData(AmbientDataKey, result); + } + + return result; + } + + private class AmbientData + { + public readonly List DatabaseContexts = new List(); + public IDatabaseContext ActiveDatabaseContext; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs index 56fdb9c..9fcff3b 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -27,7 +27,7 @@ public static IDatabaseContext Get(this IDatabaseContextService databaseContextS Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNotFoundException); + return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNameNotFoundException); } public static void Activate(this IDatabaseContextService databaseContextService, string name) diff --git a/Shuttle.Core.Data/Resources.Designer.cs b/Shuttle.Core.Data/Resources.Designer.cs index 4d397d3..84e8006 100644 --- a/Shuttle.Core.Data/Resources.Designer.cs +++ b/Shuttle.Core.Data/Resources.Designer.cs @@ -96,6 +96,15 @@ public static string DatabaseContextFactoryOptionsException { } } + /// + /// Looks up a localized string similar to Attempt to retrieve non-existent database context with key '{0}' for name '{1}'.. + /// + public static string DatabaseContextKeyNotFoundException { + get { + return ResourceManager.GetString("DatabaseContextKeyNotFoundException", resourceCulture); + } + } + /// /// Looks up a localized string similar to There is no active database context. Use the database context factory to create a context.. /// @@ -108,9 +117,9 @@ public static string DatabaseContextMissing { /// /// Looks up a localized string similar to Attempt to retrieve non-existent database context with name '{0}'.. /// - public static string DatabaseContextNotFoundException { + public static string DatabaseContextNameNotFoundException { get { - return ResourceManager.GetString("DatabaseContextNotFoundException", resourceCulture); + return ResourceManager.GetString("DatabaseContextNameNotFoundException", resourceCulture); } } @@ -141,15 +150,6 @@ public static string DuplicateDatabaseContextException { } } - /// - /// Looks up a localized string similar to There is already a database context with key '{0}' in the IDatabaseContextCache.. - /// - public static string DuplicateDatabaseContextKeyException { - get { - return ResourceManager.GetString("DuplicateDatabaseContextKeyException", resourceCulture); - } - } - /// /// Looks up a localized string similar to Could not retrieve the value for property `{0}` from `parameters`.. /// diff --git a/Shuttle.Core.Data/Resources.resx b/Shuttle.Core.Data/Resources.resx index 91ebd11..15c9da6 100644 --- a/Shuttle.Core.Data/Resources.resx +++ b/Shuttle.Core.Data/Resources.resx @@ -130,14 +130,10 @@ There is no active database context. Use the database context factory to create a context. - + Attempt to retrieve non-existent database context with name '{0}'. {0} = database context name - - There is already a database context with key '{0}' in the IDatabaseContextCache. - {0} = database context name - Could not find embedded resource script for name '{0}' at path '{1}'. {0} = script file name, {1} = path to resource @@ -180,4 +176,8 @@ Database context with name '{0}' is already active. {0} = database context name + + Attempt to retrieve non-existent database context with key '{0}' for name '{1}'. + {0} = database context key, {1} = database context name + \ No newline at end of file From b27f90139ff8a9a15a77af4a0a5e25e75ec62626 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 3 Feb 2024 17:36:19 +0200 Subject: [PATCH 28/37] - `AsyncLocal` handling of `DatabaseContext` --- .../Fakes/OrderAssembler.cs | 4 +- .../ScriptProviderFixture.cs | 64 ++++++----- .../ScriptProviderNegativeFixture.cs | 17 --- Shuttle.Core.Data/DatabaseContext.cs | 9 +- .../DatabaseContextAmbientData.cs | 54 ++++++++++ ...ContextAsyncLocalValueAssignedEventArgs.cs | 15 +++ .../DatabaseContextAsyncValueEventArgs.cs | 16 +++ .../DatabaseContextCreatedEventArgs.cs | 4 +- Shuttle.Core.Data/DatabaseContextFactory.cs | 32 +++--- Shuttle.Core.Data/DatabaseContextService.cs | 101 +++++++----------- Shuttle.Core.Data/IDatabaseContext.cs | 5 +- Shuttle.Core.Data/IDatabaseContextFactory.cs | 6 +- Shuttle.Core.Data/IDatabaseContextService.cs | 10 +- Shuttle.Core.Data/IScriptProvider.cs | 3 +- Shuttle.Core.Data/ScriptProvider.cs | 63 +++++------ Shuttle.Core.Data/ScriptProviderExtensions.cs | 14 +++ 16 files changed, 242 insertions(+), 175 deletions(-) delete mode 100644 Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs create mode 100644 Shuttle.Core.Data/DatabaseContextAmbientData.cs create mode 100644 Shuttle.Core.Data/DatabaseContextAsyncLocalValueAssignedEventArgs.cs create mode 100644 Shuttle.Core.Data/DatabaseContextAsyncValueEventArgs.cs create mode 100644 Shuttle.Core.Data/ScriptProviderExtensions.cs diff --git a/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs b/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs index 45289c1..f7a5e1d 100644 --- a/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs +++ b/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs @@ -27,9 +27,9 @@ public IEnumerable Assemble(MappedData data) return result; } - public Task> AssembleAsync(MappedData data) + public async Task> AssembleAsync(MappedData data) { - return Task.FromResult(Assemble(data)); + return await Task.FromResult(Assemble(data)); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs index e5c389e..4cea72a 100644 --- a/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs +++ b/Shuttle.Core.Data.Tests/ScriptProviderFixture.cs @@ -8,55 +8,54 @@ namespace Shuttle.Core.Data.Tests { public class ScriptProviderFixture : Fixture { - [SetUp] - public void SetupContext() - { - var cache = new Mock(); - - cache.Setup(m => m.Current).Returns(new Mock().Object); - } - [Test] - public void Should_fail_when_there_is_no_ambient_database_context() - { + public void Should_fail_when_there_connections_string_is_not_found() + { + var connectionStringOptions = new Mock>(); + Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); + () => new ScriptProvider(connectionStringOptions.Object, Options.Create(new ScriptProviderOptions())).Get("missing", "throw")); } [Test] - public void Should_eb_able_to_retrieve_script_from_file() + public void Should_be_able_to_retrieve_script_from_file() { - var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions + var connectionStringOptions = new Mock>(); + + connectionStringOptions.Setup(m => m.Get("shuttle")).Returns(new ConnectionStringOptions + { + Name = "shuttle" + }); + + var provider = new ScriptProvider(connectionStringOptions.Object, Options.Create( new ScriptProviderOptions { FileNameFormat = "{ScriptName}.sql", ScriptFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @".\.scripts\") - }), MockDatabaseContextService()); + })); - var script = provider.Get("file-script"); + var script = provider.Get("shuttle", "file-script"); Assert.IsFalse(string.IsNullOrEmpty(script)); Assert.AreEqual("select 'file-script'", script); } - private static IDatabaseContextService MockDatabaseContextService() - { - var databaseContextCache = new Mock(); - - databaseContextCache.Setup(m => m.Current).Returns(new Mock().Object); - - return databaseContextCache.Object; - } - [Test] public void Should_eb_able_to_retrieve_script_from_resource() { - var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions + var connectionStringOptions = new Mock>(); + + connectionStringOptions.Setup(m => m.Get("shuttle")).Returns(new ConnectionStringOptions + { + Name = "shuttle" + }); + + var provider = new ScriptProvider(connectionStringOptions.Object, Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly, ResourceNameFormat = "Shuttle.Core.Data.Tests..scripts.Microsoft.Data.SqlClient.{ScriptName}.sql" - }), MockDatabaseContextService()); + })); - var script = provider.Get("embedded-script"); + var script = provider.Get("shuttle", "embedded-script"); Assert.IsFalse(string.IsNullOrEmpty(script)); Assert.AreEqual("select 'embedded-script'", script); @@ -65,10 +64,17 @@ public void Should_eb_able_to_retrieve_script_from_resource() [Test] public void Should_throw_exception_when_no_resource_or_file_found() { - var provider = new ScriptProvider(Options.Create(new ScriptProviderOptions + var connectionStringOptions = new Mock>(); + + connectionStringOptions.Setup(m => m.Get("shuttle")).Returns(new ConnectionStringOptions + { + Name = "shuttle" + }); + + var provider = new ScriptProvider(connectionStringOptions.Object, Options.Create(new ScriptProviderOptions { ResourceAssembly = GetType().Assembly - }), MockDatabaseContextService()); + })); Assert.Throws(() => provider.Get("Microsoft.Data.SqlClient", "missing-script")); } diff --git a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs b/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs deleted file mode 100644 index 0be7b18..0000000 --- a/Shuttle.Core.Data.Tests/ScriptProviderNegativeFixture.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; - -namespace Shuttle.Core.Data.Tests -{ - public class ScriptProviderNegativeFixture : Fixture - { - [Test] - public void Should_fail_when_there_is_no_ambient_database_context() - { - Assert.Throws( - () => new ScriptProvider(Options.Create(new ScriptProviderOptions()), new DatabaseContextService()).Get("throw")); - } - } -} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContext.cs b/Shuttle.Core.Data/DatabaseContext.cs index b288642..0371387 100644 --- a/Shuttle.Core.Data/DatabaseContext.cs +++ b/Shuttle.Core.Data/DatabaseContext.cs @@ -55,7 +55,7 @@ public BlockedDbCommand CreateCommand(IQuery query) return new BlockedDbCommand((DbCommand)command, new BlockingSemaphoreSlim(_dbCommandLock), _dbDataReaderLock); } - public BlockedDbConnection GetBlockedDbConnection() + public BlockedDbConnection GetDbConnection() { _dbConnectionLock.Wait(CancellationToken.None); @@ -180,5 +180,12 @@ private void GuardDisposed() throw new ObjectDisposedException(nameof(DatabaseContext)); } + + public async ValueTask DisposeAsync() + { + Dispose(); + + await new ValueTask(); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextAmbientData.cs b/Shuttle.Core.Data/DatabaseContextAmbientData.cs new file mode 100644 index 0000000..3093850 --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextAmbientData.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; + +namespace Shuttle.Core.Data +{ + public class DatabaseContextAmbientData + { + public IEnumerable DatabaseContexts => _databaseContexts.AsReadOnly(); + + public IDatabaseContext ActiveDatabaseContext { get; private set; } + + private readonly List _databaseContexts = new List(); + + internal void Active(IDatabaseContext databaseContext) + { + ActiveDatabaseContext = databaseContext; + } + + internal void Add(IDatabaseContext context) + { + if (_databaseContexts.Find(candidate => candidate.Name.Equals(context.Name, StringComparison.InvariantCultureIgnoreCase)) != null) + { + throw new Exception(string.Format(Resources.DuplicateDatabaseContextException, context.Name)); + } + + _databaseContexts.Add(context); + } + + internal void Inactive() + { + ActiveDatabaseContext = null; + } + + internal void Remove(IDatabaseContext databaseContext) + { + if (_databaseContexts.Find(candidate => candidate.Key.Equals(databaseContext.Key)) == null) + { + throw new InvalidOperationException(string.Format(Resources.DatabaseContextKeyNotFoundException, databaseContext.Key, databaseContext.Name)); + } + + if (ActiveDatabaseContext != null && databaseContext.Key.Equals(ActiveDatabaseContext.Key)) + { + ActiveDatabaseContext = null; + } + + _databaseContexts.Remove(databaseContext); + } + + public IDatabaseContext Find(Predicate match) + { + return _databaseContexts.Find(match); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextAsyncLocalValueAssignedEventArgs.cs b/Shuttle.Core.Data/DatabaseContextAsyncLocalValueAssignedEventArgs.cs new file mode 100644 index 0000000..1dacf06 --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextAsyncLocalValueAssignedEventArgs.cs @@ -0,0 +1,15 @@ +using System; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public class DatabaseContextAsyncLocalValueAssignedEventArgs : EventArgs + { + public DatabaseContextAmbientData AmbientData { get; } + + public DatabaseContextAsyncLocalValueAssignedEventArgs(DatabaseContextAmbientData ambientData) + { + AmbientData = Guard.AgainstNull(ambientData, nameof(ambientData)); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextAsyncValueEventArgs.cs b/Shuttle.Core.Data/DatabaseContextAsyncValueEventArgs.cs new file mode 100644 index 0000000..7d0a127 --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextAsyncValueEventArgs.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public class DatabaseContextAsyncLocalValueChangedEventArgs : EventArgs + { + public AsyncLocalValueChangedArgs AsyncLocalValueChangedArgs { get; } + + public DatabaseContextAsyncLocalValueChangedEventArgs(AsyncLocalValueChangedArgs asyncLocalValueChangedArgs) + { + AsyncLocalValueChangedArgs = Guard.AgainstNull(asyncLocalValueChangedArgs, nameof(asyncLocalValueChangedArgs)); + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs b/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs index 2ffb5b6..b18785a 100644 --- a/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs +++ b/Shuttle.Core.Data/DatabaseContextCreatedEventArgs.cs @@ -5,11 +5,11 @@ namespace Shuttle.Core.Data { public class DatabaseContextEventArgs : EventArgs { - public IDatabaseContext DatabaseContext { get; } - public DatabaseContextEventArgs(IDatabaseContext databaseContext) { DatabaseContext = Guard.AgainstNull(databaseContext, nameof(databaseContext)); } + + public IDatabaseContext DatabaseContext { get; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/DatabaseContextFactory.cs b/Shuttle.Core.Data/DatabaseContextFactory.cs index 5db7e3a..530b2e5 100644 --- a/Shuttle.Core.Data/DatabaseContextFactory.cs +++ b/Shuttle.Core.Data/DatabaseContextFactory.cs @@ -8,45 +8,49 @@ namespace Shuttle.Core.Data { public class DatabaseContextFactory : IDatabaseContextFactory { - private readonly SemaphoreSlim _lock = new SemaphoreSlim(1,1); private readonly IOptionsMonitor _connectionStringOptions; private readonly DataAccessOptions _dataAccessOptions; + private readonly IDatabaseContextService _databaseContextService; + private readonly IDbCommandFactory _dbCommandFactory; + + private readonly IDbConnectionFactory _dbConnectionFactory; + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); public DatabaseContextFactory(IOptionsMonitor connectionStringOptions, IOptions dataAccessOptions, IDbConnectionFactory dbConnectionFactory, IDbCommandFactory dbCommandFactory, IDatabaseContextService databaseContextService) { Guard.AgainstNull(dataAccessOptions, nameof(dataAccessOptions)); - + _connectionStringOptions = Guard.AgainstNull(connectionStringOptions, nameof(connectionStringOptions)); _dataAccessOptions = Guard.AgainstNull(dataAccessOptions.Value, nameof(dataAccessOptions.Value)); - DbConnectionFactory = Guard.AgainstNull(dbConnectionFactory, nameof(dbConnectionFactory)); - DbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); - DatabaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); + _dbConnectionFactory = Guard.AgainstNull(dbConnectionFactory, nameof(dbConnectionFactory)); + _dbCommandFactory = Guard.AgainstNull(dbCommandFactory, nameof(dbCommandFactory)); + _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } public event EventHandler DatabaseContextCreated; - public IDatabaseContext Create(string name) + public IDatabaseContext Create(string connectionStringName) { - Guard.AgainstNullOrEmptyString(name, nameof(name)); + Guard.AgainstNullOrEmptyString(connectionStringName, nameof(connectionStringName)); _lock.Wait(); try { - var connectionStringOptions = _connectionStringOptions.Get(name); + var connectionStringOptions = _connectionStringOptions.Get(connectionStringName); if (connectionStringOptions == null || string.IsNullOrEmpty(connectionStringOptions.Name)) { - throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, name)); + throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, connectionStringName)); } - if (DatabaseContextService.Contains(name)) + if (_databaseContextService.Contains(connectionStringName)) { - throw new InvalidOperationException(string.Format(Resources.DuplicateDatabaseContextException, name)); + throw new InvalidOperationException(string.Format(Resources.DuplicateDatabaseContextException, connectionStringName)); } - var databaseContext = new DatabaseContext(name, connectionStringOptions.ProviderName, (DbConnection)DbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), DbCommandFactory, DatabaseContextService); + var databaseContext = new DatabaseContext(connectionStringName, connectionStringOptions.ProviderName, (DbConnection)_dbConnectionFactory.Create(connectionStringOptions.ProviderName, connectionStringOptions.ConnectionString), _dbCommandFactory, _databaseContextService); DatabaseContextCreated?.Invoke(this, new DatabaseContextEventArgs(databaseContext)); @@ -58,10 +62,6 @@ public IDatabaseContext Create(string name) } } - public IDbConnectionFactory DbConnectionFactory { get; } - public IDbCommandFactory DbCommandFactory { get; } - public IDatabaseContextService DatabaseContextService { get; } - public IDatabaseContext Create() { if (string.IsNullOrEmpty(_dataAccessOptions.DatabaseContextFactory.DefaultConnectionStringName)) diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 58c67bc..fdf94c3 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using Shuttle.Core.Contract; @@ -11,6 +10,7 @@ public class DatabaseContextService : IDatabaseContextService { private const string AmbientDataKey = "__DatabaseContextService-AmbientData__"; private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private static AsyncLocal _ambientData; public IDatabaseContext Current { @@ -20,12 +20,7 @@ public IDatabaseContext Current try { - if (GetAmbientData().ActiveDatabaseContext == null) - { - throw new InvalidOperationException(Resources.DatabaseContextMissing); - } - - return GetAmbientData().ActiveDatabaseContext; + return GetAmbientData().ActiveDatabaseContext ?? throw new InvalidOperationException(Resources.DatabaseContextMissing); } finally { @@ -34,9 +29,13 @@ public IDatabaseContext Current } } - public void Activate(IDatabaseContext context) + public event EventHandler DatabaseContextAsyncLocalValueChanged; + public event EventHandler DatabaseContextAsyncLocalAssigned ; + public event EventHandler DatabaseContextAsyncLocalValueAssigned; + + public void Activate(IDatabaseContext databaseContext) { - Guard.AgainstNull(context, nameof(context)); + Guard.AgainstNull(databaseContext, nameof(databaseContext)); _lock.Wait(); @@ -44,19 +43,19 @@ public void Activate(IDatabaseContext context) { var current = GetAmbientData().ActiveDatabaseContext; - if (current != null && current.Name.Equals(context.Name)) + if (current != null && current.Name.Equals(databaseContext.Name)) { - throw new Exception(string.Format(Resources.DatabaseContextAlreadyActiveException, context.Name)); + throw new Exception(string.Format(Resources.DatabaseContextAlreadyActiveException, databaseContext.Name)); } - var active = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(context.Name, StringComparison.InvariantCultureIgnoreCase)); + var active = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(databaseContext.Name, StringComparison.InvariantCultureIgnoreCase)); if (active == null) { - throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, context.Name)); + throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, databaseContext.Name)); } - GetAmbientData().ActiveDatabaseContext = active; + GetAmbientData().Active(active); } finally { @@ -64,50 +63,33 @@ public void Activate(IDatabaseContext context) } } - public void Add(IDatabaseContext context) + public void Add(IDatabaseContext databaseContext) { - Guard.AgainstNull(context, nameof(context)); + Guard.AgainstNull(databaseContext, nameof(databaseContext)); _lock.Wait(); try { - if (Find(candidate => candidate.Name.Equals(context.Name), false) != null) - { - throw new Exception(string.Format(Resources.DuplicateDatabaseContextException, context.Name)); - } - - GetAmbientData().DatabaseContexts.Add(context); + GetAmbientData().Add(databaseContext); } finally { _lock.Release(); } - Activate(context); + Activate(databaseContext); } - public void Remove(IDatabaseContext context) + public void Remove(IDatabaseContext databaseContext) { - Guard.AgainstNull(context, nameof(context)); + Guard.AgainstNull(databaseContext, nameof(databaseContext)); _lock.Wait(); try { - var candidate = Find(candidate => candidate.Key.Equals(context.Key), false); - - if (candidate == null) - { - throw new InvalidOperationException(string.Format(Resources.DatabaseContextKeyNotFoundException, context.Key, context.Name)); - } - - if (GetAmbientData().ActiveDatabaseContext != null && candidate.Key.Equals(GetAmbientData().ActiveDatabaseContext.Key)) - { - GetAmbientData().ActiveDatabaseContext = null; - } - - GetAmbientData().DatabaseContexts.Remove(candidate); + GetAmbientData().Remove(databaseContext); } finally { @@ -116,50 +98,41 @@ public void Remove(IDatabaseContext context) } public IDatabaseContext Find(Predicate match) - { - return Find(match, true); - } - - private IDatabaseContext Find(Predicate match, bool locked) { Guard.AgainstNull(match, nameof(match)); - if (locked) - { - _lock.Wait(); - } + _lock.Wait(); try { - return GetAmbientData().DatabaseContexts.Find(match); + return GetAmbientData().Find(match); } finally { - if (locked) - { - _lock.Release(); - } + _lock.Release(); } } - private AmbientData GetAmbientData() + private DatabaseContextAmbientData GetAmbientData() { - var result = AmbientContext.GetData(AmbientDataKey) as AmbientData; - - if (result == null) + if (_ambientData == null) { - result = new AmbientData(); + _ambientData = new AsyncLocal(args => + { + DatabaseContextAsyncLocalValueChanged?.Invoke(this, new DatabaseContextAsyncLocalValueChangedEventArgs(args)); + }); - AmbientContext.SetData(AmbientDataKey, result); + DatabaseContextAsyncLocalAssigned?.Invoke(this, EventArgs.Empty); } - return result; - } + if (_ambientData.Value == null) + { + _ambientData.Value = new DatabaseContextAmbientData(); - private class AmbientData - { - public readonly List DatabaseContexts = new List(); - public IDatabaseContext ActiveDatabaseContext; + DatabaseContextAsyncLocalValueAssigned?.Invoke(this, new DatabaseContextAsyncLocalValueAssignedEventArgs(_ambientData.Value)); + } + + return _ambientData.Value; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContext.cs b/Shuttle.Core.Data/IDatabaseContext.cs index 25b978a..88aa3be 100644 --- a/Shuttle.Core.Data/IDatabaseContext.cs +++ b/Shuttle.Core.Data/IDatabaseContext.cs @@ -1,12 +1,11 @@ using System; using System.Data; -using System.Threading; using System.Threading.Tasks; using IsolationLevel = System.Data.IsolationLevel; namespace Shuttle.Core.Data { - public interface IDatabaseContext : IDisposable + public interface IDatabaseContext : IDisposable, IAsyncDisposable { event EventHandler TransactionStarted; event EventHandler TransactionCommitted; @@ -18,7 +17,7 @@ public interface IDatabaseContext : IDisposable IDbTransaction Transaction { get; } BlockedDbCommand CreateCommand(IQuery query); - BlockedDbConnection GetBlockedDbConnection(); + BlockedDbConnection GetDbConnection(); bool HasTransaction { get; } string ProviderName { get; } diff --git a/Shuttle.Core.Data/IDatabaseContextFactory.cs b/Shuttle.Core.Data/IDatabaseContextFactory.cs index b0c7480..0e5ac5d 100644 --- a/Shuttle.Core.Data/IDatabaseContextFactory.cs +++ b/Shuttle.Core.Data/IDatabaseContextFactory.cs @@ -6,11 +6,7 @@ public interface IDatabaseContextFactory { event EventHandler DatabaseContextCreated; - IDatabaseContext Create(string name); + IDatabaseContext Create(string connectionStringName); IDatabaseContext Create(); - - IDbConnectionFactory DbConnectionFactory { get; } - IDbCommandFactory DbCommandFactory { get; } - IDatabaseContextService DatabaseContextService { get; } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs index 883a3e4..0c84193 100644 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -4,9 +4,13 @@ namespace Shuttle.Core.Data { public interface IDatabaseContextService { - void Activate(IDatabaseContext context); - void Add(IDatabaseContext context); - void Remove(IDatabaseContext context); + event EventHandler DatabaseContextAsyncLocalValueChanged; + event EventHandler DatabaseContextAsyncLocalAssigned; + event EventHandler DatabaseContextAsyncLocalValueAssigned; + + void Activate(IDatabaseContext databaseContext); + void Add(IDatabaseContext databaseContext); + void Remove(IDatabaseContext databaseContext); IDatabaseContext Find(Predicate match); IDatabaseContext Current { get; } } diff --git a/Shuttle.Core.Data/IScriptProvider.cs b/Shuttle.Core.Data/IScriptProvider.cs index 0ab13bd..e0d6b79 100644 --- a/Shuttle.Core.Data/IScriptProvider.cs +++ b/Shuttle.Core.Data/IScriptProvider.cs @@ -2,7 +2,6 @@ { public interface IScriptProvider { - string Get(string scriptName); - string Get(string scriptName, params object[] parameters); + string Get(string connectionStringName, string scriptName); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/ScriptProvider.cs b/Shuttle.Core.Data/ScriptProvider.cs index 0e2f87b..3682b51 100644 --- a/Shuttle.Core.Data/ScriptProvider.cs +++ b/Shuttle.Core.Data/ScriptProvider.cs @@ -8,56 +8,57 @@ namespace Shuttle.Core.Data { public class ScriptProvider : IScriptProvider { + private readonly IOptionsMonitor _connectionStringOptions; private static readonly object Lock = new object(); private readonly ScriptProviderOptions _options; - private readonly IDatabaseContextService _databaseContextService; private readonly string[] _emptyFiles = Array.Empty(); private readonly Dictionary _scripts = new Dictionary(); - public ScriptProvider(IOptions options, IDatabaseContextService databaseContextService) + public ScriptProvider(IOptionsMonitor connectionStringOptions, IOptions options) { Guard.AgainstNull(options, nameof(options)); + _connectionStringOptions = Guard.AgainstNull(connectionStringOptions, nameof(connectionStringOptions)); _options = Guard.AgainstNull(options.Value, nameof(options.Value)); - _databaseContextService = Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); } - public string Get(string scriptName) - { - return Get(scriptName, null); - } - - public string Get(string scriptName, params object[] parameters) + public string Get(string connectionStringName, string scriptName) { + Guard.AgainstNullOrEmptyString(connectionStringName, nameof(connectionStringName)); Guard.AgainstNullOrEmptyString(scriptName, nameof(scriptName)); - var key = Key(scriptName); - lock (Lock) { - if (!_scripts.ContainsKey(key)) + var connectionStringOptions = _connectionStringOptions.Get(connectionStringName); + + if (connectionStringOptions == null || string.IsNullOrEmpty(connectionStringOptions.Name)) + { + throw new InvalidOperationException(string.Format(Resources.ConnectionStringMissingException, connectionStringName)); + } + + var key = Key(connectionStringOptions.ProviderName, scriptName); + + if (!_scripts.ContainsKey(key)) { - AddScript(scriptName); + AddEmbedded(connectionStringOptions.ProviderName, scriptName); } - return parameters != null - ? string.Format(_scripts[key], parameters) - : _scripts[key]; + return _scripts[key]; } } - private string Key(string scriptName) + private string Key(string providerName, string scriptName) { lock (Lock) { - return $"[{_databaseContextService.Current.ProviderName}]-{scriptName}"; + return $"[{providerName}]-{scriptName}"; } } - private void AddScript(string scriptName) + private void AddEmbedded(string providerName, string scriptName) { - var key = Key(scriptName); + var key = Key(providerName, scriptName); lock (Lock) { @@ -70,12 +71,12 @@ private void AddScript(string scriptName) if (!string.IsNullOrEmpty(_options.ScriptFolder) && Directory.Exists(_options.ScriptFolder)) { - files = Directory.GetFiles(_options.ScriptFolder, FormattedFileName(scriptName), SearchOption.AllDirectories); + files = Directory.GetFiles(_options.ScriptFolder, FormattedFileName(providerName, scriptName), SearchOption.AllDirectories); } if (files.Length == 0) { - AddEmbeddedScript(scriptName); + AddEmbeddedScript(providerName, scriptName); return; } @@ -90,29 +91,29 @@ private void AddScript(string scriptName) } } - private string FormattedFileName(string scriptName) + private string FormattedFileName(string providerName, string scriptName) { - return FormattedScriptPath(_options.FileNameFormat, scriptName); + return FormattedScriptPath(providerName, _options.FileNameFormat, scriptName); } - private string FormattedResourceName(string scriptName) + private string FormattedResourceName(string providerName, string scriptName) { - return FormattedScriptPath(_options.ResourceNameFormat, scriptName); + return FormattedScriptPath(providerName, _options.ResourceNameFormat, scriptName); } - private string FormattedScriptPath(string format, string scriptName) + private string FormattedScriptPath(string providerName, string format, string scriptName) { - return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", _databaseContextService.Current.ProviderName); + return format.Replace("{ScriptName}", scriptName).Replace("{ProviderName}", providerName); } - private void AddEmbeddedScript(string scriptName) + private void AddEmbeddedScript(string providerName, string scriptName) { if (_options.ResourceAssembly == null) { throw new InvalidOperationException(Resources.ResourceAssemblyMissingException); } - var path = _options.ResourceNameFormat != null ? FormattedResourceName(scriptName) : scriptName; + var path = _options.ResourceNameFormat != null ? FormattedResourceName(providerName, scriptName) : scriptName; using (var stream = _options.ResourceAssembly.GetManifestResourceStream(path)) { @@ -123,7 +124,7 @@ private void AddEmbeddedScript(string scriptName) using (var reader = new StreamReader(stream)) { - _scripts.Add(Key(scriptName), reader.ReadToEnd()); + _scripts.Add(Key(providerName, scriptName), reader.ReadToEnd()); } } } diff --git a/Shuttle.Core.Data/ScriptProviderExtensions.cs b/Shuttle.Core.Data/ScriptProviderExtensions.cs new file mode 100644 index 0000000..2617a11 --- /dev/null +++ b/Shuttle.Core.Data/ScriptProviderExtensions.cs @@ -0,0 +1,14 @@ +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public static class ScriptProviderExtensions + { + public static string Get(this IScriptProvider scriptProvider, string connectionStringName, string scriptName, params object[] parameters) + { + Guard.AgainstNull(scriptProvider, nameof(scriptProvider)); + + return string.Format(scriptProvider.Get(connectionStringName, scriptName), parameters); + } + } +} \ No newline at end of file From 1b933626b9654f5cc90735d2627ffe1c6f7aa512 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 2 Mar 2024 17:29:29 +0200 Subject: [PATCH 29/37] - moved static SectionName to options --- Shuttle.Core.Data/Configuration/DataAccessBuilder.cs | 2 -- Shuttle.Core.Data/Configuration/DataAccessOptions.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle.Core.Data/Configuration/DataAccessBuilder.cs b/Shuttle.Core.Data/Configuration/DataAccessBuilder.cs index 85e82e3..949451f 100644 --- a/Shuttle.Core.Data/Configuration/DataAccessBuilder.cs +++ b/Shuttle.Core.Data/Configuration/DataAccessBuilder.cs @@ -8,8 +8,6 @@ namespace Shuttle.Core.Data { public class DataAccessBuilder { - public const string SectionName = "Shuttle:DataAccess"; - private DataAccessOptions _dataAccessOptions = new DataAccessOptions(); public DataAccessOptions Options diff --git a/Shuttle.Core.Data/Configuration/DataAccessOptions.cs b/Shuttle.Core.Data/Configuration/DataAccessOptions.cs index 687ff4c..0ca54da 100644 --- a/Shuttle.Core.Data/Configuration/DataAccessOptions.cs +++ b/Shuttle.Core.Data/Configuration/DataAccessOptions.cs @@ -2,6 +2,8 @@ namespace Shuttle.Core.Data { public class DataAccessOptions { + public const string SectionName = "Shuttle:DataAccess"; + public int CommandTimeout { get; set; } = 15; public DatabaseContextFactoryOptions DatabaseContextFactory { get; set; } = new DatabaseContextFactoryOptions(); } From 7d834a62479927ab3d3db139e05b5e88956e8514 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 16 Mar 2024 20:30:34 +0200 Subject: [PATCH 30/37] - fixed DatabaseContextNameNotFoundException message --- .../Extensions/DatabaseContextServiceExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs index 9fcff3b..65a2942 100644 --- a/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs +++ b/Shuttle.Core.Data/Extensions/DatabaseContextServiceExtensions.cs @@ -27,7 +27,7 @@ public static IDatabaseContext Get(this IDatabaseContextService databaseContextS Guard.AgainstNull(databaseContextService, nameof(databaseContextService)); Guard.AgainstNullOrEmptyString(name, nameof(name)); - return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(Resources.DatabaseContextNameNotFoundException); + return databaseContextService.Find(databaseContext => databaseContext.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) ?? throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, name)); } public static void Activate(this IDatabaseContextService databaseContextService, string name) From 3b99b2ace331852d3ff92286be217e1861b90da3 Mon Sep 17 00:00:00 2001 From: Eben Date: Sat, 16 Mar 2024 20:33:49 +0200 Subject: [PATCH 31/37] - Renamed ambient context `Active` to `Activate` --- Shuttle.Core.Data/DatabaseContextAmbientData.cs | 7 +------ Shuttle.Core.Data/DatabaseContextService.cs | 6 +++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseContextAmbientData.cs b/Shuttle.Core.Data/DatabaseContextAmbientData.cs index 3093850..cf9208e 100644 --- a/Shuttle.Core.Data/DatabaseContextAmbientData.cs +++ b/Shuttle.Core.Data/DatabaseContextAmbientData.cs @@ -11,7 +11,7 @@ public class DatabaseContextAmbientData private readonly List _databaseContexts = new List(); - internal void Active(IDatabaseContext databaseContext) + internal void Activate(IDatabaseContext databaseContext) { ActiveDatabaseContext = databaseContext; } @@ -26,11 +26,6 @@ internal void Add(IDatabaseContext context) _databaseContexts.Add(context); } - internal void Inactive() - { - ActiveDatabaseContext = null; - } - internal void Remove(IDatabaseContext databaseContext) { if (_databaseContexts.Find(candidate => candidate.Key.Equals(databaseContext.Key)) == null) diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index fdf94c3..6ed1f0e 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -48,14 +48,14 @@ public void Activate(IDatabaseContext databaseContext) throw new Exception(string.Format(Resources.DatabaseContextAlreadyActiveException, databaseContext.Name)); } - var active = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(databaseContext.Name, StringComparison.InvariantCultureIgnoreCase)); + var activate = GetAmbientData().DatabaseContexts.FirstOrDefault(item => item.Name.Equals(databaseContext.Name, StringComparison.InvariantCultureIgnoreCase)); - if (active == null) + if (activate == null) { throw new Exception(string.Format(Resources.DatabaseContextNameNotFoundException, databaseContext.Name)); } - GetAmbientData().Active(active); + GetAmbientData().Activate(activate); } finally { From cbb8467090fd289bdbfc6ae63bcb9b7d49a56382 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 31 Mar 2024 11:42:04 +0200 Subject: [PATCH 32/37] - ambient data fix --- Shuttle.Core.Data/DatabaseContextService.cs | 28 ++++++++++---------- Shuttle.Core.Data/IDatabaseContextService.cs | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 6ed1f0e..024e187 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -2,15 +2,21 @@ using System.Linq; using System.Threading; using Shuttle.Core.Contract; -using Shuttle.Core.Threading; namespace Shuttle.Core.Data { public class DatabaseContextService : IDatabaseContextService { - private const string AmbientDataKey = "__DatabaseContextService-AmbientData__"; - private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private static AsyncLocal _ambientData; + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + + public DatabaseContextService() + { + _ambientData = new AsyncLocal(OnAsyncLocalValueChanged) + { + Value = new DatabaseContextAmbientData() + }; + } public IDatabaseContext Current { @@ -30,7 +36,6 @@ public IDatabaseContext Current } public event EventHandler DatabaseContextAsyncLocalValueChanged; - public event EventHandler DatabaseContextAsyncLocalAssigned ; public event EventHandler DatabaseContextAsyncLocalValueAssigned; public void Activate(IDatabaseContext databaseContext) @@ -115,16 +120,6 @@ public IDatabaseContext Find(Predicate match) private DatabaseContextAmbientData GetAmbientData() { - if (_ambientData == null) - { - _ambientData = new AsyncLocal(args => - { - DatabaseContextAsyncLocalValueChanged?.Invoke(this, new DatabaseContextAsyncLocalValueChangedEventArgs(args)); - }); - - DatabaseContextAsyncLocalAssigned?.Invoke(this, EventArgs.Empty); - } - if (_ambientData.Value == null) { _ambientData.Value = new DatabaseContextAmbientData(); @@ -134,5 +129,10 @@ private DatabaseContextAmbientData GetAmbientData() return _ambientData.Value; } + + private void OnAsyncLocalValueChanged(AsyncLocalValueChangedArgs args) + { + DatabaseContextAsyncLocalValueChanged?.Invoke(this, new DatabaseContextAsyncLocalValueChangedEventArgs(args)); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs index 0c84193..c5757cf 100644 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -5,7 +5,6 @@ namespace Shuttle.Core.Data public interface IDatabaseContextService { event EventHandler DatabaseContextAsyncLocalValueChanged; - event EventHandler DatabaseContextAsyncLocalAssigned; event EventHandler DatabaseContextAsyncLocalValueAssigned; void Activate(IDatabaseContext databaseContext); From 52f4a7bbcef9377e906418fb764127a8006bcb3d Mon Sep 17 00:00:00 2001 From: Eben Date: Mon, 1 Apr 2024 09:59:41 +0200 Subject: [PATCH 33/37] - creating ambient data only once --- Shuttle.Core.Data/DatabaseContextService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 024e187..8b74fea 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -12,6 +12,11 @@ public class DatabaseContextService : IDatabaseContextService public DatabaseContextService() { + if (_ambientData != null) + { + return; + } + _ambientData = new AsyncLocal(OnAsyncLocalValueChanged) { Value = new DatabaseContextAmbientData() From a8c534dbb3fba460be92cef2b3ac42df8f5ab517 Mon Sep 17 00:00:00 2001 From: Eben Date: Tue, 2 Apr 2024 19:56:04 +0200 Subject: [PATCH 34/37] - ambient scope --- Shuttle.Core.Data/DatabaseContextService.cs | 20 ++++++++++++++++---- Shuttle.Core.Data/IDatabaseContextService.cs | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Shuttle.Core.Data/DatabaseContextService.cs b/Shuttle.Core.Data/DatabaseContextService.cs index 8b74fea..bf54dd8 100644 --- a/Shuttle.Core.Data/DatabaseContextService.cs +++ b/Shuttle.Core.Data/DatabaseContextService.cs @@ -7,8 +7,16 @@ namespace Shuttle.Core.Data { public class DatabaseContextService : IDatabaseContextService { + private class NullScope : IDisposable + { + public void Dispose() + { + } + } + private static AsyncLocal _ambientData; private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private readonly NullScope _nullScope = new NullScope(); public DatabaseContextService() { @@ -17,10 +25,7 @@ public DatabaseContextService() return; } - _ambientData = new AsyncLocal(OnAsyncLocalValueChanged) - { - Value = new DatabaseContextAmbientData() - }; + _ambientData = new AsyncLocal(OnAsyncLocalValueChanged); } public IDatabaseContext Current @@ -40,6 +45,13 @@ public IDatabaseContext Current } } + public IDisposable GetAmbientScope() + { + GetAmbientData(); + + return _nullScope; + } + public event EventHandler DatabaseContextAsyncLocalValueChanged; public event EventHandler DatabaseContextAsyncLocalValueAssigned; diff --git a/Shuttle.Core.Data/IDatabaseContextService.cs b/Shuttle.Core.Data/IDatabaseContextService.cs index c5757cf..a527795 100644 --- a/Shuttle.Core.Data/IDatabaseContextService.cs +++ b/Shuttle.Core.Data/IDatabaseContextService.cs @@ -12,5 +12,6 @@ public interface IDatabaseContextService void Remove(IDatabaseContext databaseContext); IDatabaseContext Find(Predicate match); IDatabaseContext Current { get; } + IDisposable GetAmbientScope(); } } \ No newline at end of file From 72c76dc0a9c4433954becee727d403d30b4677db Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 21 Apr 2024 10:23:19 +0200 Subject: [PATCH 35/37] - minor refactoring - docs --- README.md | 160 ++++++------------ .../Fakes/OrderAssembler.cs | 3 +- .../Extensions/AssemblerExtensions.cs | 5 +- Shuttle.Core.Data/IAssembler.cs | 4 +- Shuttle.Core.Data/IAsyncAssembler.cs | 11 ++ 5 files changed, 68 insertions(+), 115 deletions(-) create mode 100644 Shuttle.Core.Data/IAsyncAssembler.cs diff --git a/README.md b/README.md index 0ea1bbb..9b54652 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Provides an abstraction built directly on ADO.NET which falls within the Micro O # Overview -The `Shuttle.Core.Data` package provides a thin abstraction over ADO.NET by making use of the `DbProviderFactories` (see `Shuttle.Core.Data.SqlClient` for .Net Core Provider Factory adapter). Even though it provides object/relational mapping mechanisms it is in no way an ORM. +The `Shuttle.Core.Data` package provides a thin abstraction over ADO.NET by making use of the `DbProviderFactories`. Even though it provides object/relational mapping mechanisms it is in no way an ORM. ## Configuration @@ -72,27 +72,15 @@ The default JSON settings structure is as follows: In order to access a database we need a database connection. A database connection is represented by an `IDatabaseContext` instance that may be obtained by using an instance of an `IDatabaseContextFactory` implementation. -The `DatabaseContextFactory` implementation makes use of an `IDbConnectionFactory` implementation which creates a `System.Data.IDbConnection` by using the provider name and connection string. An `IDbCommandFactory` creates a `System.Data.IDbCommand` by using an `IDbConnection` instance. +The `DatabaseContextFactory` implementation makes use of an `IDbConnectionFactory` implementation which creates a `System.Data.IDbConnection` by using the provider name and connection string, which is obtained from the registered connection name. An `IDbCommandFactory` creates a `System.Data.IDbCommand` by using an `IDbConnection` instance. ``` c# -var databaseContextFactory = provider.GetRequiredService(); +var databaseContextFactory = provider.GetRequiredService(); using (var databaseContext = databaseContextFactory.Create("connection-name")) { // database interaction } - -using (var databaseContext = databaseContextFactory - .Create("Microsoft.Data.SqlClient", - "Data Source=.\sqlexpress;Initial Catalog=Shuttle;Integrated Security=SSPI;TrustServerCertificate=true")) -{ - // database interaction -} - -using (var databaseContext = databaseContextFactory.Create(existingIDbConnection)) -{ - // database interaction -} ``` # IQuery @@ -105,32 +93,20 @@ void Prepare(IDbCommand command); This should ensure that the given `IDbCommand` is configured for execution by setting the relvant command attributes and parameters. -## RawQuery +## Query -The `RawQuery` represents a `Text` command type: +The `Query` represents a `Text` command type: ``` c# -public RawQuery(string sql); -public static IQueryParameter Create(string sql, params object[] args); -public static IQueryParameter Create(string sql, dynamic parameters); +public Query(string commandText, CommandType commandType = CommandType.Text) ``` -You can either use the constructor or one of the static methods to specify the `sql` to use. Parameters may either be added using the `AddParameterValue` of the returned `IQueryParameter` or they may be added as `params object[] args` in order to insert them at the required index. When using `dynamic` parameters the object values are converted to `Column` instances and added as parameters using `AddParameterValue`. - -## ProcedureQuery - -The `ProcedureQuery` represents a `StoredProcedure` command type. - -## Dynamic mapping extensions - -Methods that take an `IQuery` instance as a parameter will typically also be implemented as an extension method that take the `sql` and `dynamic` parameters as input, e.g.: +You can then add parameters to the query: ``` c# -public static int DataAccessMethod(this IDataAccessInterface dataAccessInstance, string sql, dynamic parameters = null); +query.AddParameter(new Column("Id", DbType.Guid), new Guid('{75208260-CF93-454E-95EC-FE1903F3664E}')); ``` -Where `DataAccessMethod` is the relevant method on the `IDataAccessInterface` that you would like to call. - # Column Typically you would not want to create a `Column` each time you need it and these are also quite fixed. A column mapping can, therefore, by defined statically: @@ -154,7 +130,7 @@ namespace Shuttle.Ordering.DataAccess new Column("OrderDate", DbType.DateTime); public static readonly Column CustomerName = - new Column("CustomerName", DbType.String, 65); + new Column("CustomerName", DbType.String, 65); public static readonly Column CustomerEMail = new Column("CustomerEMail", DbType.String); // size omitted @@ -172,52 +148,46 @@ public T Value(DataRow row) This will return the typed value of the specified column as contained in the passed-in `DataRow`. -# IQueryParameter: IQuery - -An `IQueryParameter` inherits the `IQuery` interface and extends it by allowing you to add parameters to a query by specifying an `IColumn` (see below) instance along with the value for the parameter. - -There are two implementations of this interface. - # IDatabaseGateway -The `DatabaseGateway` is used to execute `IQuery` instances in order return data from, or make changes to, the underlying data store. If there is no active open `IDatabaseContext` returned by the `DatabaseContext.Current` and `InvalidOperationException` will be thrown. +The `DatabaseGateway` is used to execute `IQuery` instances in order return data from, or make changes to, the underlying data store. If there is no active open `IDatabaseContext` returned by the `DatabaseContextService.Current` an `InvalidOperationException` will be thrown. -The following section each describe the methods available in the `IDatabaseGateway` interface. +The following sections each describe the methods available in the `IDatabaseGateway` interface. -## GetReader +## GetReaderAsync ``` c# -Task GetReader(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task GetReaderAsync(IQuery query, CancellationToken cancellationToken = default); ``` Returns an `IDataReader` instance for the given `query` statement: ``` c# -using (var databaseContext = databaseContextFactory.Create("connection-name")) +using (databaseContextFactory.Create("connection-name")) { - var reader = await gateway.GetReader(databaseContext, RawQuery.Create("select Id, Username from dbo.Member")); + var reader = await gateway.GetReaderAsync(new Query("select Id, Username from dbo.Member")); } ``` -## Execute +## ExecuteAsync ``` c# -Task Execute(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task ExecuteAsync(IQuery query, CancellationToken cancellationToken = default); ``` Executes the given query and returns the number of rows affected: ``` c# -using (var databaseContext = databaseContextFactory.Create("connection-name")) +using (databaseContextFactory.Create("connection-name")) { - gateway.Execute(databaseContext, RawQuery.Create("delete from dbo.Member where Username = 'mr.resistor'")); + await gateway.ExecuteAsync(new Query("delete from dbo.Member where Username = 'mr.resistor'")); } ``` -## GetScalar +## GetScalarAsync ```c# -Task GetScalar(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task GetScalarAsync(IQuery query, CancellationToken cancellationToken = default); ``` Get the scalar value returned by the `select` query. The query shoud return only one value (scalar): @@ -225,63 +195,54 @@ Get the scalar value returned by the `select` query. The query shoud return onl ```c# using (var databaseContext = databaseContextFactory.Create("connection-name")) { - var username = await gateway.GetScalar( - databaseContext, - RawQuery.Create("select Username from dbo.Member where Id = 10") - ); + var username = await gateway.GetScalarAsync(new Query("select Username from dbo.Member where Id = 10")); - var id = await gateway.GetScalar( - databaseContext, - RawQuery.Create("select Id from dbo.Member where Username = 'mr.resistor'") - ); + var id = await gateway.GetScalarAsync(new Query.Create("select Id from dbo.Member where Username = 'mr.resistor'") ); } ``` -## GetDataTable +## GetDataTableAsync ``` c# -Task GetDataTable(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task GetDataTableAsync(IQuery query, CancellationToken cancellationToken = default); ``` Returns a `DataTable` containing the rows returned for the given `select` query. ``` c# -using (var databaseContext = databaseContextFactory.Create("connection-name")) +using (databaseContextFactory.Create("connection-name")) { - var table = await gateway.GetDataTable(databaseContext, RawQuery.Create("select Id, Username from dbo.Member")); + var table = await gateway.GetDataTableAsync(new Query("select Id, Username from dbo.Member")); } ``` -## GetRows +## GetRowsAsync ``` c# -Task> GetRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task> GetRowsAsync(IQuery query, CancellationToken cancellationToken = default); ``` Returns an enumerable containing the `DataRow` instances returned for a `select` query: ``` c# -using (var databaseContext = databaseContextFactory.Create("connection-name")) +using (databaseContextFactory.Create("connection-name")) { - var rows = gateway.GetRows(databaseContext, RawQuery.Create("select Id, Username from dbo.Member")); + var rows = await gateway.GetRowsAsync(new Query("select Id, Username from dbo.Member")); } ``` -## GetRow +## GetRowAsync ``` c# -Task GetRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task GetRowAsync(IQuery query, CancellationToken cancellationToken = default); ``` Returns a single `DataRow` containing the values returned for a `select` statement that returns exactly one row: ``` c# -using (var databaseContext = databaseContextFactory.Create("connection-name")) +using (databaseContextFactory.Create("connection-name")) { - var row = await gateway.GetRow( - databaseContext, - RawQuery.Create("select Id, Username, EMail, DateActivated from dbo.Member where Id = 10") - ); + var row = await gateway.GetRowAsync(new Query("select Id, Username, EMail, DateActivated from dbo.Member where Id = 10") ); } ``` @@ -291,72 +252,53 @@ An `IDataRepository` implementation is responsible for returning a hydrated o The following methods can be used to interact with your object type. -## FetchItems +## FetchItemsAsync ``` c# -Task> FetchItems(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task> FetchItemsAsync(IQuery query, CancellationToken cancellationToken = default); ``` Uses the `select` clause represented by the `IQuery` instance to create a list of objects of type `T`. The `select` clause will need to select all the required columns and will, typically, return more than one instance. -## FetchItem +## FetchItemAsync ``` c# -Task FetchItem(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task FetchItemAsync(IQuery query, CancellationToken cancellationToken = default); ``` Returns a single object instance of type `T` that is hydrated using the data returned from the `select` clause represented by the `IQuery` instance. -## FetchMappedRows +## FetchMappedRowsAsync ``` c# -IEnumerable> FetchMappedRows(IQuery query); +Task> FetchMappedRowsAsync(IQuery query, CancellationToken cancellationToken = default); ``` This is similar to the `FetchItems` method but instead returns a list of `MappedRow` instances. Uses the `select` clause represented by the `IQuery` instance to create a list of `MappedRow` instances of type `T`. The `select` clause will need to select all the required columns and will, typically, return more than one instance. -## FetchMappedRow +## FetchMappedRowAsync ``` c# -Task> FetchMappedRow(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task>> FetchMappedRowAsync(IQuery query, CancellationToken cancellationToken = default); ``` Similar to the `FetchItem` method but instead return a `MappedRow` instance that is hydrated using the data returned from the `select` clause represented by the `IQuery` instance. -## FetchMappedRows +## ContainsAsync ``` c# -Task>> FetchMappedRows(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); -``` - -Similar to the `FetchMappedRow` method but returns an enumerable containing all the `MappedRow` instances. - -## Contains - -``` c# -Task Contains(IDatabaseContext databaseContext, IQuery query, CancellationToken cancellationToken = default); +Task ContainsAsync(IQuery query, CancellationToken cancellationToken = default); ``` Returns `true` is the `IQuery` instance `select` clause returns an `int` scalar that equals `1`; else returns `false`. -## RawQuery - -The `RawQuery` enables you to create any query using the native language structure: - -``` c# -var query = RawQuery.Create("select UserName from dbo.Member where Id = @Id") - .AddParameterValue(new Column("Id", DbType.Guid), - new Guid('{75208260-CF93-454E-95EC-FE1903F3664E}')); -``` - -## ProcedureQuery +## Query -The `ProcedureQuery` is used to execute a stored procedure: +The `Query` enables you to create any query using the native language structure: ``` c# -var query = ProcedureQuery.Create("uspMemberById") - .AddParameterValue(new Column("Id", DbType.Guid), - new Guid('{75208260-CF93-454E-95EC-FE1903F3664E}')); +var query = new Query("select UserName from dbo.Member where Id = @Id") + .AddParameter(new Column("Id", DbType.Guid), new Guid('{75208260-CF93-454E-95EC-FE1903F3664E}')); ``` # IDataRowMapper @@ -392,9 +334,9 @@ namespace Shuttle.ProcessManagement # MappedRow -A `MappedRow` instance contains bother a `DataRow` and the object that the `DataRow` mapped to. +A `MappedRow` instance contains both a `DataRow` and the object that the `DataRow` mapped to. -This may be useful in situation where the `DataRow` contains more information that is available on the object. An example may be an `OrderLine` where the `DataRow` contains the `OrderId` column but the `OrderLine` object does not. In order to still be able to make that association it is useful to have both available. +This may be useful in situations where the `DataRow` contains more information than is available on the object. An example may be an `OrderLine` where the `DataRow` contains the `OrderId` column but the `OrderLine` object does not. In order to still be able to make that association it is useful to have both available. # IAssembler diff --git a/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs b/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs index f7a5e1d..5e1581a 100644 --- a/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs +++ b/Shuttle.Core.Data.Tests/Fakes/OrderAssembler.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Shuttle.Core.Data.Tests.Fakes @@ -27,7 +28,7 @@ public IEnumerable Assemble(MappedData data) return result; } - public async Task> AssembleAsync(MappedData data) + public async Task> AssembleAsync(MappedData data, CancellationToken cancellationToken) { return await Task.FromResult(Assemble(data)); } diff --git a/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs b/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs index a3e6800..e75c6bf 100644 --- a/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs +++ b/Shuttle.Core.Data/Extensions/AssemblerExtensions.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Shuttle.Core.Data @@ -10,9 +11,9 @@ public static T AssembleItem(this IAssembler assembler, MappedData data) w return assembler.Assemble(data).FirstOrDefault(); } - public static async Task AssembleItemAsync(this IAssembler assembler, MappedData data) where T : class + public static async Task AssembleItemAsync(this IAsyncAssembler assembler, MappedData data, CancellationToken cancellationToken = default) where T : class { - return (await assembler.AssembleAsync(data).ConfigureAwait(false)).FirstOrDefault(); + return (await assembler.AssembleAsync(data, cancellationToken).ConfigureAwait(false)).FirstOrDefault(); } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IAssembler.cs b/Shuttle.Core.Data/IAssembler.cs index 9203f0a..ace8bbe 100644 --- a/Shuttle.Core.Data/IAssembler.cs +++ b/Shuttle.Core.Data/IAssembler.cs @@ -1,11 +1,9 @@ using System.Collections.Generic; -using System.Threading.Tasks; namespace Shuttle.Core.Data { - public interface IAssembler where T : class + public interface IAssembler where T : class { IEnumerable Assemble(MappedData data); - Task> AssembleAsync(MappedData data); } } \ No newline at end of file diff --git a/Shuttle.Core.Data/IAsyncAssembler.cs b/Shuttle.Core.Data/IAsyncAssembler.cs new file mode 100644 index 0000000..1164487 --- /dev/null +++ b/Shuttle.Core.Data/IAsyncAssembler.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Shuttle.Core.Data +{ + public interface IAsyncAssembler where T : class + { + Task> AssembleAsync(MappedData data, CancellationToken cancellationToken = default); + } +} \ No newline at end of file From 4b9265c9e34e889529e84e0dae7a1a0762495034 Mon Sep 17 00:00:00 2001 From: Eben Date: Sun, 21 Apr 2024 10:26:04 +0200 Subject: [PATCH 36/37] - docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b54652..f8a0090 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Provides an abstraction built directly on ADO.NET which falls within the Micro O # Overview -The `Shuttle.Core.Data` package provides a thin abstraction over ADO.NET by making use of the `DbProviderFactories`. Even though it provides object/relational mapping mechanisms it is in no way an ORM. +The `Shuttle.Core.Data` package provides a thin abstraction over ADO.NET by making use of the `DbProviderFactories`. Even though it provides object/relational mapping mechanisms it is in no way a fully fledged ORM. ## Configuration From bf05f1ca70df51066b884730b09d93a96563edc5 Mon Sep 17 00:00:00 2001 From: Eben Date: Tue, 30 Apr 2024 12:08:24 +0200 Subject: [PATCH 37/37] Update README.md --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f8a0090..55da080 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,6 @@ services.AddDataAccess(builder => builder.Options.CommandTimeout = timeout; builder.Options.DatabaseContextFactory.DefaultConnectionStringName = "connection-string-name"; - // -- or -- - builder.Options.DatabaseContextFactory.DefaultProviderName = "provider-name", - builder.Options.DatabaseContextFactory.DefaultConnectionString = "connection-string" - } }); ``` @@ -59,9 +55,6 @@ The default JSON settings structure is as follows: "DatabaseContextFactory": { "DefaultConnectionStringName": "connection-string-name", - // or - "DefaultProviderName": "provider-name", - "DefaultConnectionString": "connection-string" } } } @@ -85,7 +78,7 @@ using (var databaseContext = databaseContextFactory.Create("connection-name")) # IQuery -An `IQuery` encapsulates a database query that can be executed. There is only one method that needs to be implemented: +An `IQuery` encapsulates a database query that can be executed: ``` c# void Prepare(IDbCommand command); @@ -93,6 +86,12 @@ void Prepare(IDbCommand command); This should ensure that the given `IDbCommand` is configured for execution by setting the relvant command attributes and parameters. +```c# +IQuery AddParameter(IColumn column, object value); +``` + +This method is used to add a parameter to the query. The `IColumn` instance is used to define the column type and the value is the value that should be used for the parameter. + ## Query The `Query` represents a `Text` command type: