DapperLib / Dapper

Dapper - a simple object mapper for .Net
https://www.learndapper.com/
Other
17.47k stars 3.67k forks source link

InvalidCastException - Converting DB type Int64 to C# IComparable #1207

Open bhasden opened 5 years ago

bhasden commented 5 years ago

I'm having an issue where I'm trying to query integer data and populate it onto a property of type IComparable. The exception that occurs is:

{System.InvalidCastException: Invalid cast from 'System.Int64' to 'System.IComparable'. at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)}

The best I can tell, there's an issue with the IL emitted code in SqlMapper.FlexibleConvertBoxedFromHeadOfStack where it's falling back to a System.Convert.ChangeType() call if it didn't successfully handle the type conversion earlier in the method. An additional else if condition to check if the to type is assignable from the from type and casting directly to the to type appears to resolve the bug. I put a commit together at https://github.com/bhasden/Dapper/commit/135a30b71f03a0830024f397de5f20b76271ba07 that resolves the issue for my scenario. I'm happy to submit a PR if this is an acceptable fix. I have a test project that I can convert to a unit test as well. Any feedback is greatly appreciated.

mgravell commented 5 years ago

So to be clear: you have something like:

class SomeModel {
    // ... other things
    public IComparable SomeValue {get;set;}
}

and the value of SomeValue is a long at runtime. Is that right?

That isn't a scenario I've explicitly considered; I think I'd consider this more a "feature request" than a "bug", though. Can I ask for more info about the scenario where you want to use this kind of layout? It just seems... unusual, so I'd like to understand the intent more.

bhasden commented 5 years ago

Sure. I've worked around the issue/feature request for now, but decided to go on a bit of a fact finding mission to see what it would take to make Dapper handle the mapping internally. As far as the code I'm working with, I have a class that's something like:

        protected class MyObject
        {
            public IComparable ID { get; set; }
            public string Value { get; set; }
            ...
        }

That object can be created/populated from various places. In the case where it's populated from the DB, the ID property is populated from a PK which is an integer. In other instances, the property is populated with other types. Normally, I'd use some sort of DB representation for this object and then map between it and the desired type, but that's a bit more difficult in this situation and seems a little unnecessary since there's no real type conversion or manipulation outside of the mapping of the ID field. It may help to mention that all of this occurs in some behind the scenes processing within a couple of private methods.

Olbrasoft commented 5 years ago

await multi.ReadFirstAsync() Error (int)await multi.ReadFirstAsync() Ok