Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dapper MultiMap doesn't work with splitOn with NULL value

Tags:

dapper

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");
    }
}
like image 751
Jakub Konecki Avatar asked May 24 '12 20:05

Jakub Konecki


1 Answers

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();
like image 153
Sam Saffron Avatar answered Sep 22 '22 13:09

Sam Saffron