Personalizzato C # Dapper wrapper ORM

c# dapper design-patterns

Domanda

Sto sviluppando i servizi REST API asp.net web. I miei dati sono memorizzati nel database relazionale MySQL. Nel livello di accesso ai dati mi piacerebbe usare Dapper micro ORM, quindi mi piacerebbe creare una sorta di miei metodi di wrapper ORM. Se deciderò in futuro di passare ad un altro ORM, non avrò bisogno di riscrivere il mio intero codice del livello DAL.

Cosa ne pensi del mio approccio? Ecco il codice:

public abstract class BaseORMCommandSettings //SQL command base class
{
    public string CommandText { get; private set; }
    public object Parameters { get; private set; }
    public IDbTransaction Transaction { get; private set; }
    public int? CommandTimeout { get; private set; }
    public CommandType? CommandType { get; private set; }
    public CancellationToken CancellationToken { get; private set; }

    public BaseORMCommandSettings(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null,
                             CommandType? commandType = null, CancellationToken cancellationToken = default(CancellationToken))
    {
        this.CommandText = commandText;
        this.Parameters = parameters;
        this.Transaction = transaction;
        this.CommandTimeout = commandTimeout;
        this.CommandType = commandType;
        this.CancellationToken = cancellationToken;
    }
}

public class DapperCommandSettings : BaseORMCommandSettings//dapper cmd impl
{
    public CommandFlags Flags { get; private set; }

    public DapperCommandSettings(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null,
                             CommandType? commandType = null, CancellationToken cancellationToken = default(CancellationToken), CommandFlags flags = CommandFlags.Buffered)
         :base(commandText, parameters, transaction, commandTimeout, commandType, cancellationToken)
    {
        this.Flags = flags;
    }
}

public interface ICustomORM //base interface, for now have only generic Read 
                            list method
{
    IEnumerable<T> Read<T>(BaseORMCommandSettings cmd);
}

public class DapperORM : ICustomORM //my own dapper ORM wrapper implentation
{
    private readonly IDbConnection con;

    public DapperORM(IDbConnection con)
    {
        this.con = con;
    }

    public IEnumerable<T> Read<T>(BaseORMCommandSettings cmd)
    {
        var cmdDapper = cmd as DapperCommandSettings;
        var dapperCmd = new CommandDefinition(cmdDapper.CommandText, cmdDapper.Parameters, cmdDapper.Transaction,
                                              cmdDapper.CommandTimeout, cmdDapper.CommandType, cmdDapper.Flags, 
                                              cmdDapper.CancellationToken);

        return con.Query<T>(dapperCmd);
    }
}

Grazie in anticipo per qualsiasi tipo di aiuto.

Risposta accettata

Sì. Per favore, non farlo. Dapper esiste e gode del successo che ha, perché fornisce un modo terso ed espressivo di fare ADO. Non è un ORM. Se avvolgi il dapper, perdi l'interfaccia espressiva concisa e perdi il punto. Gli ORM (che non sono i migliori) esistono in parte per fornire la portabilità del DB. Cominciare a parlare della portabilità degli ORM farà sì che la gente sbattesse la testa contro un muro, nella disperazione! Basta usare Dapper e ammirarlo.


Risposta popolare

Ti suggerisco di andare per un desegno OO e creare un oggetto che ha solo un metodo che restituisce una sequenza di qualche tipo di oggetto. In questo modo è possibile creare un oggetto PAge e passare i parametri attraverso un costruttore in modo da poter creare diversi tipi di pagine: SqlPages, DapperPages, TestablePages e così via. In questo modo hai un modo flessibile di lavorare e puoi definire il tuo codice e lasciare i dettagli dell'infrastruttura / database implementati alla fine. Vorrei incapsulare i dettagli di Dtabase all'interno dell'oggetto, non lasciare che i dettagli si diffondano attraverso il tuo codice:

/// <summary>
/// DTO
/// </summary>
public class MyDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}

/// <summary>
/// Define a contract that get a sequence of something
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IFetch<T>
{
    IEnumerable<T> Fetch();
}

/// <summary>
/// Define a pageTemplate
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class PageTemplate<T> : IFetch<T>
{
    protected readonly int pageSize;
    protected readonly int page;

    public PageTemplate(int page, int pageSize)
    {
        this.page = page;
        this.pageSize = pageSize;
    }
    public abstract IEnumerable<T> Fetch();
}

/// <summary>
/// Design a MyDto Page object, Here you are using the Template method
/// </summary>
public abstract class MyDtoPageTemplate : PageTemplate<MyDto>
{
    public MyDtoPageTemplate(int page, int pageSize) : base(page, pageSize) { }
}

/// <summary>
/// You can use ado.net for full performance or create a derivated class of MyDtoPageTemplate to use Dapper
/// </summary>
public sealed class SqlPage : MyDtoPageTemplate
{
    private readonly string _connectionString;
    public SqlPage(int page, int pageSize, string connectionString) : base(page, pageSize)
    {
        _connectionString = connectionString;
    }

    public override IEnumerable<MyDto> Fetch()
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            //This can be injected from contructor or encapsulated here, use a Stored procedure, is fine
            string commandText = "Select Something";
            using (var command = new SqlCommand(commandText, connection))
            {
                connection.Open();
                using (var reader = command.ExecuteReader())
                {
                    if (reader.HasRows) yield break;
                    while (reader.Read())
                    {
                        yield return new MyDto()
                        {
                            Id = reader.GetInt32(0),
                            Name = reader.GetString(1),
                            Value = reader.GetString(2)
                        };
                    }
                }
            }
        }
    }
}

/// <summary>
/// You can test and mock the fetcher
/// </summary>
public sealed class TestPage : IFetch<MyDto>
{
    public IEnumerable<MyDto> Fetch()
    {
        yield return new MyDto() { Id = 0, Name = string.Empty, Value = string.Empty };
        yield return new MyDto() { Id = 1, Name = string.Empty, Value = string.Empty };
    }
}

public class AppCode
{
    private readonly IFetch<MyDto> fetcher;
    /// <summary>
    /// From IoC, inject a fetcher object
    /// </summary>
    /// <param name="fetcher"></param>
    public AppCode(IFetch<MyDto> fetcher)
    {
        this.fetcher = fetcher;
    }
    public IEnumerable<MyDto> FetchDtos()
    {
        return fetcher.Fetch();
    }
}

public class CustomController
{
    private readonly string connectionString;

    public void RunSql()
    {
        var fetcher = new SqlPage(1, 10, connectionString);
        var appCode = new AppCode(fetcher);
        var dtos = appCode.FetchDtos();
    }

    public void RunTest()
    {
        var fetcher = new TestPage();
        var appCode = new AppCode(fetcher);
        var dtos = appCode.FetchDtos();
    }
}


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché