2017-08-12 11 views
4

を経由して、読み取り専用フィールドでDDDエンティティのためのカスタム・マッピングを扱い、私は私が水和しようとしている以下のクラスがあります。Dapperの - コンストラクタ

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

私のDDDエンティティからの概念を実装する以下のクラスを使用していますドメインモデル(非関連するコードは、短いコードを維持するために除去するために読み取り専用として設定され不変一度構築):

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

は、私が試してみて、Productオブジェクトに水和されますパラメータ化クエリを実行します。

のSystem.InvalidOperationException:「パラメータなしのデフォルトコンストラクタ または1つのマッチングの署名(システム、それはコンストラクタでSkuタイプに対処する方法を知らないよう

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

これは、次の例外がスローされます。文字列のベンダーID、可能System.String はSkuValue、可能System.String名前、可能System.String説明、System.UInt64 IsArchived)は

Domain.Model.Products.Product マテリア」のために必要されます

私はカスタムSqlMapper.TypeHandler<Product>を調べましたが、Parse(object value)は、VendorIdデータベース列(ここで値の配列を渡した場合は、自分でマッピングすることができます)から単一の解析された値しか渡されません。

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

私はProduct(string VendorShortname, string SkuValue, string Name, string Description, UInt64 IsArchived)が、私に特定のコンストラクタを作成することができると思います:

私は以下のようなコンストラクタにしてすべてのパラメータを渡すことができるようにオブジェクトの処理をカスタマイズする方法はあります私のドメインモデルではなく、マッピングコードでこの懸念を抱くことになります。

いくつかの擬似コードを調べると、自分のORMをロールすることができますが、代わりにDapperを使用して似たようにしたいと考えています。

  1. は(SQLリーダ列にコンストラクタ名をマップする、コンストラクタ内の任意のパラメータが型の場合、(パラメータを含む)各コンストラクタのためにそのコンストラクタ
  2. を取得反射
  3. によってオブジェクトのすべてのコンストラクタを取得し、タイプ)

これは公共Product(Sku sku, string name, string description, bool isArchived)に使用VendorId(string vendorShortname)に使用VendorShortname、およびNameDescriptionisArchivedに等しくなります...何かがシミです私の答えごとに次のリンク、Dapperの手動マッピング同等に掲示ようlarlyあなたは、その後のリストを使用して、所望のモデルを構築するだろうMongoDB Composite Key: InvalidOperationException: {document}.Identity is not supported

+0

'私のドメインモデルではなく、マッピングコードにこの懸念があると思います。このようにしていますか? – mjwills

+0

他のリポジトリ(MongoDB)のドメインモデルに永続性の懸念がありません。SQL/Dapperを使用したいと思っています。私のポストを更新する必要がありますが、自分自身をロールバックすることができますが、ホイールを再発明したくないし、Dapperさんのようにコードを書くこともできません – g18c

答えて

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 
) 

素晴らしいだろうMongoDBので行います動的オブジェクト。だから、オリジナルのポストからの例を使用して

、パラメータ化クエリは、フレームワークの目的である

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

へ...

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

...から変更されます。使用されるプロパティがクエリによって返されたフィールドに直接マップされていることを確認してください。

これは集中的に思えるかもしれませんが、これはターゲットオブジェクトのコンストラクタの複雑な性質を考慮すると実行可能なソリューションです。

1

「ドメイン」モデルと永続性を分離し、else-elseを作成するオプションも検討できます。たとえば:

  • は、データベースレコードごとにクラスを作成します。ProductRecordを
  • は、ファクトリを作成します:ProductFactory
  • は、データを取得します:factory.Build(productRecords)

いくつかの長所:var productRecords = connection.Query<ProductRecord>("select * from products").AsList();

  • は、製品をビルドします。懸念の分離、柔軟性、大きなプロジェクトに合う

    いくつかの詐欺小規模プロジェクトのための過剰な殺害

  • +0

    ありがとうございます.POCOレコードと似ていますし、ダブルクラスでは痛いことがあります。確かに1つの有効なオプションを指摘してください。 – g18c