如何将List <dapper.sqlmapper.fastexpando>转换为运行时类型(List <idictionary <string,object>&gt;)</idictionary <string,object> </dapper.sqlmapper.fastexpando>

时间:2013-04-17 17:50:54

标签: c# linq casting dapper dynamic-language-runtime

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

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

LINQPad.Extensions.Dump表示x0的运行时类型为:List<IDictionary<String, Object>>,但我似乎无法转换/转换为List<IDictionary<String, Object>>。 以下是Linqpad转储的屏幕截图: enter image description here

最终,我需要将每个词典中的所有值加入到单个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包装器,无法更改返回非规范化结果的数据库表/存储过程。 sproc返回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;
}

以下是FindQuery的签名:

//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 个答案:

答案 0 :(得分:2)

您可以使用LINQ Cast方法转换序列中的每个项目。

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

因此,您可以使用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);

答案 3 :(得分:0)

解决方案

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

值得注意的是,C#dynamic不会跨越程序集边界进行编组,因此这可能是您遇到的问题。如果您要对程序集进行编组,则需要使用Expando,List&lt;字典&lt; string,object&gt; &gt;,或两个程序集引用的其他类型。

说明

这是Dapper SQLMapper source的旧版本,其中包含FastExpando(第941行)。 注意FastExpando的类定义。

私有类FastExpando:System.Dynamic.DynamicObject,IDictionary&lt; string,object&gt;