如何创建一个动态确定的属性名称的匿名对象?

给定一个值数组,我想创建一个具有基于这些值的属性的匿名对象。 属性名称只是"pN" ,其中N是数组中值的索引。

例如,给定

object[] values = { 123, "foo" };

我想创建一个匿名对象

new { p0 = 123, p1 = "foo" };

我能想到的唯一方法就是使用一个switch或链接到合理数量的参数来支持,但我想知道是否有更优雅的方法来做到这一点:

 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语句的stringparams 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方法,并以与现有方法一致的方式进行,例如:

 public IEnumerable ExecuteQuery(string commandText, CommandType commandType, params object[] parameterValues) { .... } 

但Dapper的Query方法获取匿名对象中的参数值:

 var dog = connection.Query("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(connection, sql, values); 

在Dapper的SqlMapper.cs文件的第581行引发exception:

 using (var reader = cmd.ExecuteReader()) 

并且exception是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(connection, sql, values); 

DynamicParameters可以在构造函数中接受匿名类。 您可以使用AddDynamicParams方法连接AddDynamicParams

此外,对匿名类型没有严格的依赖性。 Dapper将允许混凝土类型作为参数,例如:

 class Stuff { public int Thing { get; set; } } ... cnn.Execute("select @Thing", new Stuff{Thing = 1}); 

凯文有一个类似的问题: 寻找一种快速简便的方法来合并POCO上的所有属性 – DynamicParameters在这里完美运行,而不需要魔术箍跳跃。

不完全是一个匿名对象,但是如何实现一个DynamicObject ,它根据数组中的值返回p1 … pn的值? 这会与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"