16

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

例如,给定

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

我想创建匿名对象

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

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

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 语句。这些方法通常采用stringsql 语句和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 的问题。


DynamicParameter按照@Paolo Tedesco 的要求使用该类添加代码。

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

4

3 回答 3

15

您正在滥用 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方法连接。

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

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

...

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

Kevin 有一个类似的问题:寻找一种快速简单的方法来合并 POCO 上的所有属性——DynamicParameters在这里也可以完美地工作,不需要任何魔术跳圈。

于 2011-12-18T06:53:05.657 回答
5

不完全是一个匿名对象,但是如何实现一个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);
    }
}
于 2011-12-13T15:41:20.670 回答
1

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

var person = New.Person();
person["FirstName"] = "Louis";
// person.FirstName now returns "Louis"
于 2011-12-13T16:00:50.667 回答