Search code examples
c#linqdapper

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


I don't know why the return value of the LoadData function (shown below) in one case supports the FirstOrDefault() method and in another case doesn't. In both cases it is the same return type.

The only difference is the way in which the dynamic parameter is created.

    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);

    }



Solution

  • p2 is dynamic, which means that result2 is also dynamic (since it takes p2 as an input), and FirstOrDefault is an extension method. Since the type of result2 is not known at compile time, you can't call an extension method on it.

    You can still do this by using the actual static method instead of an extension method:

    var rtv2 = Enumerable.FirstOrDefault(result2);
    

    since the static method can be bound at run-time.

    But I would just change GetUpdateParams so that it returns a named type rather than an anonymous type, and then you don't have to use dynamic. That will also get you more type safety since the compiler can do type checking and you don't have to wait until runtime to find type errors.