Dapper MultiMap doesn't work with splitOn with NULL value

dapper

Question

I have a problem with MultiMaps in dapper trying to split on column that contains NULL. Dapper seems not to instantiate object and my mapping function receives null instead of object.

Here's my new test:

    class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Category Category { get; set; }
    }
    class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
    public void TestMultiMapWithSplitWithNullValue()
    {
        var sql = @"select 1 as id, 'abc' as name, NULL as description, 'def' as name";
        var product = connection.Query<Product, Category, Product>(sql, (prod, cat) =>
        {
            prod.Category = cat;
            return prod;
        }, splitOn: "description").First();
        // assertions
        product.Id.IsEqualTo(1);
        product.Name.IsEqualTo("abc");
        product.Category.IsNotNull();
        product.Category.Id.IsEqualTo(0);
        product.Category.Name.IsEqualTo("def");
        product.Category.Description.IsNull();
    }

The line that fails is product.Category.IsNotNull(); due to the fact that cat passed to mapping function is null.

I've also added this method to Assert class:

public static void IsNotNull(this object obj)
{
    if (obj == null)
    {
        throw new ApplicationException("Expected not null");
    }
}

Accepted Answer

This is "by-design" though I would be ok to revisit it.

In particular this behaviour is there to help with left joins. Take this for example:

cnn.Query<Car,Driver>("select * from Cars c left join Drivers on c.Id = CarId",
   (c,d) => {c.Driver = d; return c;}) 

Trouble is that if we allow a "blanket" creation of a Driver object, every Car is going to have a Driver even ones where the join failed.

To work around we could scan the entire segment being split and ensure ALL values are NULL before mapping a NULL object. This will have a very minor perf impact on the multi mapper.

To workaround for your case, you could insert a surrogate column:

var sql = @"select 1 as id, 'abc' as name, '' as split, 
            NULL as description, 'def' as name";
    var product = connection.Query<Product, Category, Product>(sql, (prod, cat) =>
    {
        prod.Category = cat;
        return prod;
    }, splitOn: "split").First();

Popular Answer

For all who wants visualization :

Dapper splits by the last equal column name :

enter image description here

Let's swap location of columns :

enter image description here

null problem :

enter image description here

Swapped column null :

enter image description here

Spliton to the rescue :

enter image description here




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