我使用Dapper将我的实体映射到Server。如果我用Kind=Utc
保存一个Kind=Utc
,当我把它读回来时,我会得到一个带有Kind=Unspecified
的DateTime
,这会导致各种各样的问题。
示例:
var f = new Foo { Id = 42, ModificationDate = DateTime.UtcNow };
Console.WriteLine("{0} ({1})", f.ModificationDate, f.ModificationDate.Kind);
connection.Execute("insert into Foo(Id, ModificationDate) values(@Id, @ModificationDate)", f);
var f2 = connection.Query<Foo>("select * from Foo where Id = @Id", f).Single();
Console.WriteLine("{0} ({1})", f2.ModificationDate, f2.ModificationDate.Kind);
此代码提供以下输出:
20/09/2012 10:04:16 (Utc)
20/09/2012 10:04:16 (Unspecified)
我知道我应该使用DateTimeOffset
,但不幸的是,SQL不支持这种类型。
有解决办法吗?我能告诉Dapper假设所有的日期都有DateTimeKind.Utc
吗?更广泛地说,我有哪些选项可以自定义映射?
编辑:我目前的解决办法是在Dapper将结果具体化后修补日期,但它有点气味……
var results = _connection.Query<Foo>(sql, param).Select(PatchDate);
...
static Foo PatchDate(Foo f)
{
if (f.ModificationDate.Kind == DateTimeKind.Unspecified)
f.ModificationDate = DateTime.SpecifyKind(f.ModificationDate, DateTimeKind.Utc);
return f;
}
发布于 2016-09-27 16:52:06
添加这个答案给其他人谁来寻找一个简单的修复。现在,通过在Dapper中添加SqlMapper.TypeHandler,这是可能的。
添加此类以将值从db转换为指定为UTC的日期时间。
public class DateTimeHandler : SqlMapper.TypeHandler<DateTime>
{
public override void SetValue(IDbDataParameter parameter, DateTime value)
{
parameter.Value = value;
}
public override DateTime Parse(object value)
{
return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
}
}
然后,在Web的Global.asax文件中,将类型处理程序添加到dapper中。
SqlMapper.AddTypeHandler(new DateTimeHandler());
如果需要确保始终以UTC的形式插入日期,则可以在SetValue方法上使用:
parameter.Value = DateTime.SpecifyKind(value, DateTimeKind.Utc);
发布于 2018-04-11 16:53:22
我只想把我的完整解决方案放在这里,以便将DateTimeOffset
/ DateTimeOffset?
字段/属性与MySQL 5.7数据库(不支持DbType.DateTimeOffset
)无缝集成--基于上面@matt的答案:
public static class DapperExtensions
{
class DateTimeOffsetTypeHandler : SqlMapper.TypeHandler<DateTimeOffset>
{
public override void SetValue(IDbDataParameter parameter, DateTimeOffset value)
{
switch (parameter.DbType)
{
case DbType.DateTime:
case DbType.DateTime2:
case DbType.AnsiString: // Seems to be some MySQL type mapping here
parameter.Value = value.UtcDateTime;
break;
case DbType.DateTimeOffset:
parameter.Value = value;
break;
default:
throw new InvalidOperationException("DateTimeOffset must be assigned to a DbType.DateTime SQL field.");
}
}
public override DateTimeOffset Parse(object value)
{
switch (value)
{
case DateTime time:
return new DateTimeOffset(DateTime.SpecifyKind(time, DateTimeKind.Utc), TimeSpan.Zero);
case DateTimeOffset dto:
return dto;
default:
throw new InvalidOperationException("Must be DateTime or DateTimeOffset object to be mapped.");
}
}
}
private static int DateTimeOffsetMapperInstalled = 0;
public static void InstallDateTimeOffsetMapper()
{
// Assumes SqlMapper.ResetTypeHandlers() is never called.
if (Interlocked.CompareExchange(ref DateTimeOffsetMapperInstalled, 1, 0) == 0)
{
// First remove the default type map between typeof(DateTimeOffset) => DbType.DateTimeOffset (not valid for MySQL)
SqlMapper.RemoveTypeMap(typeof(DateTimeOffset));
SqlMapper.RemoveTypeMap(typeof(DateTimeOffset?));
// This handles nullable value types automatically e.g. DateTimeOffset?
SqlMapper.AddTypeHandler(typeof(DateTimeOffset), new DateTimeOffsetTypeHandler());
}
}
}
发布于 2014-01-24 21:42:03
如果您是从源(而不是nuget)使用Dapper,则可以调整代码以始终强制UTC的DateTimeKind。一个更可配置的选项可能是为DateTime属性值创建一个新属性,该属性允许您指定日期时间类型作为对dapper的提示。Dapper可以使用这个属性查找DateTime属性,当发现它时,可以在ORM映射期间使用它来指定DateTime类型。对于核心dapper来说,这可能是一个很好的特性,因为您不是唯一有此问题的人:)
https://stackoverflow.com/questions/12510299
复制相似问题