feat: make data access layer generic
This commit is contained in:
parent
2242fe8733
commit
1266932d4e
|
@ -4,6 +4,14 @@
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<Authors>Kyle Ratti</Authors>
|
||||||
|
<RepositoryUrl>https://github.com/kyleratti/FruityFoundation</RepositoryUrl>
|
||||||
|
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE" Pack="true" PackagePath="" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -5,7 +5,6 @@ namespace FruityFoundation.DataAccess.Abstractions;
|
||||||
public interface INonTransactionalDbConnection<TConnectionType> : IDatabaseConnection<TConnectionType>, IDisposable, IAsyncDisposable
|
public interface INonTransactionalDbConnection<TConnectionType> : IDatabaseConnection<TConnectionType>, IDisposable, IAsyncDisposable
|
||||||
where TConnectionType : ConnectionType
|
where TConnectionType : ConnectionType
|
||||||
{
|
{
|
||||||
public Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction();
|
public Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(CancellationToken cancellationToken);
|
||||||
public Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel);
|
public Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel, CancellationToken cancellationToken);
|
||||||
public Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel, bool deferred);
|
|
||||||
}
|
}
|
||||||
|
|
30
FruityFoundation.DataAccess.Core/DbConnectionFactory.cs
Normal file
30
FruityFoundation.DataAccess.Core/DbConnectionFactory.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Data.Common;
|
||||||
|
using FruityFoundation.DataAccess.Abstractions;
|
||||||
|
|
||||||
|
namespace FruityFoundation.DataAccess.Core;
|
||||||
|
|
||||||
|
public class DbConnectionFactory : IDbConnectionFactory
|
||||||
|
{
|
||||||
|
private readonly Func<DbConnection> _readWriteConnectionFactory;
|
||||||
|
private readonly Func<DbConnection> _readOnlyConnectionFactory;
|
||||||
|
|
||||||
|
public DbConnectionFactory(Func<DbConnection> readWriteConnectionFactory, Func<DbConnection> readOnlyConnectionFactory)
|
||||||
|
{
|
||||||
|
_readWriteConnectionFactory = readWriteConnectionFactory;
|
||||||
|
_readOnlyConnectionFactory = readOnlyConnectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INonTransactionalDbConnection<ReadWrite> CreateConnection()
|
||||||
|
{
|
||||||
|
var connection = _readWriteConnectionFactory();
|
||||||
|
var nonTxConnection = new NonTransactionalDbConnection<ReadWrite>(connection);
|
||||||
|
return nonTxConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INonTransactionalDbConnection<ReadOnly> CreateReadOnlyConnection()
|
||||||
|
{
|
||||||
|
var connection = _readOnlyConnectionFactory();
|
||||||
|
var nonTxConnection = new NonTransactionalDbConnection<ReadOnly>(connection);
|
||||||
|
return nonTxConnection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,21 +2,20 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using FruityFoundation.DataAccess.Abstractions;
|
using FruityFoundation.DataAccess.Abstractions;
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
|
|
||||||
namespace FruityFoundation.DataAccess.Sqlite;
|
namespace FruityFoundation.DataAccess.Core;
|
||||||
|
|
||||||
|
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
|
||||||
public class DbTransaction<TConnectionType> : IDatabaseTransactionConnection<TConnectionType>
|
public class DbTransaction<TConnectionType> : IDatabaseTransactionConnection<TConnectionType>
|
||||||
where TConnectionType : ConnectionType
|
where TConnectionType : ConnectionType
|
||||||
{
|
{
|
||||||
private readonly SqliteTransaction _transaction;
|
private readonly DbTransaction _transaction;
|
||||||
|
|
||||||
internal DbTransaction(SqliteTransaction transaction)
|
internal DbTransaction(DbTransaction transaction)
|
||||||
{
|
{
|
||||||
_transaction = transaction;
|
_transaction = transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<IEnumerable<T>> Query<T>(
|
public async Task<IEnumerable<T>> Query<T>(
|
||||||
string sql,
|
string sql,
|
||||||
|
@ -109,17 +108,34 @@ public class DbTransaction<TConnectionType> : IDatabaseTransactionConnection<TCo
|
||||||
await _transaction.RollbackAsync(cancellationToken);
|
await _transaction.RollbackAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
#pragma warning disable IDISP007
|
||||||
|
_transaction.Dispose();
|
||||||
|
#pragma warning restore IDISP007
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_transaction.Dispose();
|
Dispose(true);
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual async ValueTask DisposeAsyncCore()
|
||||||
|
{
|
||||||
|
#pragma warning disable IDISP007
|
||||||
|
await _transaction.DisposeAsync();
|
||||||
|
#pragma warning restore IDISP007
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
await _transaction.DisposeAsync();
|
await DisposeAsyncCore();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,17 +3,23 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<Authors>Kyle Ratti</Authors>
|
||||||
|
<RepositoryUrl>https://github.com/kyleratti/FruityFoundation</RepositoryUrl>
|
||||||
|
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||||
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE" Pack="true" PackagePath="" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\FruityFoundation.DataAccess.Abstractions\FruityFoundation.DataAccess.Abstractions.csproj" />
|
<ProjectReference Include="..\FruityFoundation.DataAccess.Abstractions\FruityFoundation.DataAccess.Abstractions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.6" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
124
FruityFoundation.DataAccess.Core/NonTransactionalDbConnection.cs
Normal file
124
FruityFoundation.DataAccess.Core/NonTransactionalDbConnection.cs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Dapper;
|
||||||
|
using FruityFoundation.DataAccess.Abstractions;
|
||||||
|
|
||||||
|
namespace FruityFoundation.DataAccess.Core;
|
||||||
|
|
||||||
|
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
|
||||||
|
public class NonTransactionalDbConnection<TConnectionType> : INonTransactionalDbConnection<TConnectionType>
|
||||||
|
where TConnectionType : ConnectionType
|
||||||
|
{
|
||||||
|
private readonly DbConnection _connection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C'tor
|
||||||
|
/// </summary>
|
||||||
|
public NonTransactionalDbConnection(DbConnection connection)
|
||||||
|
{
|
||||||
|
_connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IEnumerable<T>> Query<T>(
|
||||||
|
string sql,
|
||||||
|
object? param = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
) => await _connection.QueryAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async IAsyncEnumerable<T> QueryUnbuffered<T>(
|
||||||
|
string sql,
|
||||||
|
object? param = null,
|
||||||
|
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var query = _connection.QueryUnbufferedAsync<T>(sql, param, transaction: null)
|
||||||
|
.WithCancellation(cancellationToken);
|
||||||
|
|
||||||
|
await foreach (var item in query)
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<T> QuerySingle<T>(
|
||||||
|
string sql,
|
||||||
|
object? param = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
) => await _connection.QuerySingleAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task Execute(
|
||||||
|
string sql,
|
||||||
|
object? param = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
) => await _connection.ExecuteAsync(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<T?> ExecuteScalar<T>(
|
||||||
|
string sql,
|
||||||
|
object? param = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
) => await _connection.ExecuteScalarAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<DbDataReader> ExecuteReader(
|
||||||
|
string sql,
|
||||||
|
object? param = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
) => await _connection.ExecuteReaderAsync(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
|
await _connection.OpenAsync(cancellationToken);
|
||||||
|
|
||||||
|
var tx = await _connection.BeginTransactionAsync(cancellationToken);
|
||||||
|
|
||||||
|
return new DbTransaction<TConnectionType>(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_connection.State.HasFlag(ConnectionState.Open))
|
||||||
|
await _connection.OpenAsync(cancellationToken);
|
||||||
|
|
||||||
|
var tx = await _connection.BeginTransactionAsync(isolationLevel, cancellationToken);
|
||||||
|
|
||||||
|
return new DbTransaction<TConnectionType>(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
#pragma warning disable IDISP007
|
||||||
|
_connection.Dispose();
|
||||||
|
#pragma warning restore IDISP007
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual async ValueTask DisposeAsyncCore()
|
||||||
|
{
|
||||||
|
#pragma warning disable IDISP007
|
||||||
|
await _connection.DisposeAsync();
|
||||||
|
#pragma warning restore IDISP007
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await DisposeAsyncCore();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +0,0 @@
|
||||||
using FruityFoundation.DataAccess.Abstractions;
|
|
||||||
|
|
||||||
namespace FruityFoundation.DataAccess.Sqlite;
|
|
||||||
|
|
||||||
public class DbConnectionFactory : IDbConnectionFactory
|
|
||||||
{
|
|
||||||
private readonly string _readWriteConnectionString;
|
|
||||||
private readonly string _readOnlyConnectionString;
|
|
||||||
|
|
||||||
public DbConnectionFactory(string readWriteConnectionString, string readOnlyConnectionString)
|
|
||||||
{
|
|
||||||
_readWriteConnectionString = readWriteConnectionString;
|
|
||||||
_readOnlyConnectionString = readOnlyConnectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public INonTransactionalDbConnection<ReadWrite> CreateConnection()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_readWriteConnectionString))
|
|
||||||
throw new ApplicationException("ReadWrite connection string cannot be null or empty.");
|
|
||||||
|
|
||||||
var connection = new NonTransactionalDbConnection<ReadWrite>(_readWriteConnectionString);
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public INonTransactionalDbConnection<ReadOnly> CreateReadOnlyConnection()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(_readOnlyConnectionString))
|
|
||||||
throw new ApplicationException("ReadOnly connection string cannot be null or empty.");
|
|
||||||
|
|
||||||
var connection = new NonTransactionalDbConnection<ReadOnly>(_readOnlyConnectionString);
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
using System.Data;
|
|
||||||
using System.Data.Common;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Dapper;
|
|
||||||
using FruityFoundation.DataAccess.Abstractions;
|
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
|
|
||||||
namespace FruityFoundation.DataAccess.Sqlite;
|
|
||||||
|
|
||||||
// ReSharper disable once UnusedTypeParameter
|
|
||||||
public class NonTransactionalDbConnection<TConnectionType> : SqliteConnection, INonTransactionalDbConnection<TConnectionType>
|
|
||||||
where TConnectionType : ConnectionType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// C'tor
|
|
||||||
/// </summary>
|
|
||||||
public NonTransactionalDbConnection(string connectionString) : base(connectionString)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<IEnumerable<T>> Query<T>(
|
|
||||||
string sql,
|
|
||||||
object? param = null,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
) => await this.QueryAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async IAsyncEnumerable<T> QueryUnbuffered<T>(
|
|
||||||
string sql,
|
|
||||||
object? param = null,
|
|
||||||
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var query = this.QueryUnbufferedAsync<T>(sql, param, transaction: null)
|
|
||||||
.WithCancellation(cancellationToken);
|
|
||||||
|
|
||||||
await foreach (var item in query)
|
|
||||||
yield return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<T> QuerySingle<T>(
|
|
||||||
string sql,
|
|
||||||
object? param = null,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
) => await this.QuerySingleAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Execute(
|
|
||||||
string sql,
|
|
||||||
object? param = null,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
) => await this.ExecuteAsync(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<T?> ExecuteScalar<T>(
|
|
||||||
string sql,
|
|
||||||
object? param = null,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
) => await this.ExecuteScalarAsync<T>(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<DbDataReader> ExecuteReader(
|
|
||||||
string sql,
|
|
||||||
object? param = null,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
) => await this.ExecuteReaderAsync(new CommandDefinition(sql, param, cancellationToken: cancellationToken));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction()
|
|
||||||
{
|
|
||||||
if (!State.HasFlag(ConnectionState.Open))
|
|
||||||
await OpenAsync();
|
|
||||||
|
|
||||||
var tx = BeginTransaction();
|
|
||||||
|
|
||||||
return new DbTransaction<TConnectionType>(tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel)
|
|
||||||
{
|
|
||||||
if (!State.HasFlag(ConnectionState.Open))
|
|
||||||
await OpenAsync();
|
|
||||||
|
|
||||||
var tx = BeginTransaction(isolationLevel);
|
|
||||||
|
|
||||||
return new DbTransaction<TConnectionType>(tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<IDatabaseTransactionConnection<TConnectionType>> CreateTransaction(IsolationLevel isolationLevel, bool deferred)
|
|
||||||
{
|
|
||||||
if (!State.HasFlag(ConnectionState.Open))
|
|
||||||
await OpenAsync();
|
|
||||||
|
|
||||||
var tx = BeginTransaction(isolationLevel, deferred);
|
|
||||||
|
|
||||||
return new DbTransaction<TConnectionType>(tx);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using FruityFoundation.DataAccess.Abstractions;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace FruityFoundation.DataAccess.Sqlite;
|
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddSqliteConnectionFactory(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddSingleton<IDbConnectionFactory, DbConnectionFactory>();
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System.Data;
|
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
|
|
||||||
namespace FruityFoundation.DataAccess.Sqlite;
|
|
||||||
|
|
||||||
public static class SqliteConnectionExtensions
|
|
||||||
{
|
|
||||||
public static async Task<SqliteTransaction> CreateTransaction(this SqliteConnection connection, IsolationLevel isolationLevel)
|
|
||||||
{
|
|
||||||
await connection.OpenAsync();
|
|
||||||
return connection.BeginTransaction(isolationLevel);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
using FruityFoundation.DataAccess.Abstractions;
|
|
||||||
using FruityFoundation.DataAccess.Sqlite;
|
|
||||||
|
|
||||||
namespace FruityFoundation.Tests.DataAccess.Sqlite;
|
|
||||||
|
|
||||||
public class DbConnectionFactoryTests
|
|
||||||
{
|
|
||||||
[TestCase("")]
|
|
||||||
[TestCase(" ")]
|
|
||||||
[TestCase(null)]
|
|
||||||
public void CreateConnection_ThrowsException_WhenConnectionStringIsNullOrEmpty(string? connectionString)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var dbConnectionFactory = new DbConnectionFactory(connectionString!, readOnlyConnectionString: "ReadOnlyConnectionString");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var exception = Assert.Throws<ApplicationException>(() => dbConnectionFactory.CreateConnection());
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(exception, Is.Not.Null);
|
|
||||||
Assert.That(exception.Message, Is.EqualTo("ReadWrite connection string cannot be null or empty."));
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase("")]
|
|
||||||
[TestCase(" ")]
|
|
||||||
[TestCase(null)]
|
|
||||||
public void CreateReadOnlyConnection_ThrowsException_WhenConnectionStringIsNullOrEmpty(string? connectionString)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var dbConnectionFactory = new DbConnectionFactory(readWriteConnectionString: "connectionString", readOnlyConnectionString: null!);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var exception = Assert.Throws<ApplicationException>(() => dbConnectionFactory.CreateReadOnlyConnection());
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(exception, Is.Not.Null);
|
|
||||||
Assert.That(exception.Message, Is.EqualTo("ReadOnly connection string cannot be null or empty."));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void CreateConnection_ReturnsNonTransactionalDbConnection_WhenConnectionStringIsValid()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var dbConnectionFactory = new DbConnectionFactory(readWriteConnectionString: "Data Source=:memory:", readOnlyConnectionString: null!);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var connection = dbConnectionFactory.CreateConnection();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(connection, Is.Not.Null);
|
|
||||||
Assert.That(connection, Is.InstanceOf<INonTransactionalDbConnection<ReadWrite>>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void CreateReadOnlyConnection_ReturnsNonTransactionalDbConnection_WhenConnectionStringIsValid()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var dbConnectionFactory = new DbConnectionFactory(readWriteConnectionString: null!, readOnlyConnectionString: "Data Source=:memory:;Mode=ReadOnly");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var connection = dbConnectionFactory.CreateReadOnlyConnection();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.That(connection, Is.Not.Null);
|
|
||||||
Assert.That(connection, Is.InstanceOf<INonTransactionalDbConnection<ReadOnly>>());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
|
||||||
<IsTestProject>true</IsTestProject>
|
|
||||||
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
|
||||||
<PackageReference Include="NUnit" Version="3.14.0"/>
|
|
||||||
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
|
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="NUnit.Framework"/>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\FruityFoundation.DataAccess.Sqlite\FruityFoundation.DataAccess.Sqlite.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -18,9 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataAccess", "DataAccess",
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FruityFoundation.DataAccess.Abstractions", "FruityFoundation.DataAccess.Abstractions\FruityFoundation.DataAccess.Abstractions.csproj", "{C003E247-C62E-4830-94E4-F274D8466A5C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FruityFoundation.DataAccess.Abstractions", "FruityFoundation.DataAccess.Abstractions\FruityFoundation.DataAccess.Abstractions.csproj", "{C003E247-C62E-4830-94E4-F274D8466A5C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FruityFoundation.DataAccess.Sqlite", "FruityFoundation.DataAccess.Sqlite\FruityFoundation.DataAccess.Sqlite.csproj", "{BB25E92F-5D51-487A-8937-27E28EF5E20F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FruityFoundation.DataAccess.Core", "FruityFoundation.DataAccess.Core\FruityFoundation.DataAccess.Core.csproj", "{B65527CC-218A-4EA3-93DC-985713B5DFF4}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FruityFoundation.Tests.DataAccess.Sqlite", "FruityFoundation.Tests.DataAccess.Sqlite\FruityFoundation.Tests.DataAccess.Sqlite.csproj", "{A2E62C7C-62A1-43C0-BD60-752B0C84E518}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -56,21 +54,16 @@ Global
|
||||||
{C003E247-C62E-4830-94E4-F274D8466A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C003E247-C62E-4830-94E4-F274D8466A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{C003E247-C62E-4830-94E4-F274D8466A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C003E247-C62E-4830-94E4-F274D8466A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C003E247-C62E-4830-94E4-F274D8466A5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{C003E247-C62E-4830-94E4-F274D8466A5C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{BB25E92F-5D51-487A-8937-27E28EF5E20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B65527CC-218A-4EA3-93DC-985713B5DFF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{BB25E92F-5D51-487A-8937-27E28EF5E20F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B65527CC-218A-4EA3-93DC-985713B5DFF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{BB25E92F-5D51-487A-8937-27E28EF5E20F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B65527CC-218A-4EA3-93DC-985713B5DFF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{BB25E92F-5D51-487A-8937-27E28EF5E20F}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B65527CC-218A-4EA3-93DC-985713B5DFF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{A2E62C7C-62A1-43C0-BD60-752B0C84E518}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A2E62C7C-62A1-43C0-BD60-752B0C84E518}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A2E62C7C-62A1-43C0-BD60-752B0C84E518}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A2E62C7C-62A1-43C0-BD60-752B0C84E518}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{50A75644-A1C3-4495-9DEB-DBB12D9334B5} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
{50A75644-A1C3-4495-9DEB-DBB12D9334B5} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
||||||
{EBDC3640-4E47-43FE-BF0D-4BFFD07FE2EF} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
{EBDC3640-4E47-43FE-BF0D-4BFFD07FE2EF} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
||||||
{A64E73D3-EF87-4938-B01E-F9CC0B59F9DE} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
{A64E73D3-EF87-4938-B01E-F9CC0B59F9DE} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
||||||
{C003E247-C62E-4830-94E4-F274D8466A5C} = {5C3A014A-7931-4A36-95F0-5EFE15AB06A3}
|
{C003E247-C62E-4830-94E4-F274D8466A5C} = {5C3A014A-7931-4A36-95F0-5EFE15AB06A3}
|
||||||
{BB25E92F-5D51-487A-8937-27E28EF5E20F} = {5C3A014A-7931-4A36-95F0-5EFE15AB06A3}
|
{B65527CC-218A-4EA3-93DC-985713B5DFF4} = {5C3A014A-7931-4A36-95F0-5EFE15AB06A3}
|
||||||
{A2E62C7C-62A1-43C0-BD60-752B0C84E518} = {B44178DF-5B81-4029-90FA-2BF8E2A1EDBF}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
Loading…
Reference in New Issue
Block a user