2017-08-10 2 views
-1

Ich entwickle ASP.net-Web-API-REST-Dienste. Meine Daten werden in einer relationalen MySQL-Datenbank gespeichert. In der Datenzugriffsebene möchte ich Dapper micro ORM verwenden, also möchte ich eine Art von eigenen ORM-Wrapper-Methoden erstellen. Wenn ich mich in Zukunft dazu entscheide, zu einem anderen ORM zu wechseln, muss ich nicht meinen gesamten DAL-Layer-Code neu schreiben.Benutzerdefinierter C# Dapper ORM-Wrapper

Was denken Sie über meinen Ansatz? Hier ist der Code:

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); 
    } 
} 

Vielen Dank im Voraus für jede Art von Hilfe.

+0

[SO] nicht über Code-Review, sollten Sie vielleicht fragen, auf https://codereview.stackexchange.com/ – Richard

+0

Danken ... für deine Antwort. Eigentlich möchte ich die Meinung von jemandem über meine Code-Implementierung hören, um Code zu schreiben, wo ich ziemlich einfach und schnell die Technologie ändern kann, die ich benutze ... Das ist also mehr über Design, nicht über Code-Review. – user2214626

Antwort

2

Yup. Bitte tu das nicht. Dapper existiert und genießt den Erfolg, weil es eine knappe, ausdrucksstarke Art und Weise bietet, ADO zu machen. Es ist kein ORM. Wenn Sie dapper umhüllen, verlieren Sie die knappe ausdrucksvolle Oberfläche, und Sie verlieren den Punkt. ORM's (was dapper nicht ist) existieren teilweise, um DB-Portabilität bereitzustellen. Wenn man anfängt, über die Portabilität von ORM zu sprechen, werden die Leute verzweifelt gegen die Wand schlagen. Benutze einfach Dapper und bewundere es.

+0

vielleicht, wenn das Dapper-Team vollständige Beispielimplementierungen mit Schritt-für-Schritt- oder sogar Best-Practices bereitstellen könnte, würde es eine Menge der Ratespiele für neue Benutzer nehmen, die nach Dapper kommen möchten. Die readme.md ist bei weitem nicht ausreichend. –

0

Ich schlage vor, dass Sie für ein OO desing gehen und ein Objekt erstellen, das nur eine Methode hat, die eine Sequenz eines Objekttyps zurückgibt. Auf diese Weise können Sie ein PAge-Objekt erstellen und die Parameter über einen Konstruktor übergeben, sodass Sie verschiedene Arten von Seiten erstellen können: SqlPages, DapperPages, TestablePages und so weiter. Auf diese Weise haben Sie eine flexible Arbeitsweise und können Ihren Code entwerfen und die Infraestruktur/Datenbank-Details am Ende implementieren. Ich würde Details von dtabases innerhalb Objekts kapseln, lasse nicht die Details über Ihren Code gespreizt:

/// <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(); 
    } 
}