DapperLib / DapperLib/Dapper

Non-nullable TypeHandler passed DBNull when selecting scalar results

Open
#224 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:typehandlers bug needs-investigation
Dominant language
C#
Stars
18.4k
Forks
3.7k
Avg merge
5h 8m
Merged PRs (30d)
1

Description

When selecting a single column result for a custom type that is nullable, DBNull is passed to the type handler normally resulting in an InvalidCastException as the type handler is not expecting a null. Since the type handler can't return null either, it has no sensible way to handle the DBNull.

void Main()
{
    SqlMapper.AddTypeHandler (new MoneyTypeHandler ());
    using (var db = new SqlConnection("Server=(local); Integrated Security=SSPI")) {
        db.Open ();

        // Normal CLR types work fine when nullable
        db.Query<int?> ("SELECT 1").Single ().Dump ();
        db.Query<int?> ("SELECT NULL").Single ().Dump ();

        // Nullable custom type works correctly when used as a property
        db.Query<Foo> ("SELECT 1 AS Cost").Single ().Dump ();
        db.Query<Foo> ("SELECT NULL AS Cost").Single ().Dump ();

        // Fails when used as a single column scalar result
        db.Query<Money?> ("SELECT 1").Single ().Dump ();
        db.Query<Money?> ("SELECT NULL").Single ().Dump ();
    }  
}

public class MoneyTypeHandler : SqlMapper.TypeHandler<Money>
{
    public override void SetValue (IDbDataParameter p, Money value)
    {
        p.DbType = DbType.Int32;
        p.Value = value.Value;
    }

    public override Money Parse (object obj)
    {
        if (obj is DBNull) {
            throw new InvalidCastException ("Expected type int, got type DBNull");
        }
        return new Money ((int) obj);
    }
}

public struct Money
{
    private readonly int value;

    public Money (int value)
    {
        this.value = value;
    }

    public int Value { get { return value; } }
}

public class Foo
{
    public Money? Cost { get; set; }
}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied C# repro and trace the scalar Query path for nullable custom types and registered type handlers. Confirm the behavior for SELECT NULL and SELECT 1, then verify that a nullable scalar result no longer passes DBNull to a non-nullable handler while ordinary scalar and property mapping cases still work.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sql
Domain
backend-api-design, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.