【问题标题】:How to create an anonymous object with property names determined dynamically?如何创建具有动态确定属性名称的匿名对象?
【发布时间】:2011-12-13 15:35:41
【问题描述】:

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

例如,给定

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

我想创建匿名对象

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

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

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 语句。这些方法通常将string 用于sql 语句,将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&lt;T&gt; 方法采用匿名对象中的参数值:

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 属性显示没有为命令配置的参数。

【问题讨论】:

  • 是的,你在滥用 DynamicParameter,见第 243 行:code.google.com/p/dapper-dot-net/source/browse/Tests/Tests.cs
  • 另外,IDynamicParamters 为您提供绝对的灵活性。为了小巧玲珑的好处,你永远不需要烘焙匿名类型,作为一种你可以的学习体验,但这不值得付出努力。特别是,如果你真的想使用 emit,你可以从 ICollection 中烘焙一个强类型。

标签: c# anonymous-types dapper


【解决方案1】:

您在滥用 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 可以在构造函数中接收匿名类。您可以使用AddDynamicParams 方法连接DynamicParameters

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

class Stuff
{
   public int Thing { get; set; }
}

...

cnn.Execute("select @Thing", new Stuff{Thing = 1});

Kevin 也有类似的问题:Looking for a fast and easy way to coalesce all properties on a POCO - DynamicParameters 在这里也能完美运行,无需任何魔术跳圈。

【讨论】:

    【解决方案2】:

    不完全是一个匿名对象,但是如何实现一个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);
        }
    }
    

    【讨论】:

    • +1,谢谢,这很好,但不幸的是 Dapper 不喜欢它:(。它最终试图在没有任何参数的情况下执行 db 命令——不知道为什么,但我认为它正在尝试将 DynamicParameter 实例评估为参数值。
    • @adrift:你能发布一个小代码示例来说明你的问题吗?
    • 编辑了问题以包含显示尝试使用 DynamicParameter 时生成的异常的代码。
    【解决方案3】:

    您不能动态创建匿名对象。但是 Dapper 应该使用动态对象。要以一种很好的方式创建动态对象,您可以使用Clay。它使您能够编写类似

    的代码
    var person = New.Person();
    person["FirstName"] = "Louis";
    // person.FirstName now returns "Louis"
    

    【讨论】:

      猜你喜欢
      • 2017-04-06
      • 2013-07-23
      • 1970-01-01
      • 1970-01-01
      • 2010-12-20
      相关资源
      最近更新 更多