A parameterless default constructor is required to allow for dapper materialization error when reading object with list of child objects

asp.net-mvc-3 c# dapper

Question

I have this:

public class object_a
{
    public int ta_Id { get; set; }
    public string ta_Label { get; set; }
    public IEnumerable<table_c> SomeName { get; set; }
}

public class table_b
{
    public int Id {get;set;}
    public int SomeId {get;set;}
    public int FK_A {get;set;}
}

public class table_c
{
    public int Id {get;set;}
    public int Max {get;set;}
    public string Label {get;set;}
    public int FK_A {get;set;}
}

Using Dapper I retrieve a list of object_a with any number of child objects table_c

using (System.Data.SqlClient.SqlConnection sqlConnection = new System.Data.SqlClient.SqlConnection(_con))
{
        sqlConnection.Open();
        var sql = string.Format(
        @"
        select ta.Id as ta_Id, ta.Label as ta_label, splitLimit = '', tc.Id, tc.Max, tc.Label
        from table_a as ta
        left join table_b as tb
        on tb.FK_A = ta.Id
        left join table_c as tc
        on tc.FK_A = ta.Id
        where tb.SomeId = SomeInt);

        var items = sqlConnection.Query<object_a, IEnumerable<table_c>, object_a>(sql, (a, c) => { a.c = table_c; return a; }, splitOn: "splitLimit");
        return items;
    }

When calling the Dapper code from a controller I get this error:

A parameterless default constructor is required to allow for dapper materialization

I have also tried this:

var items = sqlConnection.Query<object_a,IEnumerable<table_c>, object_a>(sql, (a, c) => 
{ 
                    if (c!= null) 
                    {
                        a.SomeName = c;
                    } 
                    return a; 

                }, splitOn: "splitLimit");

I think it might has something to do with the IEnumerable<table_c> but I don't understand what I am doing wrong here. I have read related questions that SO suggest, but I don't 'get' it.

I would like to know what I am doing wrong and what the correct code is. Thanks!

Accepted Answer

The problem is the IEnumerable<>, here:

Query<object_a, IEnumerable<table_c>, object_a>

dapper can't create an IEnumerable<table_c>, and it is never going to. The way this method works is intended to be:

Query<object_a, table_c, object_a>

then for every row dapper will create an object_a and a table_c from the row, then use your user-provided method to combine the two. Currently, we don't have any inbuilt code to perform identity-based aggregation, but that could be added in the custom method - for example the following tries to roll-up repeated object_a instances:

var identityMap = new Dictionary<int, object_a>();
var data = Query<object_a, table_c, object_a>(sql, (a, c) => {
    object_a master;
    if(!identityMap.TryGetValue(a.ta_id, out master)) {
        identityMap[a.ta_id] = master = a;
    }
    var list = (List<table_c>)master.SomeName;
    if(list == null) {
        master.SomeName = list = new List<table_c>();
    }
    list.Add(c);
    return master;
}, ...).Distinct().ToList();


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why