値の配列が与えられたので、これらの値に基づくプロパティを持つ匿名オブジェクトを作成したいと思います。プロパティ名は単純に"pN"
になります。ここで、N
は配列内の値のインデックスです。
たとえば、与えられた
object[] values = { 123, "foo" };
匿名オブジェクトを作成したい
new { p0 = 123, p1 = "foo" };
これを行うために私が考えることができる唯一の方法は、サポートするパラメータの妥当な数までswitch
またはif
チェーンを使用することですが、もっとエレガントなものがあるかどうか疑問に思いましたこれを行う方法:
object[] parameterValues = new object[] { 123, "foo" };
dynamic values = null;
switch (parameterValues.Length)
{
case 1:
values = new { p0 = parameterValues[0] };
break;
case 2:
values = new { p0 = parameterValues[0], p1 = parameterValues[1] };
break;
// etc. up to a reasonable # of parameters
}
背景
データベースに対してSQLステートメントを実行する既存のメソッドセットがあります。メソッドは通常、SQLステートメントにstring
を取り、パラメーターがある場合はparams object[]
を取ります。クエリでパラメータを使用する場合、それらの名前は@p0, @p1, @p2, etc.
になると理解されています。
例:
public int ExecuteNonQuery(string commandText, CommandType commandType, params object[] parameterValues) { .... }
これは次のように呼ばれます:
db.ExecuteNonQuery("insert into MyTable(Col1, Col2) values (@p0, @p1)", CommandType.Text, 123, "foo");
ここで、このクラス内で Dapper を使用して、DapperのQuery<T>
メソッドをラップして公開し、既存のメソッドと一貫性のある方法で実行したいと思います。何かのようなもの:
public IEnumerable<T> ExecuteQuery<T>(string commandText, CommandType commandType, params object[] parameterValues) { .... }
ただし、DapperのQuery<T>
メソッドは、匿名オブジェクトのパラメーター値を取ります。
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });
dapperにパラメータを渡すための匿名オブジェクトの作成についての私の質問につながります。
@Paolo Tedescoの要求に応じて、DynamicParameter
クラスを使用してコードを追加します。
string sql = "select * from Account where Id = @p0 and username = @p1";
dynamic values = new DynamicParameter(123, "test");
var accounts = SqlMapper.Query<Account>(connection, sql, values);
dapperのSqlMapper.csファイルの581行目に例外をスローします。
using (var reader = cmd.ExecuteReader())
例外はSqlException
です:
スカラー変数「@ p0」を宣言する必要があります。
cmd.Parameters
プロパティを確認すると、コマンドに構成されたパラメーターが表示されません。
Dapperを誤用しているので、これを行う必要はありません。代わりに、IDynamicParameters
を実装するか、特定の非常に柔軟なDynamicParameters
クラスを使用してください。
特に:
string sql = "select * from Account where Id = @id and username = @name";
var values = new DynamicParameters();
values.Add("id", 1);
values.Add("name", "bob");
var accounts = SqlMapper.Query<Account>(connection, sql, values);
DynamicParameters
は、コンストラクターで匿名クラスを取り込むことができます。 DynamicParameters
メソッドを使用してAddDynamicParams
を連結できます。
さらに、anon-typesへの厳密な依存関係はありません。 Dapperは、パラメータとして具象型を許可します。例:
class Stuff
{
public int Thing { get; set; }
}
...
cnn.Execute("select @Thing", new Stuff{Thing = 1});
ケビンも同様の質問をしました: POCOのすべてのプロパティを合体させるための高速で簡単な方法を探しています --DynamicParameters
は、魔法のフープジャンプを必要とせずにここでも完全に機能します。
正確には匿名オブジェクトではありませんが、配列内の値に基づいてp1 ... pnの値を返す DynamicObject を実装するのはどうですか?それはDapperで機能しますか?
例:
using System;
using System.Dynamic;
using System.Text.RegularExpressions;
class DynamicParameter : DynamicObject {
object[] _p;
public DynamicParameter(params object[] p) {
_p = p;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
Match m = Regex.Match(binder.Name, @"^p(\d+)$");
if (m.Success) {
int index = int.Parse(m.Groups[1].Value);
if (index < _p.Length) {
result = _p[index];
return true;
}
}
return base.TryGetMember(binder, out result);
}
}
class Program {
static void Main(string[] args) {
dynamic d1 = new DynamicParameter(123, "test");
Console.WriteLine(d1.p0);
Console.WriteLine(d1.p1);
}
}
匿名オブジェクトを動的に作成することはできません。ただし、Dapperは動的オブジェクトで動作するはずです。動的オブジェクトを適切に作成するには、 Clay を使用できます。それはあなたが次のようなコードを書くことを可能にします
var person = New.Person();
person["FirstName"] = "Louis";
// person.FirstName now returns "Louis"