C# 对象不支持 FirstOrDefault() 取决于输入参数

C# Object doesnt support FirstOrDefault() depending on the input parameter

我不知道为什么 LoadData 函数(如下所示)的 return 值在一种情况下支持 FirstOrDefault() 方法,而在另一种情况下不支持。在这两种情况下,它都是相同的 return 类型。

唯一的区别是创建动态参数的方式。

    public async Task<LookupModel?> UpdateLookup(LookupModel item, Guid newUk)
        var p1 = new
        {
            item.Code,
            item.Name,
            item.uk,
            newUk
        };

        var result1 = await _db.LoadData<LookupModel, dynamic>("lu.Lookup_Update", p1);
                // result1.GetType() = System.Collections.Generic.List`1[DataAccess.Models.Lookups.LookupModel]
        var rtv1 = result1.FirstOrDefault();        // ok


        var p2 = item.GetUpdateParams(newUk);
        
        var result2 = await _db.LoadData<LookupModel, dynamic>("lu.Lookup_Update", p2);
                // result2.GetType() = System.Collections.Generic.List`1[DataAccess.Models.Lookups.LookupModel]

        var rtv2 = result2.FirstOrDefault();        // not ok
                                                    // This throws an exception as FirstOrDefault() is not defined

        return rtv1;
    }

    // LookupModel
    public dynamic GetUpdateParams(Guid newUk)
    {
        return new
        {
            this.Code,
            this.Name,
            this.uk,
            newUK
        };
    }

    // LoadData
    public async Task<IEnumerable<T>> LoadData<T, U>(string storedProcedure, U parameters, string connectionId = "Default")
    {
        using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));
        return await connection.QueryAsync<T>(storedProcedure, parameters, commandType: CommandType.StoredProcedure);

    }


因为p1是具体类型,而p2是对象类型(在运行时)。所以在 运行 时间,LoadData 将分别对匿名类型或对象泛型。由于对象没有任何属性,因此失败。

我建议在调用 LoadData

之前将 p2 克隆为具体的匿名类型来解决这个问题
Parameters p2clone = new { Code = p2.Code...};
var bar = LoadData(p2clone);

interface Parameters 
{
    string Code {get;}
    string Name {get;}
    ...
}

您需要这样做的原因是 Linq 表达式编译器在传入的类型上大量使用反射。没有类型信息,dapper 无法编译您的 SQL。

p2dynamic,这意味着 result2 也是动态的(因为它以 p2 作为输入),而 FirstOrDefault 是一个扩展方法。由于 result2 的类型在编译时未知,因此您不能对其调用扩展方法。

您仍然可以使用实际的静态方法而不是扩展方法来执行此操作:

var rtv2 = Enumerable.FirstOrDefault(result2);

因为静态方法 可以 在 运行 时绑定。

但我只想更改 GetUpdateParams 以便它 returns 命名类型而不是匿名类型,这样您就不必使用 dynamic。这也将使您获得更多类型安全性,因为编译器可以进行类型检查,您不必等到 运行 时间来查找类型错误。

从此行中删除等待

//var result1 = await _db.LoadData<LookupModel, dynamic>("lu.Lookup_Update", p1);
var result1 = _db.LoadData<LookupModel, dynamic>("lu.Lookup_Update", p1);

改变

var rtv1 = result1.FirstOrDefault();

var rtv1= await result1.FirstOrDefaultAsync()

应添加属于 FirstorDefaultAsync() 的命名空间。