Unidad de trabajo: Dapper and Repository Pattern - C # .Net

.net c# dapper unit-of-work

Pregunta

Proponer una buena solución con Dapper, Repositories, and Unit of Work ha sido un desafío para mí. He investigado mucho y he visto implementaciones en las que la clase Unit of Work tiene un dictionary of repositories . Esa no parece ser la forma correcta de hacerlo. Esto es lo que estoy tratando de hacer.

UnitOfWork.cs

public interface IUnitOfWork
{
    SqlConnection GetConnection();

    SqlTransaction GetTransaction();

    void CommitChanges();
}

public class UnitOfWork : IUnitOfWork
{
    private SqlConnection connection;
    private SqlTransaction transaction;

    public SqlConnection GetConnection()
    {
        if (connection != null)
        {
            return connection;
        }

        connection = new SqlConnection(@"Data Source=");
        connection.Open();
        transaction = connection.BeginTransaction();
        return connection;
    }

    public SqlTransaction GetTransaction()
    {
        return this.transaction;
    }

    public void CommitChanges()
    {
        try
        {
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
        }
        finally
        {
            transaction.Dispose();
            connection.Close();
        }
    }
}

Sé que esta es probablemente una implementación terrible, pero solo estoy tratando de comenzar una fundación.

Aquí hay una implementación del servicio . La instancia de UnitOfWork se ha inyectado en el servicio.

public class VeterinarianService : IVeterinarianService
{
    private readonly IClock clock;
    private readonly IUnitOfWork work;
    private readonly IVeterinarianRepository vetRepository;
    private readonly ITestingRepository testingRepository;


    public VeterinarianService(IClock clock, IUnitOfWork work, IVeterinarianRepository vetRepository, ITestingRepository testingRepository)
    {
        this.clock = clock;
        this.work = work;
        this.vetRepository = vetRepository;
        this.testingRepository = testingRepository;
    }

    /// <summary>
    /// Creates a veterinarian.
    /// </summary>
    /// <param name="newVetDTO">The newVetDTO containing all required parameters.</param>
    /// <returns>The newly created veterinarian.</returns>
    public async Task<VeterinarianDTO> CreateVeterinarian(NewVeterinarianDTO newVetDTO)
    {
        var now = clock.Now.ToDateTimeUtc();

        var vet = Mapper.Map<Veterinarian>(newVetDTO);
        var veterinarian = await vetRepository.Create(vet, now);
        // Call the second repository method here.

        // Commit the database changes from both repositories.
        this.work.CommitChanges();
        return Mapper.Map<VeterinarianDTO>(veterinarian);
    }
}

Aquí está la implementación del repositorio . Tenga en cuenta que el mismo objeto UnitOfWork se inyecta en este repositorio. Es el mismo UnitOfWork que se inyectó en la clase de servicio.

public class VeterinarianRepository : IVeterinarianRepository
{
    private readonly ICache cache;
    private readonly IUnitOfWork work;

    private readonly TimeSpan cacheTimeSpan = new TimeSpan(5, 0, 0);
    private const CommandType Type = CommandType.StoredProcedure;

    public VeterinarianRepository(ICache cache, IUnitOfWork work)
    {
        this.cache = cache;
        this.work = work;
    }

    public async Task<Veterinarian> Create(Veterinarian vet, DateTime date)
    {
            var connection = work.GetConnection();
            var parameters = new DynamicParameters();
            parameters.Add("@FirstName", vet.FirstName);
            parameters.Add("@LastName", vet.LastName);
            parameters.Add("@Date", date);
            parameters.Add("@User", 1);


            var identity = await connection.ExecuteScalarAsync<int?>("dbo.CreateVeterinarian", parameters, this.work.GetTransaction(), commandType: Type);

            if (identity.HasValue)
                vet.VeterinarianIdentity = identity.Value;
            else throw new Exception();
    }
}

Mi proceso de pensamiento aquí es que la misma instancia de la clase UnitOfWork se injected en el servicio y los repositorios subyacentes. Un servicio puede llamar a varios repositories utilizando la misma connection / transaction y, si todo fue exitoso, puede commit toda la transaction o devolverla por completo. Esto revertiría cualquier cambio realizado por cualquiera de los repositorios.

Dos preguntas:

  1. ¿Es esto una manera ridícula de implementar tal cosa?

  2. Mi VeterinarianRepository aplicación, en el método Create, que pasa la Transaction creada por el UnitOfWork instancia al ExecuteScalarAsync método. ExecuteScalarAsync embargo, ExecuteScalarAsync ejecutar ExecuteScalarAsync , la transacción parece estar completa. Obtengo un "This SqlTransaction has completed; it is no longer usable." excepción cuando se intenta utilizar el UnitOfWork CommitChanges() . Esperaba que la transacción permaneciera abierta para que otros repositorios pudieran usarla, si el servicio así lo requería.

Respuesta popular

Su servicio debería facilitar un repositorio que contiene un constructor que obtiene un servicio de configuración o conexión. La implementación de ese repositorio debe, en mi opinión, construirse de manera que solo se necesite el punto final (o conexión) de almacenamiento persistente para realizar llamadas.

Su VeterinarianRepository debe crearse con una configuración que defina una ruta a la persistencia y la implementación para llevar a cabo el comando utilizando una implementación específica. No hay orquestación gestionada o conocida por su VeterinarianRepository , fuera de su ámbito de trabajo. Además, de nuevo mi opinión, su Servicio de VeterinarianService debería poder funcionar independientemente de cómo el VeterinarianRepository invocado hizo o no hizo su trabajo. No sé si esto ayuda en relación con Dapper, sin embargo, los ORM que llevan sus propias implementaciones reforzadas no son muy amigables con los repositorios de los servicios genéricos.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué