feat: upgrade to .NET 6, refactor everything
This commit is contained in:
		@@ -1,17 +1,13 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.Core
 | 
			
		||||
namespace PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
public interface IPKCommand: IDbCommand, IAsyncDisposable
 | 
			
		||||
{
 | 
			
		||||
    public interface IPKCommand: IDbCommand, IAsyncDisposable
 | 
			
		||||
    {
 | 
			
		||||
        public Task PrepareAsync(CancellationToken ct = default);
 | 
			
		||||
        public Task<int> ExecuteNonQueryAsync(CancellationToken ct = default);
 | 
			
		||||
        public Task<object?> ExecuteScalarAsync(CancellationToken ct = default);
 | 
			
		||||
        public Task<DbDataReader> ExecuteReaderAsync(CancellationToken ct = default);
 | 
			
		||||
        public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken ct = default);
 | 
			
		||||
    }
 | 
			
		||||
    public Task PrepareAsync(CancellationToken ct = default);
 | 
			
		||||
    public Task<int> ExecuteNonQueryAsync(CancellationToken ct = default);
 | 
			
		||||
    public Task<object?> ExecuteScalarAsync(CancellationToken ct = default);
 | 
			
		||||
    public Task<DbDataReader> ExecuteReaderAsync(CancellationToken ct = default);
 | 
			
		||||
    public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken ct = default);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +1,29 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
using Npgsql;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.Core
 | 
			
		||||
namespace PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
public interface IPKConnection: IDbConnection, IAsyncDisposable
 | 
			
		||||
{
 | 
			
		||||
    public interface IPKConnection: IDbConnection, IAsyncDisposable
 | 
			
		||||
    {
 | 
			
		||||
        public Guid ConnectionId { get; }
 | 
			
		||||
    public Guid ConnectionId { get; }
 | 
			
		||||
 | 
			
		||||
        public Task OpenAsync(CancellationToken cancellationToken = default);
 | 
			
		||||
        public Task CloseAsync();
 | 
			
		||||
    public Task OpenAsync(CancellationToken cancellationToken = default);
 | 
			
		||||
    public Task CloseAsync();
 | 
			
		||||
 | 
			
		||||
        public Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default);
 | 
			
		||||
    public Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default);
 | 
			
		||||
 | 
			
		||||
        public ValueTask<IPKTransaction> BeginTransactionAsync(CancellationToken ct = default) => BeginTransactionAsync(IsolationLevel.Unspecified, ct);
 | 
			
		||||
        public ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default);
 | 
			
		||||
    public ValueTask<IPKTransaction> BeginTransactionAsync(CancellationToken ct = default) =>
 | 
			
		||||
        BeginTransactionAsync(IsolationLevel.Unspecified, ct);
 | 
			
		||||
 | 
			
		||||
        public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand);
 | 
			
		||||
        public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand);
 | 
			
		||||
    public ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default);
 | 
			
		||||
 | 
			
		||||
        [Obsolete] new void Open();
 | 
			
		||||
        [Obsolete] new void Close();
 | 
			
		||||
    public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand);
 | 
			
		||||
    public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand);
 | 
			
		||||
 | 
			
		||||
        [Obsolete] new IDbTransaction BeginTransaction();
 | 
			
		||||
        [Obsolete] new IDbTransaction BeginTransaction(IsolationLevel il);
 | 
			
		||||
    }
 | 
			
		||||
    [Obsolete] new void Open();
 | 
			
		||||
    [Obsolete] new void Close();
 | 
			
		||||
 | 
			
		||||
    [Obsolete] new IDbTransaction BeginTransaction();
 | 
			
		||||
    [Obsolete] new IDbTransaction BeginTransaction(IsolationLevel il);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,9 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.Core
 | 
			
		||||
namespace PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
public interface IPKTransaction: IDbTransaction, IAsyncDisposable
 | 
			
		||||
{
 | 
			
		||||
    public interface IPKTransaction: IDbTransaction, IAsyncDisposable
 | 
			
		||||
    {
 | 
			
		||||
        public Task CommitAsync(CancellationToken ct = default);
 | 
			
		||||
        public Task RollbackAsync(CancellationToken ct = default);
 | 
			
		||||
    }
 | 
			
		||||
    public Task CommitAsync(CancellationToken ct = default);
 | 
			
		||||
    public Task RollbackAsync(CancellationToken ct = default);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,7 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
using App.Metrics;
 | 
			
		||||
 | 
			
		||||
@@ -14,113 +11,125 @@ using Npgsql;
 | 
			
		||||
 | 
			
		||||
using Serilog;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.Core
 | 
			
		||||
namespace PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
internal class PKCommand: DbCommand, IPKCommand
 | 
			
		||||
{
 | 
			
		||||
    internal class PKCommand: DbCommand, IPKCommand
 | 
			
		||||
    private readonly ILogger _logger;
 | 
			
		||||
    private readonly IMetrics _metrics;
 | 
			
		||||
 | 
			
		||||
    private readonly PKConnection _ourConnection;
 | 
			
		||||
 | 
			
		||||
    public PKCommand(NpgsqlCommand inner, PKConnection ourConnection, ILogger logger, IMetrics metrics)
 | 
			
		||||
    {
 | 
			
		||||
        private NpgsqlCommand Inner { get; }
 | 
			
		||||
 | 
			
		||||
        private readonly PKConnection _ourConnection;
 | 
			
		||||
        private readonly ILogger _logger;
 | 
			
		||||
        private readonly IMetrics _metrics;
 | 
			
		||||
 | 
			
		||||
        public PKCommand(NpgsqlCommand inner, PKConnection ourConnection, ILogger logger, IMetrics metrics)
 | 
			
		||||
        {
 | 
			
		||||
            Inner = inner;
 | 
			
		||||
            _ourConnection = ourConnection;
 | 
			
		||||
            _logger = logger.ForContext<PKCommand>();
 | 
			
		||||
            _metrics = metrics;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override Task<int> ExecuteNonQueryAsync(CancellationToken ct) => LogQuery(Inner.ExecuteNonQueryAsync(ct));
 | 
			
		||||
        public override Task<object?> ExecuteScalarAsync(CancellationToken ct) => LogQuery(Inner.ExecuteScalarAsync(ct));
 | 
			
		||||
        protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken ct) => await LogQuery(Inner.ExecuteReaderAsync(behavior, ct));
 | 
			
		||||
 | 
			
		||||
        public override Task PrepareAsync(CancellationToken ct = default) => Inner.PrepareAsync(ct);
 | 
			
		||||
        public override void Cancel() => Inner.Cancel();
 | 
			
		||||
        protected override DbParameter CreateDbParameter() => Inner.CreateParameter();
 | 
			
		||||
 | 
			
		||||
        [AllowNull]
 | 
			
		||||
        public override string CommandText
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.CommandText;
 | 
			
		||||
            set => Inner.CommandText = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int CommandTimeout
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.CommandTimeout;
 | 
			
		||||
            set => Inner.CommandTimeout = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override CommandType CommandType
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.CommandType;
 | 
			
		||||
            set => Inner.CommandType = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override UpdateRowSource UpdatedRowSource
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.UpdatedRowSource;
 | 
			
		||||
            set => Inner.UpdatedRowSource = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DbParameterCollection DbParameterCollection => Inner.Parameters;
 | 
			
		||||
        protected override DbTransaction? DbTransaction
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.Transaction;
 | 
			
		||||
            set => Inner.Transaction = value switch
 | 
			
		||||
            {
 | 
			
		||||
                NpgsqlTransaction npg => npg,
 | 
			
		||||
                PKTransaction pk => pk.Inner,
 | 
			
		||||
                _ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlTransaction")
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool DesignTimeVisible
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.DesignTimeVisible;
 | 
			
		||||
            set => Inner.DesignTimeVisible = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override DbConnection? DbConnection
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.Connection;
 | 
			
		||||
            set =>
 | 
			
		||||
                Inner.Connection = value switch
 | 
			
		||||
                {
 | 
			
		||||
                    NpgsqlConnection npg => npg,
 | 
			
		||||
                    PKConnection pk => pk.Inner,
 | 
			
		||||
                    _ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlConnection")
 | 
			
		||||
                };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int ExecuteNonQuery() => throw SyncError(nameof(ExecuteNonQuery));
 | 
			
		||||
        public override object ExecuteScalar() => throw SyncError(nameof(ExecuteScalar));
 | 
			
		||||
        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) => throw SyncError(nameof(ExecuteDbDataReader));
 | 
			
		||||
        public override void Prepare() => throw SyncError(nameof(Prepare));
 | 
			
		||||
 | 
			
		||||
        private async Task<T> LogQuery<T>(Task<T> task)
 | 
			
		||||
        {
 | 
			
		||||
            var start = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                return await task;
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                var end = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
                var elapsed = end - start;
 | 
			
		||||
 | 
			
		||||
                _logger.Verbose("Executed query {Query} in {ElapsedTime} on connection {ConnectionId}", CommandText, elapsed, _ourConnection.ConnectionId);
 | 
			
		||||
 | 
			
		||||
                // One "BCL compatible tick" is 100 nanoseconds
 | 
			
		||||
                var micros = elapsed.BclCompatibleTicks / 10;
 | 
			
		||||
                _metrics.Provider.Timer.Instance(CoreMetrics.DatabaseQuery, new MetricTags("query", CommandText))
 | 
			
		||||
                    .Record(micros, TimeUnit.Microseconds, CommandText);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbCommand function {caller}!");
 | 
			
		||||
        Inner = inner;
 | 
			
		||||
        _ourConnection = ourConnection;
 | 
			
		||||
        _logger = logger.ForContext<PKCommand>();
 | 
			
		||||
        _metrics = metrics;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private NpgsqlCommand Inner { get; }
 | 
			
		||||
 | 
			
		||||
    protected override DbParameterCollection DbParameterCollection => Inner.Parameters;
 | 
			
		||||
 | 
			
		||||
    protected override DbTransaction? DbTransaction
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.Transaction;
 | 
			
		||||
        set => Inner.Transaction = value switch
 | 
			
		||||
        {
 | 
			
		||||
            NpgsqlTransaction npg => npg,
 | 
			
		||||
            PKTransaction pk => pk.Inner,
 | 
			
		||||
            _ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlTransaction")
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool DesignTimeVisible
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.DesignTimeVisible;
 | 
			
		||||
        set => Inner.DesignTimeVisible = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override DbConnection? DbConnection
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.Connection;
 | 
			
		||||
        set =>
 | 
			
		||||
            Inner.Connection = value switch
 | 
			
		||||
            {
 | 
			
		||||
                NpgsqlConnection npg => npg,
 | 
			
		||||
                PKConnection pk => pk.Inner,
 | 
			
		||||
                _ => throw new ArgumentException($"Can't convert input type {value?.GetType()} to NpgsqlConnection")
 | 
			
		||||
            };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override Task<int> ExecuteNonQueryAsync(CancellationToken ct) =>
 | 
			
		||||
        LogQuery(Inner.ExecuteNonQueryAsync(ct));
 | 
			
		||||
 | 
			
		||||
    public override Task<object?> ExecuteScalarAsync(CancellationToken ct) =>
 | 
			
		||||
        LogQuery(Inner.ExecuteScalarAsync(ct));
 | 
			
		||||
 | 
			
		||||
    public override Task PrepareAsync(CancellationToken ct = default) => Inner.PrepareAsync(ct);
 | 
			
		||||
    public override void Cancel() => Inner.Cancel();
 | 
			
		||||
 | 
			
		||||
    [AllowNull]
 | 
			
		||||
    public override string CommandText
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.CommandText;
 | 
			
		||||
        set => Inner.CommandText = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override int CommandTimeout
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.CommandTimeout;
 | 
			
		||||
        set => Inner.CommandTimeout = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override CommandType CommandType
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.CommandType;
 | 
			
		||||
        set => Inner.CommandType = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override UpdateRowSource UpdatedRowSource
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.UpdatedRowSource;
 | 
			
		||||
        set => Inner.UpdatedRowSource = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override int ExecuteNonQuery() => throw SyncError(nameof(ExecuteNonQuery));
 | 
			
		||||
    public override object ExecuteScalar() => throw SyncError(nameof(ExecuteScalar));
 | 
			
		||||
    public override void Prepare() => throw SyncError(nameof(Prepare));
 | 
			
		||||
 | 
			
		||||
    protected override async Task<DbDataReader>
 | 
			
		||||
        ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken ct) =>
 | 
			
		||||
        await LogQuery(Inner.ExecuteReaderAsync(behavior, ct));
 | 
			
		||||
 | 
			
		||||
    protected override DbParameter CreateDbParameter() => Inner.CreateParameter();
 | 
			
		||||
 | 
			
		||||
    protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) =>
 | 
			
		||||
        throw SyncError(nameof(ExecuteDbDataReader));
 | 
			
		||||
 | 
			
		||||
    private async Task<T> LogQuery<T>(Task<T> task)
 | 
			
		||||
    {
 | 
			
		||||
        var start = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            return await task;
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            var end = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
            var elapsed = end - start;
 | 
			
		||||
 | 
			
		||||
            _logger.Verbose("Executed query {Query} in {ElapsedTime} on connection {ConnectionId}", CommandText,
 | 
			
		||||
                elapsed, _ourConnection.ConnectionId);
 | 
			
		||||
 | 
			
		||||
            // One "BCL compatible tick" is 100 nanoseconds
 | 
			
		||||
            var micros = elapsed.BclCompatibleTicks / 10;
 | 
			
		||||
            _metrics.Provider.Timer.Instance(CoreMetrics.DatabaseQuery, new MetricTags("query", CommandText))
 | 
			
		||||
                .Record(micros, TimeUnit.Microseconds, CommandText);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Exception SyncError(string caller) =>
 | 
			
		||||
        throw new Exception($"Executed synchronous IDbCommand function {caller}!");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,7 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
using App.Metrics;
 | 
			
		||||
 | 
			
		||||
@@ -14,102 +11,120 @@ using Npgsql;
 | 
			
		||||
 | 
			
		||||
using Serilog;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.Core
 | 
			
		||||
namespace PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
internal class PKConnection: DbConnection, IPKConnection
 | 
			
		||||
{
 | 
			
		||||
    internal class PKConnection: DbConnection, IPKConnection
 | 
			
		||||
    private readonly DbConnectionCountHolder _countHolder;
 | 
			
		||||
    private readonly ILogger _logger;
 | 
			
		||||
    private readonly IMetrics _metrics;
 | 
			
		||||
    private bool _hasClosed;
 | 
			
		||||
 | 
			
		||||
    private bool _hasOpened;
 | 
			
		||||
    private Instant _openTime;
 | 
			
		||||
 | 
			
		||||
    public PKConnection(NpgsqlConnection inner, DbConnectionCountHolder countHolder, ILogger logger,
 | 
			
		||||
                        IMetrics metrics)
 | 
			
		||||
    {
 | 
			
		||||
        public NpgsqlConnection Inner { get; }
 | 
			
		||||
        public Guid ConnectionId { get; }
 | 
			
		||||
 | 
			
		||||
        private readonly DbConnectionCountHolder _countHolder;
 | 
			
		||||
        private readonly ILogger _logger;
 | 
			
		||||
        private readonly IMetrics _metrics;
 | 
			
		||||
 | 
			
		||||
        private bool _hasOpened;
 | 
			
		||||
        private bool _hasClosed;
 | 
			
		||||
        private Instant _openTime;
 | 
			
		||||
 | 
			
		||||
        public PKConnection(NpgsqlConnection inner, DbConnectionCountHolder countHolder, ILogger logger, IMetrics metrics)
 | 
			
		||||
        {
 | 
			
		||||
            Inner = inner;
 | 
			
		||||
            ConnectionId = Guid.NewGuid();
 | 
			
		||||
            _countHolder = countHolder;
 | 
			
		||||
            _logger = logger.ForContext<PKConnection>();
 | 
			
		||||
            _metrics = metrics;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override Task OpenAsync(CancellationToken ct)
 | 
			
		||||
        {
 | 
			
		||||
            if (_hasOpened) return Inner.OpenAsync(ct);
 | 
			
		||||
            _countHolder.Increment();
 | 
			
		||||
            _hasOpened = true;
 | 
			
		||||
            _openTime = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
            _logger.Verbose("Opened database connection {ConnectionId}, new connection count {ConnectionCount}", ConnectionId, _countHolder.ConnectionCount);
 | 
			
		||||
            return Inner.OpenAsync(ct);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override Task CloseAsync() => Inner.CloseAsync();
 | 
			
		||||
 | 
			
		||||
        protected override DbCommand CreateDbCommand() => new PKCommand(Inner.CreateCommand(), this, _logger, _metrics);
 | 
			
		||||
 | 
			
		||||
        public void ReloadTypes() => Inner.ReloadTypes();
 | 
			
		||||
 | 
			
		||||
        public new async ValueTask<IPKTransaction> BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default) => new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
 | 
			
		||||
 | 
			
		||||
        public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand) => Inner.BeginBinaryImport(copyFromCommand);
 | 
			
		||||
        public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand) => Inner.BeginBinaryExport(copyToCommand);
 | 
			
		||||
 | 
			
		||||
        public override void ChangeDatabase(string databaseName) => Inner.ChangeDatabase(databaseName);
 | 
			
		||||
        public override Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default) => Inner.ChangeDatabaseAsync(databaseName, ct);
 | 
			
		||||
        protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => throw SyncError(nameof(BeginDbTransaction));
 | 
			
		||||
        protected override async ValueTask<DbTransaction> BeginDbTransactionAsync(IsolationLevel level, CancellationToken ct) => new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
 | 
			
		||||
 | 
			
		||||
        public override void Open() => throw SyncError(nameof(Open));
 | 
			
		||||
        public override void Close()
 | 
			
		||||
        {
 | 
			
		||||
            // Don't throw SyncError here, Dapper calls sync Close() internally so that sucks
 | 
			
		||||
            Inner.Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IDbTransaction IPKConnection.BeginTransaction() => throw SyncError(nameof(BeginTransaction));
 | 
			
		||||
        IDbTransaction IPKConnection.BeginTransaction(IsolationLevel level) => throw SyncError(nameof(BeginTransaction));
 | 
			
		||||
 | 
			
		||||
        [AllowNull]
 | 
			
		||||
        public override string ConnectionString
 | 
			
		||||
        {
 | 
			
		||||
            get => Inner.ConnectionString;
 | 
			
		||||
            set => Inner.ConnectionString = value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override string Database => Inner.Database!;
 | 
			
		||||
        public override ConnectionState State => Inner.State;
 | 
			
		||||
        public override string DataSource => Inner.DataSource;
 | 
			
		||||
        public override string ServerVersion => Inner.ServerVersion;
 | 
			
		||||
 | 
			
		||||
        protected override void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            Inner.Dispose();
 | 
			
		||||
            if (_hasClosed) return;
 | 
			
		||||
 | 
			
		||||
            LogClose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override ValueTask DisposeAsync()
 | 
			
		||||
        {
 | 
			
		||||
            if (_hasClosed) return Inner.DisposeAsync();
 | 
			
		||||
            LogClose();
 | 
			
		||||
            return Inner.DisposeAsync();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void LogClose()
 | 
			
		||||
        {
 | 
			
		||||
            _countHolder.Decrement();
 | 
			
		||||
            _hasClosed = true;
 | 
			
		||||
 | 
			
		||||
            var duration = SystemClock.Instance.GetCurrentInstant() - _openTime;
 | 
			
		||||
            _logger.Verbose("Closed database connection {ConnectionId} (open for {ConnectionDuration}), new connection count {ConnectionCount}", ConnectionId, duration, _countHolder.ConnectionCount);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbCommand function {caller}!");
 | 
			
		||||
        Inner = inner;
 | 
			
		||||
        ConnectionId = Guid.NewGuid();
 | 
			
		||||
        _countHolder = countHolder;
 | 
			
		||||
        _logger = logger.ForContext<PKConnection>();
 | 
			
		||||
        _metrics = metrics;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NpgsqlConnection Inner { get; }
 | 
			
		||||
    public override string DataSource => Inner.DataSource;
 | 
			
		||||
    public override string ServerVersion => Inner.ServerVersion;
 | 
			
		||||
    public Guid ConnectionId { get; }
 | 
			
		||||
 | 
			
		||||
    public override Task OpenAsync(CancellationToken ct)
 | 
			
		||||
    {
 | 
			
		||||
        if (_hasOpened) return Inner.OpenAsync(ct);
 | 
			
		||||
        _countHolder.Increment();
 | 
			
		||||
        _hasOpened = true;
 | 
			
		||||
        _openTime = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
        _logger.Verbose("Opened database connection {ConnectionId}, new connection count {ConnectionCount}",
 | 
			
		||||
            ConnectionId, _countHolder.ConnectionCount);
 | 
			
		||||
        return Inner.OpenAsync(ct);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override Task CloseAsync() => Inner.CloseAsync();
 | 
			
		||||
 | 
			
		||||
    public new async ValueTask<IPKTransaction>
 | 
			
		||||
        BeginTransactionAsync(IsolationLevel level, CancellationToken ct = default) =>
 | 
			
		||||
        new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
 | 
			
		||||
 | 
			
		||||
    public NpgsqlBinaryImporter BeginBinaryImport(string copyFromCommand) =>
 | 
			
		||||
        Inner.BeginBinaryImport(copyFromCommand);
 | 
			
		||||
 | 
			
		||||
    public NpgsqlBinaryExporter BeginBinaryExport(string copyToCommand) => Inner.BeginBinaryExport(copyToCommand);
 | 
			
		||||
 | 
			
		||||
    public override void ChangeDatabase(string databaseName) => Inner.ChangeDatabase(databaseName);
 | 
			
		||||
 | 
			
		||||
    public override Task ChangeDatabaseAsync(string databaseName, CancellationToken ct = default) =>
 | 
			
		||||
        Inner.ChangeDatabaseAsync(databaseName, ct);
 | 
			
		||||
 | 
			
		||||
    public override void Open() => throw SyncError(nameof(Open));
 | 
			
		||||
 | 
			
		||||
    public override void Close()
 | 
			
		||||
    {
 | 
			
		||||
        // Don't throw SyncError here, Dapper calls sync Close() internally so that sucks
 | 
			
		||||
        Inner.Close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IDbTransaction IPKConnection.BeginTransaction() => throw SyncError(nameof(BeginTransaction));
 | 
			
		||||
 | 
			
		||||
    IDbTransaction IPKConnection.BeginTransaction(IsolationLevel level) =>
 | 
			
		||||
        throw SyncError(nameof(BeginTransaction));
 | 
			
		||||
 | 
			
		||||
    [AllowNull]
 | 
			
		||||
    public override string ConnectionString
 | 
			
		||||
    {
 | 
			
		||||
        get => Inner.ConnectionString;
 | 
			
		||||
        set => Inner.ConnectionString = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override string Database => Inner.Database!;
 | 
			
		||||
    public override ConnectionState State => Inner.State;
 | 
			
		||||
 | 
			
		||||
    public override ValueTask DisposeAsync()
 | 
			
		||||
    {
 | 
			
		||||
        if (_hasClosed) return Inner.DisposeAsync();
 | 
			
		||||
        LogClose();
 | 
			
		||||
        return Inner.DisposeAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override DbCommand CreateDbCommand() => new PKCommand(Inner.CreateCommand(), this, _logger, _metrics);
 | 
			
		||||
 | 
			
		||||
    public void ReloadTypes() => Inner.ReloadTypes();
 | 
			
		||||
 | 
			
		||||
    protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) =>
 | 
			
		||||
        throw SyncError(nameof(BeginDbTransaction));
 | 
			
		||||
 | 
			
		||||
    protected override async ValueTask<DbTransaction>
 | 
			
		||||
        BeginDbTransactionAsync(IsolationLevel level, CancellationToken ct) =>
 | 
			
		||||
        new PKTransaction(await Inner.BeginTransactionAsync(level, ct));
 | 
			
		||||
 | 
			
		||||
    protected override void Dispose(bool disposing)
 | 
			
		||||
    {
 | 
			
		||||
        Inner.Dispose();
 | 
			
		||||
        if (_hasClosed) return;
 | 
			
		||||
 | 
			
		||||
        LogClose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void LogClose()
 | 
			
		||||
    {
 | 
			
		||||
        _countHolder.Decrement();
 | 
			
		||||
        _hasClosed = true;
 | 
			
		||||
 | 
			
		||||
        var duration = SystemClock.Instance.GetCurrentInstant() - _openTime;
 | 
			
		||||
        _logger.Verbose(
 | 
			
		||||
            "Closed database connection {ConnectionId} (open for {ConnectionDuration}), new connection count {ConnectionCount}",
 | 
			
		||||
            ConnectionId, duration, _countHolder.ConnectionCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Exception SyncError(string caller) =>
 | 
			
		||||
        throw new Exception($"Executed synchronous IDbCommand function {caller}!");
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +1,28 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Data;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
using Npgsql;
 | 
			
		||||
 | 
			
		||||
namespace PluralKit.Core
 | 
			
		||||
namespace PluralKit.Core;
 | 
			
		||||
 | 
			
		||||
internal class PKTransaction: DbTransaction, IPKTransaction
 | 
			
		||||
{
 | 
			
		||||
    internal class PKTransaction: DbTransaction, IPKTransaction
 | 
			
		||||
    public PKTransaction(NpgsqlTransaction inner)
 | 
			
		||||
    {
 | 
			
		||||
        public NpgsqlTransaction Inner { get; }
 | 
			
		||||
 | 
			
		||||
        public PKTransaction(NpgsqlTransaction inner)
 | 
			
		||||
        {
 | 
			
		||||
            Inner = inner;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override void Commit() => throw SyncError(nameof(Commit));
 | 
			
		||||
        public override Task CommitAsync(CancellationToken ct = default) => Inner.CommitAsync(ct);
 | 
			
		||||
 | 
			
		||||
        public override void Rollback() => throw SyncError(nameof(Rollback));
 | 
			
		||||
        public override Task RollbackAsync(CancellationToken ct = default) => Inner.RollbackAsync(ct);
 | 
			
		||||
 | 
			
		||||
        protected override DbConnection DbConnection => Inner.Connection;
 | 
			
		||||
        public override IsolationLevel IsolationLevel => Inner.IsolationLevel;
 | 
			
		||||
 | 
			
		||||
        private static Exception SyncError(string caller) => throw new Exception($"Executed synchronous IDbTransaction function {caller}!");
 | 
			
		||||
        Inner = inner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NpgsqlTransaction Inner { get; }
 | 
			
		||||
 | 
			
		||||
    protected override DbConnection DbConnection => Inner.Connection;
 | 
			
		||||
 | 
			
		||||
    public override void Commit() => throw SyncError(nameof(Commit));
 | 
			
		||||
    public override Task CommitAsync(CancellationToken ct = default) => Inner.CommitAsync(ct);
 | 
			
		||||
 | 
			
		||||
    public override void Rollback() => throw SyncError(nameof(Rollback));
 | 
			
		||||
    public override Task RollbackAsync(CancellationToken ct = default) => Inner.RollbackAsync(ct);
 | 
			
		||||
    public override IsolationLevel IsolationLevel => Inner.IsolationLevel;
 | 
			
		||||
 | 
			
		||||
    private static Exception SyncError(string caller) =>
 | 
			
		||||
        throw new Exception($"Executed synchronous IDbTransaction function {caller}!");
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user