代码之家  ›  专栏  ›  技术社区  ›  Richardissimo

将.net DateTime的适配器映射更改为使用DbType DateTime2并重新将其恢复

  •  3
  • Richardissimo  · 技术社区  · 6 年前

    类似于这个问题 How can I get Dapper to map .net DateTime to DateTime2 ;但我想事后再把它放回去。

    目前接受的这个问题的答案是更改Dapper源文件;但是我使用的是NuGet包,所以这对我不起作用。正如对公认答案的第一次评论所指出的,这是不可逆的- “如果有些是DateTime,有些是DateTime2呢?” -这就是我的场景:不同的查询需要不同的映射(每个查询只需要一个或另一个)。

    我用的是 higher-voted answer 同样的问题。然而,这种方法

    以下代码是MCVE。如果你运行它,你会看到类型是 总是 显示为“datetime”,并且这些值的精度永远不会超过毫秒(正如您对datetime的期望);尽管尝试更改映射。然后必须注释掉对“PerformDapperQuery()”的第一个调用,并再次运行它:现在您将看到类型是 总是

    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);
        PerformDapperQuery();
        SqlMapper.AddTypeMap(typeof(DateTime), DbType.DateTime);
        PerformDapperQuery();
        SqlMapper.AddTypeMap(typeof(DateTime), oldValue);
        PerformDapperQuery();
     }
    
    private static void PerformDapperQuery()
    {
        using (var connection = new SqlConnection("server=localhost;Database=master;Integrated Security=SSPI;"))
        {
            var parameters = new { Param = DateTime.Now };
            using (var reader = connection.ExecuteReader(
                "SELECT sql_variant_property(@Param, 'BaseType'), CAST(@PARAM AS datetime2(7))", parameters))
            {
                Assert.That(reader.Read(), Is.True);
                string type = reader.GetString(0);
                DateTime value = reader.GetDateTime(1);
                Console.WriteLine($"Output: {type},{value:o}");
            }
        }
    }
    

    所以问题的第一部分是:我如何可以多次更改Dapper对DateTime的映射?问题的第二部分是我想恢复之前的映射;但是正如您所看到的, LookupDbType

    不相信缓存的达米安解释后编辑

    我遇到这个问题的原因是我想添加一些东西来包装特定的Dapper查询,使它们能够使用 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
    }
    

    using 还有一个是 使用 ;我发现单元测试 相互配合:它们可以单独工作,但当所有测试都运行时,一个或其他测试将失败。原因(多亏达米恩的解释)是Dapper缓存查询。好消息是,我认为这很好——单元测试所遇到的问题是因为它们使用的是同一个查询;但是在我的实际代码库中,如果我将一个特定的查询包装在这个 然后我总是希望该查询使用该映射。所以基本上这只是我的单元测试的问题,而不是类的真正使用。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Damien_The_Unbeliever    6 年前

    您的代码确实正确地更改了类型映射,但是dapper会积极地缓存查询。

    如果您的实际查询在 datetime datetime2 用例(我希望他们会)那么应该没问题。否则,您可以自己清除查询缓存(但很明显,这可能会有其他、连锁反应、不良后果):

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

    供您使用 LookupDbType ,我想你可以用 GetDbType 相反。