2

我正在使用一种返回签名为IEnumerable<dynamic>. 在特定调用的运行时,它正在返回List<Dapper.SqlMapper.FastExpando>.

var x0 = repo.Find(proc, param); 
//x0 runtime type is {List<Dapper.SqlMapper.FastExpando>}

LINQPad.Extensions.Dump表示运行时类型x0is:List<IDictionary<String, Object>>但我似乎无法转换/转换为List<IDictionary<String, Object>>. 这是 Linqpad 转储的屏幕截图: 在此处输入图像描述

最终,我需要将每个 Dictionary 中的所有 Values 加入到单个IEnumerable<DateTime>.

IEnumerable<DateTime> GetDates(int productId) 
{
    const string proc = "[dbo].[myproc]";
    dynamic param = new { Id = "asdf" };
    var x0 = repo.Find(proc, param); 
    //...
    //linq conversion from x0 to IEnumerable<DateTime> here.
}

我得到的错误

List<IDictionary<String, Object>> x5 = repo.Find(proc, param);

结果是:

RuntimeBinderException: Cannot implicitly convert type 'object' to
 'System.Collections.Generic
     .List<System.Collections.Generic.IDictionary<string,object>>'.
An explicit conversion exists (are you missing a cast?)

背景: 我正在使用Dapper包装器,无法更改返回非规范化结果的数据库表/存储过程。存储过程返回 1 行 100 列,而不是 100 行 1 个数据元素。我想避免创建一个类来表示 100 列,并希望利用Dapper的自动能力通过 columnName、columnValue 的 IDictionary 将列数据转换为行。

更新 这似乎是动态参数的问题。当内联指定时,它可以工作。如果在本地指定然后作为参数传递,则失败。

IEnumerable<DateTime> GetDates(int productId) 
{
    const string proc = "[dbo].[myproc]";
    dynamic param = new { Id = "asdf" };

    //next line throws RuntimeBinderException: 'object' does not 
    //contain a definition for 'First'.
    //IDictionary<String, object> x0 = repo.Find(proc, param).First();

    //this succeeds:
    IDictionary<String, object> x0 = repo.Find(proc, new { Id = "asdf" }).First();

    IEnumerable<DateTime> qry2
       = x0.Values.AsQueryable()
           .Where(x => x != null)
           .Select(x => (DateTime) x);
    return qry2;
}

以下是 和 的Find签名Query

//Repository::Find
public IEnumerable<dynamic> Find(string procName, object param = null)

//Dapper SqlMapper::Query
public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
4

4 回答 4

2

您可以使用 LINQCast方法强制转换序列中的每个项目。

List<IDictionary<String, Object>> data = repo.Find(proc, param)
    .AsEnumerable()
    .Cast<IDictionary<String, Object>>()
    .ToList();
于 2013-04-17T17:52:39.540 回答
1

Repo.Find不返回IEnumerable<dynamic>. 它返回object。如果您确定它总是会返回 a List<IDictionary<String, Object>>,请执行以下操作:

List<IDictionary<String, Object>> x5 = (List<IDictionary<String, Object>>)repo.Find(proc, param);
于 2013-04-17T18:02:30.890 回答
1

所以问题是,通过指定 param asdynamic导致方法被动态调用并且扩展方法不能再被链接。

所以你可以只使用var而不是dynamic

 var param = new { Id = "asdf" };
 var data = repo.Find(proc, param)
.Cast<IDictionary<String, Object>>()
.ToList();

或者只是在调用之前将dynamic参数转换为:object

 var data = repo.Find(proc, (object)param)
.Cast<IDictionary<String, Object>>()
.ToList();

或者,如果Find真的返回一个运行时类型,System.Collections.Generic.List<Dapper.SqlMapper.FastExpando>只需转换结果以dynamic确保它在分配它之前使用它的运行时类型,并且特别重要的是分配给它,IEnumerable<>因为它是一个协变类型(IList<>List<>将失败)。

IEnumerable<IDictionary<String, Object>> data = (dynamic) repo.Find(proc, param);
于 2013-05-01T14:18:06.383 回答
0

解决方案

不要从您的方法中传回动态。Dapper 有一个 ToList< T > 扩展方法,所以你可以转换为任何支持的类型。在这种情况下,如果您确实需要低级关联数组,请传回 List< Dictionary < string, object >>。

还值得注意的是,C# 动态不会跨程序集边界编组,因此这可能是您遇到的潜在问题。如果要跨程序集进行封送处理,则需要使用 Expando、List< Dictionary < string、object >> 或两个程序集都引用的其他类型。

解释

这是包含 FastExpando(第 941 行)的Dapper SQLMapper 源的旧版本。请注意 FastExpando 的类定义。

私有类 FastExpando : System.Dynamic.DynamicObject, IDictionary< string, object >

于 2014-09-10T03:08:06.860 回答