C# 将.net DateTime的Dapper映射更改为使用DbType DateTime2,然后再次将其恢复
与问题相似;但我希望以后能把它放回去 对于这个问题,目前公认的答案是更改一个简洁的源文件;但是我使用的是NuGet软件包,所以这对我来说不起作用。正如对公认答案的第一条评论所指出的,这是不可逆的——“如果一些是DateTime,而另一些是DateTime2呢?”——这就是我的场景:不同的查询需要不同的映射(每个查询只需要一个或另一个) 我在用这个词来回答同样的问题。然而,这种方法似乎也是不可逆的。似乎在第一次查询完成时设置的任何值都将保持不变,并且在之后都不会更改 以下代码是一个MCVE。如果运行它,您将看到类型始终显示为“datetime”,并且值的精度永远不会超过毫秒(正如您对datetime所期望的那样);尽管有人试图更改映射。然后您必须注释掉对“PerformDapperQuery()”的第一个调用,然后再次运行它:现在您将看到该类型始终返回为“datetime2”,并且这些值的精度为7位数,精确到一秒的小数点(正如您对datetime2所期望的那样) 因此,问题的第一部分是:如何多次更改Dapper的DateTime映射?问题的第二部分是我想恢复以前的映射;但正如您所看到的,C# 将.net DateTime的Dapper映射更改为使用DbType DateTime2,然后再次将其恢复,c#,sql-server,datetime,dapper,datetime2,C#,Sql Server,Datetime,Dapper,Datetime2,与问题相似;但我希望以后能把它放回去 对于这个问题,目前公认的答案是更改一个简洁的源文件;但是我使用的是NuGet软件包,所以这对我来说不起作用。正如对公认答案的第一条评论所指出的,这是不可逆的——“如果一些是DateTime,而另一些是DateTime2呢?”——这就是我的场景:不同的查询需要不同的映射(每个查询只需要一个或另一个) 我在用这个词来回答同样的问题。然而,这种方法似乎也是不可逆的。似乎在第一次查询完成时设置的任何值都将保持不变,并且在之后都不会更改 以下代码是一个MCVE。如果运
LookupDbType
被标记为过时,所以我对是否有其他方法感兴趣
在不信者Damien_给出缓存解释后进行编辑
我将上面的查询更改为$“SELECT sql_variant_property(@Param,'BaseType'),CAST(@Param AS datetime2(7))--{DateTime.Now:o}”,这样每次都会有所不同,这确实改变了行为
我遇到这个问题的原因是,我想添加一些东西来包装特定的简洁查询,使它们使用DateTime2
而不是DateTime
,因此我编写了这个类:
internal sealed class DapperDateTime2MapperScope : IDisposable
{
private readonly DbType? _predecessor;
private bool _isDisposed;
public DapperDateTime2MapperScope()
{
_predecessor = SqlMapperGetDbType();
SqlMapper.AddTypeMap(typeof(DateTime), DbType.DateTime2);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
if (_predecessor.HasValue)
{
SqlMapper.AddTypeMap(typeof(DateTime), _predecessor.Value);
}
}
_isDisposed = true;
}
}
private DbType SqlMapperGetDbType()
{
#pragma warning disable 618
return SqlMapper.LookupDbType(typeof(DateTime), null, false, out var handler);
#pragma warning restore 618
}
}
然后,可以使用它将Dapper查询包装到using块中,以使该查询使用DateTime2映射:
using (new DapperDateTime2MapperScope())
{
-- Perform Dapper query here
}
然后我编写了该类的单元测试——一个测试没有使用
的,另一个测试使用的;我发现单元测试相互作用:它们可以单独工作,但当所有测试都运行时,一个或另一个测试会失败。原因(多亏了Damien的解释)是Dapper对查询的缓存。好消息是,我认为这很好——单元测试遇到了问题,因为它们使用相同的查询;但在我真正的代码库中,如果我使用
将一个特定查询包装在这个中,那么我总是希望该查询使用该映射。因此,基本上这只是我的单元测试的一个问题,而不是类的真正用法。您的代码确实正确地更改了类型映射,但dapper积极地缓存查询
如果您的实际查询在datetime
和datetime2
用例之间发生变化(我希望它们会发生变化),那么应该可以。否则,您可以自己清除查询缓存(但显然,这可能会产生其他不希望的后果):
对于使用LookupDbType
,我认为您可以使用GetDbType
。GetDbType也过时了;但是缓存确实是一个问题,所以我将把它标记为答案并编辑这个问题,以解释在我为检查行为而编写的单元测试中这是如何发生的,以及(现在我已经考虑过了)为什么在实际代码中它实际上不是一个问题。谢谢
using (new DapperDateTime2MapperScope())
{
-- Perform Dapper query here
}
public static void Main()
{
// I know this is marked as obsolete, and I am open to suggestions for alternatives.
// see https://github.com/StackExchange/Dapper/issues/798
#pragma warning disable 618
var oldValue = SqlMapper.LookupDbType(typeof(DateTime), null, false, out var handler);
#pragma warning restore 618
PerformDapperQuery();
SqlMapper.AddTypeMap(typeof(DateTime), DbType.DateTime2);
SqlMapper.PurgeQueryCache();
PerformDapperQuery();
SqlMapper.AddTypeMap(typeof(DateTime), DbType.DateTime);
SqlMapper.PurgeQueryCache();
PerformDapperQuery();
SqlMapper.AddTypeMap(typeof(DateTime), oldValue);
SqlMapper.PurgeQueryCache();
PerformDapperQuery();
}