Skip to content

Binding a type implementing IEnumerable expands in to more values than there are parameters. #754

@Swahhillie

Description

@Swahhillie

I am trying to use a custom type handler for a collection type.
The problem is that SqlKata.Helper.Flatten(IEnumerable<object> array) is decomposing my collection type into its parts.
Thereby it creates an invalid query with more values than parameters.

Reproduction code:

using System.Collections;
using System.Data;
using System.Data.SQLite;
using Dapper;

using SqlKata.Compilers;
using SqlKata.Execution;

var sqliteCompiler = new SqliteCompiler();
var connectionStringBuilder = new SQLiteConnectionStringBuilder() { DataSource = ":memory:" };
using var connection = new SQLiteConnection(connectionStringBuilder.ToString());
var db = new QueryFactory(connection, sqliteCompiler);

db.Statement(@"
CREATE TABLE test (
    id integer primary key autoincrement,
    single blob,
    multi blob
);
");

SqlMapper.AddTypeHandler(new SomeTypeHandler());
SqlMapper.AddTypeHandler(new SomeCollectionTypeHandler());

// Ok
db.Query("test")
    .Insert(new { single = new SomeSingleObject("Alice") });

//multi type fails, SqlKata.Helper.Flatten public static IEnumerable<object> Flatten(IEnumerable<object> array)
//flattens what is supposed to be one parameter and creates multiple instead because the collection type implements IEnumerable
db.Query("test")
    .Insert(new { multi = new SomeCollectionType([new("Alice"), new("Bob")]) });

class SomeSingleObject
{
    public SomeSingleObject(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

class SomeCollectionType : IEnumerable<SomeSingleObject>
{
    public SomeCollectionType(SomeSingleObject[] myThings)
    {
        MyThings = myThings;
    }

    public SomeSingleObject[] MyThings { get; set; }

    public IEnumerator<SomeSingleObject> GetEnumerator()
    {
        foreach (var thing in MyThings)
            yield return thing;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class SomeTypeHandler : SqlMapper.TypeHandler<SomeSingleObject>
{
    public override void SetValue(IDbDataParameter parameter, SomeSingleObject value)
    {
        parameter.DbType = DbType.String;
        parameter.Value = value.Name;
    }

    public override SomeSingleObject Parse(object value)
    {
        return new SomeSingleObject((string)value);
    }
}

class SomeCollectionTypeHandler : SqlMapper.TypeHandler<SomeCollectionType>
{
    public override void SetValue(IDbDataParameter parameter, SomeCollectionType value)
    {
        parameter.DbType = DbType.String;
        parameter.Value = string.Join(",", value.MyThings.Select(my => my.Name));
    }

    public override SomeCollectionType Parse(object value)
    {
        return new SomeCollectionType(((string)value).Split(",")
            .Select(s => new SomeSingleObject(s)).ToArray());
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions