【问题标题】:ServiceStack.OrmLite: Reading back a TimeSpan using Untyped API results in InvalidCastExceptionServiceStack.OrmLite:使用无类型 API 读回 TimeSpan 会导致 InvalidCastException
【发布时间】:2018-08-01 03:58:55
【问题描述】:

我让 ServiceStack OrmLite (5.1.1) 创建表,并持久化包含 TimeSpan 的对象:

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

当我尝试读回它时,我收到了这个错误:

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

is persisted as a long 的值似乎是:

但我在使用 FromObjectDictionary 方法时得到了这个:

错误是:

   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

这是一个错误还是我遗漏了什么?

【问题讨论】:

    标签: c# mysql timespan ormlite-servicestack


    【解决方案1】:

    TimeSpan 在 OrmLite 中存储为整数列,以确保它们在所有受支持的 RDBMS 中保持精度和行为。如果您使用对象字典中的动态结果集检索它,那么它将只能返回尚未通过 OrmLite 转换器将其转换回 TimeSpan 的数据读取器值,在这种情况下您不会可以在这里使用 ServiceStack.Text 的 FromObjectDictionary() 通用扩展方法,它不使用 OrmLite 的转换器。

    【讨论】:

    • ... 因为我必须使用动态结果集,因为我正在加载我所有的“对象”,而不是在编译时指定我应该加载哪些类(只是父类@987654326 @),唯一的解决方案是删除TimeSpan?使用FromObjectDictionary时有没有办法启用转换器?
    • ...还有 - 没有conversian?需要进行一些转换操作。例如,我认为日期时间在 MySql 中存储为字符串,需要将其转换为日期时间? DateTime 和 TimeSpan 都有接受long 的构造函数,为什么不使用它们呢?为什么我们要在 FromObjectDictionary 方法中传入一个类型?使用 new TimeSpan(54000000000) 将按预期产生 1h30m。
    • TimeSpanConverter 就是这样做的:github.com/ServiceStack/ServiceStack.OrmLite/blob/master/src/…。那么,当我使用动态结果集时,为什么不这样做呢?基本上,如果在运行时知道Type,就可以确定属性是TimeSpan,值是long,那么使用构造函数?
    • @Ted TimeSpan 的序列化逻辑仅在 OrmLite 中。 JSV/JSON 序列化支持在几秒钟内从 double 反序列化为 TimeSpan,以便与其他 JSON 服务互操作,但 OrmLite 将 .NET Ticks 存储为 long 以保持精度。无论如何,我决定在 AutoMapping Utils for long > TimeSpan 中添加一个异常,该异常在 MyGet 上的最新 v5.1.1 中可用。但将来不会在 SS.Text 的 AutoMapping Utils 中导入其他 OrmLite 转换器行为。
    • 与this commit 分开,相关行for TimeSpan < - > long。
    【解决方案2】:

    我想我已经解决了这个问题。也许@mythz 可以告诉我这是否完全错误,但它似乎有效:

    实现你自己的TimeSpanAsIntConverter

    我首先驳回了这个想法,因为我错误地将 Mythz 解释为转换器不相关或如果使用无类型 API 则未执行。当我实现 TimeSpan 转换器时,它按预期工作:

    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;
            }
    
        }
    }
    

    然后,当使用该转换器时,该表是使用 TIME 类型而不是 bigint 正确创建的,并且在持久化时,一切看起来都正常:

    测试代码:

        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()));
            }
        }
    

    结果:

    这似乎至少为我解决了这个问题 - 我的 TimeSpan 按预期工作。

    【讨论】:

    • 这改变了TimeSpan 的存储方式,因此ADO.NET 提供程序返回TimeSpan 而不是long,代价是与其他RDBMS 的互操作性以及MySql TIME Type 无法存储TimeSpan 值的全部范围。
    • 当然,但这是一个小问题。现在我可以实际使用 Untyped API,这可能是 OrmLite 中最重要的 API ;-)
    猜你喜欢
    • 1970-01-01
    • 2017-05-07
    • 2012-10-28
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-09
    • 2018-03-06
    相关资源
    最近更新 更多