2017-08-12 1 views
4

Ich habe die folgende Klasse, die ich zu hydratisieren versuchen:Dapper - Umgang mit speziellen Kennfeldern für ddd Einheit mit schreibgeschützten Feldern über Konstruktor

public class Product 
{ 
    public readonly Sku Sku; 
    public string Name { get; private set; } 
    public string Description { get; private set; } 
    public bool IsArchived { get; private set; } 

    public Product(Sku sku, string name, string description, bool isArchived) 
    { 
     Sku = sku; 
     Name = name; 
     Description = description; 
     IsArchived = isArchived; 
    } 
} 

, die die folgenden Klassen verwendet, die Konzepte von meinen DDD Unternehmen implementieren Domain-Modell (nicht-relevanter Code Code kurz zu halten entfernt, setzen Sie als Nur-Lese-unveränderlich zu machen einmal aufgebaut):

public class Sku 
{ 
    public readonly VendorId VendorId; 
    public readonly string SkuValue; 

    public Sku(VendorId vendorId, string skuValue) 
    { 
     VendorId = vendorId; 
     SkuValue = skuValue; 
    } 
} 

public class VendorId 
{ 
    public readonly string VendorShortname; 

    public VendorId(string vendorShortname) 
    { 
     VendorShortname = vendorShortname; 
    } 
} 

ich versuche, die parametrisierte Abfrage ausführen, die zu einem Produkt Objekt Hydrat in werden:

using (connection) 
{ 
    connection.Open(); 
    return connection.QueryFirst<Product>(ReadQuery, new { VendorId = sku.VendorId.VendorShortname, SkuValue = sku.SkuValue }); 
} 

Es wirft die folgende Ausnahme, da sie nicht weiß, wie man mit dem Sku Typ im Konstruktor beschäftigen:

System.InvalidOperationException: ‚Ein parameterlos Standardkonstruktors oder eine passende Signatur (System. String VendorId, System.String SkuValue, System.String Name System.String Beschreibung, System.UInt64 IsArchived) ist erforderlich für Domain.Model.Products.Product Materialisierung‘

Ich schaute in die Verwendung eines benutzerdefinierten SqlMapper.TypeHandler<Product>, aber die Parse(object value) übergibt immer nur in einem einzelnen geparsten Wert aus der Datenbankspalte VendorId (wenn es in einem Array von Werten hier übergeben würde, könnte ich die Zuordnung selbst tun).

Gibt es eine Möglichkeit, die Handhabung des Objekts zu gestalten, so dass ich alle Parameter in dem Konstruktor wie unten passieren kann:

using (connection) 
{ 
    var command = connection.CreateCommand(); 
    command.CommandText = "SELECT VendorShortname, SkuValue, Name, Description, IsArchived FROM Products WHERE [email protected] AND [email protected]"; 
    command.Parameters.AddWithValue("@VendorShortname", sku.VendorId.VendorShortname); 
    command.Parameters.AddWithValue("@SkuValue", sku.SkuValue); 
    connection.Open(); 

    var reader = command.ExecuteReader(); 
    if (reader.HasRows==false) 
     return null; 

    reader.Read(); 

    return new Product(
     new Sku(new VendorId(reader.GetString("VendorId")),reader.GetString("SkuValue")), 
     reader.GetString("Name"), 
     reader.GetString("Description"), 
     reader.GetBoolean("IsArchived")); 
} 

Ich glaube, ich mit Product(string VendorShortname, string SkuValue, string Name, string Description, UInt64 IsArchived) einen bestimmten Konstruktor erstellen könnte, aber ich würde diese Bedenken eher im Mapping-Code als in meinem Domain-Modell haben.

Über einen Pseudocode gehend, was ich tun könnte, ist mein eigenes ORM zu rollen, aber würde stattdessen über Dapper ähnlich machen wollen.

  1. alle Konstrukteure durch Reflexion für Objekt Get
  2. Wenn alle Parameter im Konstruktor ein Typ ist, für jeden Konstruktor
  3. seine Konstrukteure erhalten (einschließlich Parameter), den Konstruktor Namen der SQL-Leser Spalte zuordnen (und Typ)

Dies zu VendorShortname für VendorId(string vendorShortname) verwendet entsprechen würde, und Name, Description, isArchived für öffentliche Product(Sku sku, string name, string description, bool isArchived) verwendet ... etwas ist Simi sonders von MongoDB getan, wie pro meine Antwort auf den folgenden Link gepostet, wäre eine Dapper manuelle Zuordnung Äquivalent MongoDB Composite Key: InvalidOperationException: {document}.Identity is not supported

+0

'aber ich würde lieber (muss) diese Bedenken im Mapping-Code haben als in meinem Domain-Modell. Kannst du uns erklären, warum du ** musst ** hast du es so? – mjwills

+0

Ich habe keine Bedenken hinsichtlich der Persistenz in meinem Domänenmodell für andere Repositories (MongoDB). Wo möglich, möchte ich SQL/Dapper verwenden, da es die Dinge einfacher hält. Will meinen Beitrag aktualisieren, was getan werden müsste, könnte mein eigenes rollen aber ich möchte das Rad nicht neu erfinden noch könnte ich Code so gut schreiben wie Dappers – g18c

Antwort

3

Execute a query and map it to a list of dynamic objects

public static IEnumerable<dynamic> Query (
    this IDbConnection cnn, 
    string sql, 
    object param = null, 
    SqlTransaction transaction = null, 
    bool buffered = true 
) 

Sie würden dann konstruieren, um das gewünschte Modell unter Verwendung der Liste von awesome dynamische Objekte.

das Beispiel aus dem ursprünglichen Post So verwenden würde die parametrisierte Abfrage von ...

using (connection) 
{ 
    var command = connection.CreateCommand(); 
    command.CommandText = "SELECT VendorShortname, SkuValue, Name, Description, IsArchived FROM Products WHERE [email protected] AND [email protected]"; 
    command.Parameters.AddWithValue("@VendorShortname", sku.VendorId.VendorShortname); 
    command.Parameters.AddWithValue("@SkuValue", sku.SkuValue); 
    connection.Open(); 

    var reader = command.ExecuteReader(); 
    if (reader.HasRows==false) 
     return null; 

    reader.Read(); 

    return new Product(
     new Sku(new VendorId(reader.GetString("VendorId")),reader.GetString("SkuValue")), 
     reader.GetString("Name"), 
     reader.GetString("Description"), 
     reader.GetBoolean("IsArchived")); 
} 

To ...

var ReadQuery = "SELECT VendorShortname, SkuValue, Name, Description, IsArchived FROM Products WHERE [email protected] AND [email protected]"; 
using (connection) { 
    connection.Open(); 
    return connection.Query(ReadQuery, new { VendorShortname = sku.VendorId.VendorShortname, SkuValue = sku.SkuValue }) 
      .Select(row => new Product(
       new Sku(new VendorId(row.VendorShortname), row.SkuValue), 
       row.Name, 
       row.Description, 
       row.IsArchived) 
      ); 
} 

Welche der beabsichtigte Zweck ist der Rahmen verändert werden. Stellen Sie nur sicher, dass die verwendeten Eigenschaften den von der Abfrage zurückgegebenen Feldern direkt zugeordnet werden.

Dies mag intensiv erscheinen, aber dies ist eine praktikable Lösung angesichts der komplexen Natur des Konstruktors des Zielobjekts.

1

Sie können auch eine Option betrachten, Ihre "Domänen" -Modelle von der Persistenz zu entkoppeln und sie else-where zu konstruieren. Zum Beispiel:

  • Erstellen Sie eine Klasse pro Datenbank-Eintrag: ProductRecord
  • eine Fabrik erstellen: ProductFactory
  • die Daten holen: var productRecords = connection.Query<ProductRecord>("select * from products").AsList();
  • das Produkt Körperbau: factory.Build(productRecords)

Einige Profis: Trennung von Bedenken, Flexibilität, passt zu größeren Projekten

Einige con s: Mehr Code, ein Over-Kill für kleine Projekte

+0

Danke, ja, ich habe es mit einem POCO-Record und Autoadapter getan, aber kann mit Doppelklassen schmerzhaft werden. Sicherlich eine gültige Option, darauf hinzuweisen. – g18c

Verwandte Themen