Search code examples
c#mysqltimespanormlite-servicestack

ServiceStack.OrmLite: Reading back a TimeSpan using Untyped API results in InvalidCastException


I let ServiceStack OrmLite (5.1.1) create the table, and persist the object, that contains a TimeSpan:

// ...
public TimeSpan _Jobs_VehicleNotificationTime { get; set; }
// ...

When I try to read it back, I get this error:

System.InvalidCastException: 'Invalid cast from 'System.Int64' to 'System.TimeSpan'.'

The value is persisted as a long it seems: enter image description here

but I get this when using the FromObjectDictionary method:

enter image description here

Error is:

   at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
   at ServiceStack.PlatformExtensions.ObjectDictionaryFieldDefinition.SetValue(Object instance, Object value)
   at ServiceStack.PlatformExtensions.FromObjectDictionary(IReadOnlyDictionary`2 values, Type type)
   at tWorks.Core.CoreServerCommons.Handlers.OrmLiteDbHandler.<>c__DisplayClass65_1.<ReadObjects>b__1(Dictionary`2 x) in D:\[GIT]\Core\CoreServerCommons\Handlers\DbHandlers\OrmLite\OrmLiteDbHandler.cs:line 577

Is this a bug or am I missing something?


Solution

  • I think I have resolved the issue. Maybe @mythz can tell me if this is completely wrong, but it seems to work:

    Implement your own TimeSpanAsIntConverter

    I first dismissed that idea, as I wrongly interpreted Mythz to the effect of Converters were not relevant or were not executed if using the Untyped API. When I did implement the TimeSpan converter, it worked just as expected:

    namespace Commons
    {
        public class MyTimeSpanConverter : TimeSpanAsIntConverter
        {
            public override string ColumnDefinition => "TIME";
            public override DbType DbType => DbType.Time;
    
            public override object ToDbValue(Type fieldType, object value)
            {
                TimeSpan timespan = (TimeSpan)value;
                return timespan;
            }
    
        }
    }
    

    Then, when using that converter, the table is correctly created with the TIME type instead of bigint, and when persisted, it all looks OK:

    enter image description here

    Test code:

        public void Test()
        {
            Customer c = new Customer() { Username = "TED ÅÄÖ", DeletedTime = DateTime.MaxValue, MyTimeSpan = new TimeSpan(1, 30, 0) };
            CoreObject co = c;
            long id;
            using (IDbConnection db = _dbFactory.Open())
            {
                var typedApi = db.CreateTypedApi(co.GetType());
                id = typedApi.Insert(co, selectIdentity: true);
            };
    
            using (IDbConnection db = _dbFactory.Open())
            {
                string tableName = co.GetType().GetModelMetadata().ModelName;
                List<Dictionary<string, object>> results = db.Select<Dictionary<string, object>>($"SELECT * FROM {tableName} where id={id}");
                List<CoreObject> coreObjects = results.Map(x => (CoreObject)x.FromObjectDictionary(co.GetType()));
            }
        }
    

    Results: enter image description here

    This seems to resolve at least this issue for me - my TimeSpans work as expected.