Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dapper doesn't read values from base-type fields

Tags:

c#

sqlite

dapper

Base type:

public class TreeRecord
{
    public long id;
    public long? parent_id;
}

Record type:

public class TestRecord : TreeRecord
{
    public string name;
}

Using Dapper to inset will fail, because it can't find the values. I suspect that is because some of the fields are from the base type.

public long Add(T record, bool returnIndex = false)
{
    var query =
        $"INSERT INTO {TableName} ({string.Join(", ", _fields)}, parent_id) " +
        $"VALUES (@{string.Join(", @", _fields)}, @parent_id);";

    // Seems like Dapper won't flatten this type hierachy.
    _connection.Execute(query, record);
}

I'm working in a generic container, where T inherits from TR and "extends" the fields. Because of this, I can't simply provide a flattened new ... construct for the value parameters for Dapper. _fields are non-base fields in T, in this case just name from TestRecord.

The exception thrown is:

System.InvalidOperationException: Must add values for the following parameters: @name, @parent_id

even though both are set (p_id to null, name to Books) (inside Dapper's Execute):

Is it a flattening issue? IMO, because all fields on a TestRecord, including the base fields, can be accessed by var.field, Dapper should be able to use it.

like image 497
turbo Avatar asked Mar 10 '26 20:03

turbo


1 Answers

The behavior you're seeing here is because ultimately Dapper is looking for properties and not fields. Underneath the covers, it's calling typeof(RecordType).GetProperties(); and enumerating them for the dynamic parameter generation.

If you change your types to:

public class TreeRecord
{
    public long id { get; set; }
    public long? parent_id { get; set; }
}

public class TestRecord : TreeRecord
{
    public string name { get; set; }
}

...then you should see it working, on base or inherited types.

like image 192
Nick Craver Avatar answered Mar 12 '26 08:03

Nick Craver