【问题标题】:Mapping TimeSpan in SQLite and Dapper在 SQLite 和 Dapper 中映射 TimeSpan
【发布时间】:2015-01-22 10:48:50
【问题描述】:

我正在尝试使用 Dapper 连接到现有数据库格式,该格式具有一个表,其持续时间编码为 BIGINT 列中的刻度。在插入和读取数据库时,如何告诉 Dapper 将我的 POCO 的 TimeSpan-typed 属性映射到刻度?

我尝试将TimeSpan 的类型映射设置为DbType.Int64

SqlMapper.AddTypeMap(typeof(TimeSpan), DbType.Int64);

我还创建了一个ITypeHandler,但从未调用过SetValue 方法:

public class TimeSpanToTicksHandler : SqlMapper.TypeHandler<TimeSpan>
{
    public override TimeSpan Parse(object value)
    {
        return new TimeSpan((long)value);
    }

    public override void SetValue(IDbDataParameter parameter, TimeSpan value)
    {
        parameter.Value = value.Ticks;
    }
}

这是我的 POCO:

public class Task
{
    public TimeSpan Duration { get; set; }

    // etc.
}

当执行这样一个简单的插入语句时:

string sql = "INSERT INTO Tasks (Duration) values (@Duration);";

并将 POCO 作为要插入的对象传递:

Task task = new Task { Duration = TimeSpan.FromSeconds(20) };
connection.Execute(sql, task);

我得到了这个例外:

System.InvalidCastException : Unable to cast object of type 'System.TimeSpan' to type 'System.IConvertible'.
   at System.Convert.ToInt64(Object value, IFormatProvider provider)
   at System.Data.SQLite.SQLiteStatement.BindParameter(Int32 index, SQLiteParameter param)
   at System.Data.SQLite.SQLiteStatement.BindParameters()
   at System.Data.SQLite.SQLiteCommand.BuildNextCommand()
   at System.Data.SQLite.SQLiteCommand.GetStatement(Int32 index)
   at System.Data.SQLite.SQLiteDataReader.NextResult()
   at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
   at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(CommandBehavior behavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
   at Dapper.SqlMapper.ExecuteCommand(IDbConnection cnn, ref CommandDefinition command, Action`2 paramReader) in SqlMapper.cs: line 3310
   at Dapper.SqlMapper.ExecuteImpl(IDbConnection cnn, ref CommandDefinition command) in SqlMapper.cs: line 1310
   at Dapper.SqlMapper.Execute(IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Nullable`1 commandTimeout, Nullable`1 commandType) in SqlMapper.cs: line 1185

如果我保留TimeSpan 类型映射原样(默认为DbType.Time),它会写入TimeSpan 的字符串版本,即`00:00:20.000",这无济于事它与列中其他数据的格式不匹配。

【问题讨论】:

  • 我总是通过创建第二个属性来解决这个问题,该属性将数据库类型转换为我的类型。
  • 为什么投反对票...?

标签: c# sqlite orm dapper system.data.sqlite


【解决方案1】:

LinqToDB 解决方案:

MappingSchema.SetDataType(typeof(TimeSpan), DataType.NText);

或者:

MappingSchema.SetDataType(typeof(TimeSpan), DataType.Int64);

例子:

    public class Program
{
    private const string ConnectionString = "Data Source=:memory:;Version=3;New=True;";

    public static void Main()
    {
        var dataProvider = new SQLiteDataProvider();

        var connection = dataProvider.CreateConnection(ConnectionString);
        connection.Open();

        var dataConnection = new DataConnection(dataProvider, connection);

        dataConnection.MappingSchema.SetDataType(typeof(TimeSpan), DataType.Int64);

        dataConnection.CreateTable<Category>();

        dataConnection.GetTable<Category>()
            .DataContextInfo
            .DataContext
            .Insert(new Category
            {
                Id = 2,
                Time = new TimeSpan(10, 0, 0)
            });


        foreach (var category in dataConnection.GetTable<Category>())
        {
            Console.WriteLine($@"Id: {category.Id}, Time: {category.Time}");
        }
    }

    private class Category
    {
        public int Id { get; set; }
        public TimeSpan Time { get; set; }
    }
}

【讨论】:

  • MappingSchema.SetDataType(typeof(TimeSpan), DataType.NText); 这在插入时失败,错误MappingSchema.SetDataType(typeof(TimeSpan), DataType.Int64); 适用于插入,但导致在选择时引发异常System.FormatException: String was not recognized as a valid DateTime.
  • 为了使它工作dataConnection.MappingSchema.SetDataType(typeof(TimeSpan), DataType.Int64);必须为每个新的连接实例执行。我试过像LinqToDB.Mapping.MappingSchema.Default.SetDataType(typeof(TimeSpan), DataType.Int64) 一样设置一次,但没有运气
【解决方案2】:

您可以改为执行以下操作吗?

public class Task
{
    public TimeSpan Duration { get; set; }
    public long Ticks 
    { 
        get { return Duration.Ticks; }
        set { Duration = new TimeSpan(value); }
    }
    // etc.
}

string sql = "INSERT INTO Tasks (Duration) values (@Ticks);";

【讨论】:

  • 我可以,但作为一个 Dapper 新手,我希望图书馆能提供一些东西。似乎应该有所有的ITypeHandler 和其他这样的接口......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 2013-08-24
相关资源
最近更新 更多