62

Dapperを使用してエンティティをSQLServerCEにマップしています。withを保存するDateTimeKind=Utc、それを読み返すとDateTimewithKind=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いますが、残念ながらSQLCEはこのタイプをサポートしていません。

回避策はありますか?すべての日付にあると仮定するように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;
}
4

4 に答える 4

102

簡単な修正を探しに来る他の人のためにこの答えを追加します。これは、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);
于 2016-09-27T16:52:06.047 に答える
28

Dapperコードを調べました。私が古くなっていない限り、datetime(DbType.DateTimeにマップされている)のような値型の場合、dapperはIDataReaderオブジェクトから単純なキャストを実行します。

疑似:yield return(DateTime)IDataReader.GetValue(0);

これは、一連のジェネリックコードとラムダからのDatetimeの特定のケースです。

AFAIK、SQL日時はオフセット/タイムゾーンを保存しないため、保存およびフェッチする日時には常に「未指定」と表示されます。

だから、それをきれいに行うために、あなたはdapperの内部に触れることができます:

これは、大きなIL生成メソッド(DataRow Deserializer)に触れて、DateTimeのifケースに入れる必要があるため面倒です。

また

UTCが問題であるDateTimeプロップにセッターを置くだけです(これは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
}
于 2013-06-13T17:08:48.523 に答える
4

DateTimeOffset/ DateTimeOffset?fields /propertiesをMySQL5.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());
        }
    }
}
于 2018-04-11T16:53:22.363 に答える
0

ソース(nugetではなく)からDapperを使用している場合は、コードを微調整して、常にUTCのDateTimeKindを強制することができます。より構成可能なオプションは、dapperへのヒントとして日時の種類を指定できるDateTimeプロパティ値の新しい属性を作成することです。Dapperは、この属性を持つDateTimeプロパティを検索し、見つかった場合は、それを使用してORMマッピング中にDateTimeの種類を指定できます。この問題を抱えているのはあなただけではないので、これはコアダッパーにとって素晴らしい機能かもしれません:)

于 2014-01-24T21:42:03.147 に答える