Patrón de repositorio genérico para .net core con Dapper

Patrón de repositorio genérico para .net core con Dapper

Teníamos un proyecto en el que teníamos un repositorio genérico más elegante, pero después de que el proyecto evolucionó, lo hemos abandonado. el repositorio genérico para usar el poder real de dapper.

Recomendaría usar Dapper como directo sin operaciones CRUD genéricas.

Para demostrar lo que teníamos, proporcionaré un código de muestra que no está listo para la producción y le dará una idea para implementar su propio repositorio genérico.

public abstract class ConnectionBase : IDbConnection
{

    protected ConnectionBase(IDbConnection connection)
    {
        Connection = connection;
    }

    protected IDbConnection Connection { get; private set; }

    // Verbose but necessary implementation of IDbConnection:
    #region "IDbConnection implementation"

    public string ConnectionString
    {
        get
        {
            return Connection.ConnectionString;
        }

        set
        {
            Connection.ConnectionString = value;
        }
    }

    public int ConnectionTimeout
    {
        get
        {
            return Connection.ConnectionTimeout;
        }
    }

    public string Database
    {
        get
        {
            return Connection.Database;
        }
    }

    public ConnectionState State
    {
        get
        {
            return Connection.State;
        }
    }

    public IDbTransaction BeginTransaction()
    {
        return Connection.BeginTransaction();
    }



    public void Close()
    {
        Connection.Close();
    }

    public IDbCommand CreateCommand()
    {
        return Connection.CreateCommand();
    }

    public void Dispose()
    {
        Connection.Dispose();
    }

    public void Open()
    {
        Connection.Open();
    }

    #endregion
}

Repositorio Genérico

public abstract class GenericRepository<T> : IRepository<T> where T : class //EntityBase, IAggregateRoot
    {

        private readonly string _tableName;


        internal IDbConnection Connection
        {
            get
            {
                return new SqlConnection(ConfigurationManager.ConnectionStrings["SmsQuizConnection"].ConnectionString);
            }
        }

        public GenericRepository(string tableName)
        {
            _tableName = tableName;
        }

        internal virtual dynamic Mapping(T item)
        {
            return item;
        }

        public virtual void Add(T item)
        {
            using (IDbConnection cn = Connection)
            {
                var parameters = (object)Mapping(item);
                cn.Open();
                item.ID = cn.Insert<Guid>(_tableName, parameters);
            }
        }

        public virtual void Update(T item)
        {
            using (IDbConnection cn = Connection)
            {
                var parameters = (object)Mapping(item);
                cn.Open();
                cn.Update(_tableName, parameters);
            }
        }

        public virtual void Remove(T item)
        {
            using (IDbConnection cn = Connection)
            {
                cn.Open();
                cn.Execute("DELETE FROM " + _tableName + " WHERE [email protected]", new { ID = item.ID });
            }
        }

        public virtual T FindByID(Guid id)
        {
            T item = default(T);

            using (IDbConnection cn = Connection)
            {
                cn.Open();
                item = cn.Query<T>("SELECT * FROM " + _tableName + " WHERE [email protected]", new { ID = id }).SingleOrDefault();
            }

            return item;
        }



        public virtual IEnumerable<T> FindAll()
        {
            IEnumerable<T> items = null;

            using (IDbConnection cn = Connection)
            {
                cn.Open();
                items = cn.Query<T>("SELECT * FROM " + _tableName);
            }

            return items;
        }



    }

Los ejemplos de @PathumLakshan se solicitan de los comentarios. Los ejemplos proporcionados están escritos de manera asíncrona, pero la fuente puede implementarse de forma síncrona. De todos modos, es solo una ilustración de cómo puede administrar la infraestructura con Dapper. Clase Db proporciona algunos métodos genéricos para obtener datos y ejecutar consultas SQL. Por ejemplo, puede usar la sobrecarga Get<T>(string, object) para consultas básicas, o toma Get<T>(Func<SqlConnection, SqlTransaction, int, Task<T>> para usar digamos QueryMultiple . Clase Repository<Entity> muestra, cómo puede verse el repositorio básico para la entidad Entity .

BD clase:

public class Db : IDb
{
    private readonly Func<SqlConnection> _dbConnectionFactory;

    public Db(Func<SqlConnection> dbConnectionFactory)
    {
        _dbConnectionFactory = dbConnectionFactory ?? throw new ArgumentNullException(nameof(dbConnectionFactory));
    }

    public async Task<T> CommandAsync<T>(Func<SqlConnection, SqlTransaction, int, Task<T>> command)
    {
        using (var connection = _dbConnectionFactory.Invoke())
        {
            await connection.OpenAsync();

            using (var transaction = connection.BeginTransaction())
            {
                try
                {
                    var result = await command(connection, transaction, Constants.CommandTimeout);

                    transaction.Commit();

                    return result;
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    Logger.Instance.Error(ex);
                    throw;
                }
            }
        }
    }

    public async Task<T> GetAsync<T>(Func<SqlConnection, SqlTransaction, int, Task<T>> command)
    {
        return await CommandAsync(command);
    }

    public async Task<IList<T>> SelectAsync<T>(Func<SqlConnection, SqlTransaction, int, Task<IList<T>>> command)
    {
        return await CommandAsync(command);
    }

    public async Task ExecuteAsync(string sql, object parameters)
    {
        await CommandAsync(async (conn, trn, timeout) =>
        {
            await conn.ExecuteAsync(sql, parameters, trn, timeout);
                return 1;
        });

    public async Task<T> GetAsync<T>(string sql, object parameters)
    {
        return await CommandAsync(async (conn, trn, timeout) =>
        {
            T result = await conn.QuerySingleAsync<T>(sql, parameters, trn, timeout);
            return result;
        });
    }

    public async Task<IList<T>> SelectAsync<T>(string sql, object parameters)
    {
        return await CommandAsync<IList<T>>(async (conn, trn, timeout) =>
        {
            var result = (await conn.QueryAsync<T>(sql, parameters, trn, timeout)).ToList();
            return result;
        });
    }
}

Repositorio clase:

public class Repository<Entity> : IRepository<Entity>
{
    protected readonly IDb _db;

    public Repository(IDb db)
    {
        _db = db ?? throw new
            ArgumentException(nameof(db));
    }

    public async Task Add(Entity entity)
    {
        await _db.ExecuteAsync("INSERT INTO ... VALUES...", entity);
    }

    public async Task Update(Entity entity)
    {
        await _db.ExecuteAsync("UPDATE ... SET ...", entity);
    }

    public async Task Remove(Entity entity)
    {
        await _db.ExecuteAsync("DELETE FROM ... WHERE ...", entity);
    }

    public async Task<Entity> FindByID(int id)
    {
        return await _db.GetAsync<Entity>("SELECT ... FROM ... WHERE Id = @id", new { id });
    }

    public async Task<IEnumerable<Entity>> FindAll()
    {
        return await _db.SelectAsync<Entity>("SELECT ... FROM ... ", new { });
    }
}

Db se puede ampliar con otro método genérico, por ejemplo, ExecuteScalar , que necesitarías en tus repositorios. Espero que ayude.


No