using MedicalDatabaseContracts; using MedicalDatabaseContracts.Models; using Microsoft.Extensions.Logging; using Npgsql; using System.Diagnostics; using System.Text; namespace MedicalPostgresqlDatabase { public abstract class AbstractPostgresqlStorage : IStorage where T : AbstractModel { protected ILogger _logger; protected readonly string TABLE_NAME; protected readonly string PRIMARY_KEY_COLUMN_NAME; protected readonly string PRIMARY_KEY_SEQUENCE_NAME; protected readonly string TIME_LOG_FILENAME_SUFFIX; protected readonly string TIME_LOG_FOLDERNAME; protected AbstractPostgresqlStorage( ILogger> logger, string tableName, string primaryKeyColumnName, string primary_key_sequence_name) { _logger = logger; TABLE_NAME = tableName; PRIMARY_KEY_COLUMN_NAME = primaryKeyColumnName; PRIMARY_KEY_SEQUENCE_NAME = primary_key_sequence_name; TIME_LOG_FILENAME_SUFFIX = "sqltimelog.csv"; TIME_LOG_FOLDERNAME = "timelogs"; } protected abstract T CreateEntityFromReader(NpgsqlDataReader reader); protected abstract Dictionary GetEntityAttributesDictionary(T item); public void DeleteAll(out double elapsedMilliseconds) { using var connection = GetConnection(); connection.Open(); using var cmd = new NpgsqlCommand($"DELETE FROM {TABLE_NAME}", connection); _logger.LogDebug(cmd.CommandText); Stopwatch stopwatch = new(); stopwatch.Start(); cmd.ExecuteNonQuery(); stopwatch.Stop(); elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; WriteTimeToFile(stopwatch, "DELETEALL"); } public void DeleteAll() { DeleteAll(out _); } protected void WriteTimeToFile(Stopwatch stopwatch, string filenamePrefix) { if (!Directory.Exists(TIME_LOG_FOLDERNAME)) { Directory.CreateDirectory(TIME_LOG_FOLDERNAME); } string filename = GetType().Name + "_" + filenamePrefix + "_" + TIME_LOG_FILENAME_SUFFIX; string path = Path.Combine(TIME_LOG_FOLDERNAME, filename); using (StreamWriter sw = new StreamWriter(path, true)) { sw.WriteLine(stopwatch.Elapsed.TotalMilliseconds); } } public virtual void Delete(int id) { Delete(id, out _); } public virtual void Delete(int id, out double elapsedMilliseconds) { using var connection = GetConnection(); connection.Open(); using var cmd = new NpgsqlCommand($"DELETE FROM {TABLE_NAME} WHERE {PRIMARY_KEY_COLUMN_NAME} = @id", connection); cmd.Parameters.AddWithValue("@id", id); _logger.LogDebug(cmd.CommandText); Stopwatch stopwatch = new(); stopwatch.Start(); cmd.ExecuteNonQuery(); stopwatch.Stop(); elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; WriteTimeToFile(stopwatch, "DELETE"); } public virtual T? Get(int id) { return Get(id, out _); } public virtual T? Get(int id, out double elapsedMilliseconds) { using var connection = GetConnection(); connection.Open(); using var cmd = new NpgsqlCommand($"SELECT * FROM {TABLE_NAME} WHERE {PRIMARY_KEY_COLUMN_NAME} = @id", connection); cmd.Parameters.AddWithValue($"@id", id); _logger.LogDebug(cmd.CommandText); Stopwatch stopwatch = new(); stopwatch.Start(); using var reader = cmd.ExecuteReader(); stopwatch.Stop(); elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; WriteTimeToFile(stopwatch, "GET"); if (reader.Read()) { return CreateEntityFromReader(reader); } return null; } public virtual List GetAll() { return GetAll(out _); } public virtual List GetAll(out double elapsedMilliseconds) { var items = new List(); using var connection = GetConnection(); connection.Open(); using var cmd = new NpgsqlCommand($"SELECT * FROM {TABLE_NAME} ORDER BY {PRIMARY_KEY_COLUMN_NAME}", connection); _logger.LogDebug(cmd.CommandText); Stopwatch stopwatch = new(); stopwatch.Start(); using var reader = cmd.ExecuteReader(); stopwatch.Stop(); elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; WriteTimeToFile(stopwatch, "GETALL"); while (reader.Read()) { items.Add(CreateEntityFromReader(reader)); } return items; } public virtual void Insert(T item) { Insert(item, out _); } public virtual void Insert(T item, out double elapsedMilliseconds) { using var connection = GetConnection(); connection.Open(); var dict = GetEntityAttributesDictionary(item); string namesString = ""; string valuesString = ""; foreach (var key in dict.Keys) { namesString += (string.IsNullOrEmpty(namesString) ? "" : ", ") + $"{key}"; if (key == PRIMARY_KEY_COLUMN_NAME) valuesString += (string.IsNullOrEmpty(valuesString) ? "" : ", ") + $"nextval('{PRIMARY_KEY_SEQUENCE_NAME}')"; else valuesString += (string.IsNullOrEmpty(valuesString) ? "" : ", ") + $"@{key}"; } namesString = $"({namesString})"; valuesString = $"({valuesString})"; string[] queryBuilder = { "INSERT INTO", TABLE_NAME, namesString, "VALUES", valuesString }; using var cmd = new NpgsqlCommand(string.Join(' ', queryBuilder), connection); foreach (var key in dict.Keys) { cmd.Parameters.AddWithValue($"@{key}", dict[key] ?? DBNull.Value); } _logger.LogDebug(cmd.CommandText); Stopwatch stopwatch = new(); stopwatch.Start(); cmd.ExecuteNonQuery(); stopwatch.Stop(); elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; WriteTimeToFile(stopwatch, "INSERT"); } public virtual void Update(T item) { Update(item, out _); } public virtual void Update(T item, out double elapsedMilliseconds) { using var connection = GetConnection(); connection.Open(); var dict = GetEntityAttributesDictionary(item); string setString = ""; foreach (var key in dict.Keys) { if (key != PRIMARY_KEY_COLUMN_NAME) setString += (string.IsNullOrEmpty(setString) ? "" : ", ") + $"{key} = @{key}"; } string[] queryBuilder = { "UPDATE", TABLE_NAME, "SET", setString, "WHERE", $"{PRIMARY_KEY_COLUMN_NAME} = @{PRIMARY_KEY_COLUMN_NAME}" }; using var cmd = new NpgsqlCommand(string.Join(' ', queryBuilder), connection); foreach (var key in dict.Keys) { cmd.Parameters.AddWithValue($"@{key}", dict[key] ?? DBNull.Value); } _logger.LogDebug(cmd.CommandText); Stopwatch stopwatch = new(); stopwatch.Start(); cmd.ExecuteNonQuery(); stopwatch.Stop(); elapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; WriteTimeToFile(stopwatch, "UPDATE"); } protected NpgsqlConnection GetConnection() { return new NpgsqlConnection("Host=127.0.0.1;Port=5555;Username=postgres;Database=medicalbase;Password=postgres"); } } }