DapperのC#で動的型を作成する

c# dapper

質問

未知の構造化データを保存しているアプリケーションがあります。ですから、データベースにFooという名前のテーブルがあり、次のようになっているとしましょう:

CREATE TABLE [dbo].[Foo]
(
    [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
    [DataId] INT NOT NULL, 
    [Bar] VARCHAR(50) NOT NULL, 
    CONSTRAINT [FK_Foo_Data] FOREIGN KEY ([DataId]) REFERENCES [Data]([Id])
)

そして、 Fooは、 Dataという名前のテーブルに関連しています。このテーブルは、データを記録したプロバイダと、ログに記録された日時を格納します。テーブルは次のようになります。

CREATE TABLE [dbo].[Data]
(
    [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
    [ProviderId] INT NOT NULL, 
    [RecordDateTime] DATETIME NOT NULL, 
    CONSTRAINT [FK_Data_Provider] FOREIGN KEY ([ProviderId]) REFERENCES [Provider]([Id])
)

さて、プロバイダ(私が提供しているデータを知らない)がFooという名前のクラスを持っているとしましょう。

[Serializable]
public class Foo : ISerializable
{
    private string bar;

    public string Bar
    {
        get { return bar; }
        set { bar = value; }
    }

    public Foo()
    {
        Bar = "Hello World!";
    }

    public Foo(SerializationInfo info, StreamingContext context)
    {
        this.bar = info.GetString("Bar");
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bar", bar);
    }
}

だから、プロバイダが私にFooインスタンスを送ったら、それを調べてデータベースのどのテーブルを挿入すべきかを決め、そのコードブロックが次のようになっていると仮定しよう:

this.connection.Open();

try
{
    var parameters = new
        {
            ProviderId = registeredProvidersIdBySession[providerKey.ToString()],
            RecordDateTime = DateTime.Now,
        };

    var id = connection.Query<int>("INSERT INTO Data (ProviderId, RecordDateTime) VALUES (@ProviderId, @RecordDateTime); SELECT CAST(SCOPE_IDENTITY() as INT)", parameters).Single();

    var t = data.GetType();
    var fields = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);

    var tableName = string.Format("[{0}]", t.Name);
    var fieldList = string.Join(", ", fields.Select(p => string.Format("[{0}]", p.Name)).ToArray());

    var valueList = fields.Select(p => string.Format("@{0}", p.Name)).ToList();
    valueList.Insert(0, "@DataId");

    var values = new Dictionary<string, object>();
    values.Add("@DataId", id);
    foreach (var propertyInfo in fields)
    {
        values.Add(string.Format("@{0}", propertyInfo.Name), propertyInfo.GetValue(data, null));
    }

    return connection.Execute(string.Format(
        "INSERT INTO {0} ({1}) VALUES ({2})",
            tableName,
            fieldList,
            string.Join(", ", valueList.ToArray())), values);
}
finally
{
    this.connection.Close();
}

今見たように、私は渡されたオブジェクトのTypeからtableNameを取得しています。この例ではFooです。さらに、私はリフレクションで挿入するフィールドのリストを集めています。しかし、そこにあるヒッチは、 Dapperがパラメータ値のためにオブジェクトを送る必要があるということです。さて、もし私が私に与えられたオブジェクトに渡すことができるタイプのDataIdプロパティを提供する必要はなかったが、私は正しくログを関連付けることができるようにDataIdプロパティが必要です。

私は何を求めているのですか?

  1. 私は動的なタイプを作成しなければならないのですか、あるいはDapperが他の何かを助けることができますか?
  2. ダイナミック型を作成するTypeBuilderがある場合は、 TypeBuilderを使用するよりももっと簡単な方法がTypeBuilderますか?私はそれを前にやったことがありますが、もう一度やり直すことができますが、人は痛みです。
  3. TypeBuilderを使用するTypeBuilderがある場合は、私よりも効率的な方法を知っている可能性があるので、私はあなたの例に興味があります。したがって、いくつかの考えがある場合は、例を含めてください。

免責条項

上のコード例では、 Dictionary<string, object>を渡してみましたが、 Dapperはそれを受け入れません。私はすでにソースコードを見ていたのでそれがないと思っていましたが、私は何かを逃したと思っていました。

皆さんありがとう!

受け入れられた回答

DapperにはBuiltIn型があり、そのプロパティは実行時にのみ知られているパラメータを渡します。クラスDynamicParameters確認します。それは次のように使用されます:

var p = new DynamicParameters();
p.Add("@a", 11);
p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output);
p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

パラメータオブジェクトとしてExecuteメソッドに渡されます。通常はSPと一緒に使用されるため(出力値の読み込みにも使用できます)、通常のSELECT / INSERT / UPDATEクエリでは使用できない理由はありません。


人気のある回答

私はあなたが大胆であることを尋ねたが、...

ソースDBは実際にこのタイプのシナリオをかなりうまく処理します。あなたがそれを行うにはいくつかの方法があります。以下にその例を示すコード例を示します。注:テーブル名の検索方法を上書きし、必要に応じて解決済みのテーブル名を取得できるメソッドがあります。

私はSauceDBを書いた

public class Foo
{
    public int ID { get; set; }
    publi string Name { get; set; }
}

public class Bar
{
    public int ID { get; set; }
    publi string Name { get; set; }
}

public class  MainClass
{
    IDataStore _dstore;
    public void Main()
    {
        _dstore = new SqlServerDataStore("my_connection_string")//create data store

        InsertObject(new Foo());
        InsertObject(new Bar());
    }

    public void InsertObject(object o)
    {
        //this will assume the table it belongs to is the type name + an s
            //you can override the table name behavior as well
        _dstore.InsertObject(o);
    }
}

http://sauce.codeplex.com/



ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow