I have two different connection strings for two different databases.
My first database has 2 relational tables: -First table has some default fields,among which average on UI, and some additional fields based on second database that are even displayed in UI and that change based on second database. -Second table has all numbers used to calculate the final average in first table.
My second database has one table: -contain additional fields that are in first table of first database and the final average saved in first table of first database calculated in UI. Additional fields can change if the database source is changed, the average field is always in database for default.
My goal is to save in same transaction data for all two tables of first database and save data for second database, and allow operations in different IDbConnections with same generic repistory base.
My idea is to have one repository for every entity, in this case are 3 entities for all three tables, and only one service class for first database and another service for second database.
public interface IRepository<T> where T:class
{
// one method for semplicity
Task<T> GetByIdAsync(int id);
}
//repository for first table of first database.
//FirstDatabaseSource contains properties that are for default in that table and they must be there, this repository accept entities that inherits from that, so in this way i can pass class that hase defaults properties and additional properties of second database table
public interface IFirstTableFirstDatabaseRepository<T> : IRepository<T> where T : FirstDatabaseFirstEntityBase
{
}
public interface ISecondTableFirstDatabaseRepository<T> : IRepository<T> where T : FirstDatabaseSecondEntityBase
{
}
public class FirstTableFirstDatabaseRepositorySQL:IFirstTableFirstDatabaseRepository<FirstTableEntity>
{
DbDataAccess dataAccess;
public FirstTableFirstDatabaseRepositorySQL()
{
dataAccess = new DbDataAccess(new SqlConnection());
}
public async Task GetByIdAsync(int id)
{
// I injected the IDbConnection because query parameters syntax(for example in ms access is ?, in sql is @ ecc...) change based on that I haven't found another way to avoid this
string sql="..";
dataAccess.GetByIdAsync(query,new{Id=id},"MyConnectionName");
}
}
//but in future would be other repository implementations for entity of first table of first database but with oracle ecc..
public class SecondTableFirstDatabaseRepositorySQL:ISecondTableFirstDatabaseRepository<SecondTableEntity>
{
DbDataAccess dataAccess;
public SecondTableFirstDatabaseRepositorySQL()
{
dataAccess = new DbDataAccess(new SqlConnection());
}
public async Task GetByIdAsync(int id){
// I injected the IDbConnection because query parameters syntax(for example in ms access is ?, in sql is @ ecc...) change based on that I haven't found another way to avoid this
string sql="..";
dataAccess.GetByIdAsync(query,new{Id=id},"MyConnectionName");
}
}
//but in future would be other repository implementations for entity of second table of first database but with oracle ecc..
//class that use dapper to interact directly with database
public class DbDataAccess
{
private IDbConnection _connection;
private IDbTransaction _transaction;
private bool IsClosed=false;
public DbDataAccess(IDbConnection connection)
{
_connection = connection;
}
public string GetConnectionString(string name)
{
var path = ConfigurationManager.ConnectionStrings[name].ConnectionString;
return path;
}
public async Task<List<T>> LoadDataAsync<T, U>(string query, U parameters, string connectionStringName)
{
using (_connection)
{
_connection.ConnectionString = GetConnectionString(connectionStringName);
List<T> rows = (await _connection.QueryAsync<T>(query, parameters)).ToList();
return rows;
}
}
public async Task SaveDataAsync<T>(string query, T parameters, string connectionStringName)
{
using (_connection)
{
_connection.ConnectionString = GetConnectionString(connectionStringName);
await _connection.ExecuteAsync(query, parameters);
}
}
public void StartTransaction(string connectionStringName)
{
_connection.ConnectionString = GetConnectionString(connectionStringName);
_connection.Open();
_transaction = _connection.BeginTransaction();
IsClosed = false;
}
public async Task<List<T>> LoadDataInTransactionAsync<T, U>(string query, U parameters)
{
List<T> rows = (await _connection.QueryAsync<T>(query, parameters, transaction: _transaction)).ToList();
return rows;
}
public async Task SaveDataInTransactionAsync<T>(string query, T parameters)
{
await _connection.ExecuteAsync(query, parameters, transaction: _transaction);
}
public void CommitTransaction()
{
_transaction?.Commit();
_connection?.Close();
IsClosed = true;
}
public void RollBackTransaction()
{
_transaction?.Rollback();
_connection?.Close();
IsClosed = true;
}
public void Dispose()
{
if (!IsClosed)
{
try
{
CommitTransaction();
}
catch
{
throw;
}
}
_transaction = null;
_connection = null;
}
Now if i want to do one service for first database that save at the same time in all two tables of first database i would inject IFirstTableFirstDatabaseRepository and ISecondTableFirstDatabaseRepository but they would be generics and dependency injection with generics doesn't seems to me good, maybe I am wrong.
So my abstractions would be different IDbConnections for same entity and first table first repository entity has always same base properties but additional properties that depends on second database that can be different.
I'm not sure if this implementation is good for repositories, should I use dependency injection or factories?
Should I use DTOs from service to repository and viceversa?
Any idea how to improve?
I have to use Dapper.
Sorry for the long text, but I want to try to explain my case as best as possible. I hope I was clear.
Thanks in advance.