我正在使用Dapper将我的实体映射到SQL Server CE。如果我使用DateTime
保存Kind=Utc
,当我将其读回时,我会收到DateTime
Kind=Unspecified
,这会导致各种问题。
示例:
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 CE不支持此类型。
有解决方法吗?我可以告诉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;
}
答案 0 :(得分:57)
为寻求简单修复的其他人添加此答案。现在可以在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 API的Global.asax文件中,我将类型处理程序添加到dapper。
SqlMapper.AddTypeHandler(new DateTimeHandler());
如果您需要确保始终以UTC格式插入日期,则可以使用SetValue方法:
parameter.Value = DateTime.SpecifyKind(value, DateTimeKind.Utc);
答案 1 :(得分:27)
查看Dapper代码。除非我的已经过时,对于像datetime这样的值类型(映射到DbType.DateTime),dapper只是从IDataReader对象进行简单的转换。
伪:yield return(DateTime)IDataReader.GetValue(0);
这是日期时间中一堆通用代码和lambdas的具体情况。
AFAIK,SQL datetime永远不会存储偏移量/时区,因此在您存储和获取的任何日期时间,该类型总是会显示“未指定”。
所以,要干净利落地,你可以触摸小巧玲珑的内部:
这是一个痛苦,因为你必须触摸一个大的IL生成方法(DataRow Deserializer)并为DateTime添加一个if case。
OR
只是在DateTime道具上放置一个setter,其中UTC是一个问题(这有点反对POCO,但相对理智):
class Foo
{
private DateTime _modificationDate;
public DateTime ModificationDate
{
get { return _modificationDate; }
set { _modificationDate = DateTime.SpecifyKind(value, DateTimeKind.Utc); }
}
//Ifs optional? since it's always going to be a UTC date, and any DB call will return unspecified anyways
}
答案 2 :(得分:3)
我想在此处提供完整的解决方案,以便将DateTimeOffset
/ DateTimeOffset?
字段/属性与MySQL 5.7数据库(不支持DbType.DateTimeOffset
)无缝集成 - 基于@matt -jenkins回答如上:
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());
}
}
}
答案 3 :(得分:1)
如果您从源代码使用Dapper(而不是nuget),则可以调整代码以始终强制使用UTC的DateTimeKind。一个更可配置的选项可能是为DateTime属性值创建一个新属性,允许您指定日期时间类型作为dapper的提示。 Dapper可以使用此属性查找DateTime属性,并且在找到时可以使用它在ORM映射期间指定DateTime类型。这可能是核心小巧玲珑的一个很好的功能,因为你不是唯一有这个问题的人:)