Linq To SQLクラスに特定のDateTimeプロパティをUTCと見なす(つまり、DateTime型のKindプロパティをデフォルトでUtcにする)ことを伝える(簡単な)方法はありませんか、または「クリーン」な回避策はありますか? ?
私のapp-serverのタイムゾーンはSQL 2005 Serverと同じではなく(何も変更できません)、UTCはありません。 DateTimeタイプのプロパティをdBに永続化する場合、UTC値を使用します(したがって、db列の値はUTCです)が、値を読み戻すと(Linq To SQLを使用して)、DateTimeの.Kindプロパティを取得します。値は「未指定」になります。
問題は、それをUTCに「変換」すると4時間オフになることです。これは、シリアル化されると、クライアント側で4時間の間違ったオフセットで終了することも意味します(UTCを使用してシリアル化されるため)。
生成されたLinqToSqlコードは拡張ポイントを提供するため、オブジェクトが読み込まれたときに値を設定できます。
重要なのは、生成されたクラスを拡張する部分クラスを作成してから、OnLoaded
部分メソッドを実装することです。
たとえば、クラスがPerson
であるとすると、Blah.designer.cs
に生成された部分的なPerson
クラスがあります。
次のように、新しいクラス(別のファイルにある必要があります)を作成して、部分クラスを拡張します。
public partial class Person {
partial void OnLoaded() {
this._BirthDate = DateTime.SpecifyKind(this._BirthDate, DateTimeKind.Utc);
}
}
SQL ServerのDateTimeには、タイムゾーンまたはDateTimeKind情報が含まれていないため、データベースから取得されたDateTime値のKind = DateTimeKind.Unspecifiedが正しくなります。
これらの時刻をUTCにしたい場合は、次のように「変換」する必要があります。
DateTime utcDateTime = new DateTime(databaseDateTime.Ticks, DateTimeKind.Utc);
または同等:
DateTime utcDateTime = DateTime.SpecifyKind(databaseDateTime, DateTimeKind.Utc);
私はあなたの問題があなたがそれらを次のように変換しようとしていることだと思います:
DateTime utcDateTime = databaseDateTime.ToUniversalTime();
これは一見合理的に見えるかもしれませんが、 DateTime.ToUniversalTimeのMSDNドキュメント によると、種類が指定されていないDateTimeを変換する場合:
現在のDateTimeオブジェクトは現地時間であると見なされ、KindがLocalであるかのように変換が実行されます。
この動作は、DateTime.Kindプロパティを持たない.NET1.xとの下位互換性のために必要です。
これを行うために私が考えることができる唯一の方法は、変換を行う部分クラスにshimプロパティを追加することです...
私たちの場合、前述のように常にDateTimeKindを指定することは実用的ではありませんでした。
DateTime utcDateTime = DateTime.SpecifyKind(databaseDateTime, DateTimeKind.Utc);
Entity Frameworkを使用していますが、これはLinq-to-SQLに似ているはずです。
データベースから出てくるすべてのDateTimeオブジェクトを強制的にUTCとして指定する場合は、T4変換ファイルを追加し、DateTimeKind.Utcとして初期化されるように、すべてのDateTimeおよびnullable DateTimeオブジェクトのロジックを追加する必要があります
この手順を段階的に説明するブログ投稿があります: http://www.aaroncoleman.net/post/2011/06/16/Forcing-Entity-Framework-to-mark-DateTime-fields-at- UTC.aspx
要するに:
1).edmxモデルの.ttファイル(またはLinq-to-SQLの場合は.dbml)を作成します
2).ttファイルを開き、「WritePrimitiveTypeProperty」メソッドを見つけます。
3)既存のセッターコードを置き換えます。これは、ReportPropertyChanging
メソッドとReportPropertyChanged
メソッドのコールバックの間のすべてです。
<#+ if( ((PrimitiveType)primitiveProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.DateTime)
{
#>
if(<#=code.FieldName(primitiveProperty)#> == new DateTime())
{
<#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>);
<#+
if(ef.IsNullable(primitiveProperty))
{
#>
if(value != null)
<#=code.FieldName(primitiveProperty)#> = DateTime.SpecifyKind(<#=code.FieldName(primitiveProperty)#>.Value, DateTimeKind.Utc);
<#+ }
else
{#>
<#=code.FieldName(primitiveProperty)#> = DateTime.SpecifyKind(<#=code.FieldName(primitiveProperty)#>, DateTimeKind.Utc);
<#+
}
#>
}
else
{
<#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>);
}
<#+
}
else
{
#>
<#=code.FieldName(primitiveProperty)#> = StructuralObject.SetValidValue(value<#=OptionalNullableParameterForSetValidValue(primitiveProperty, code)#>);
<#+
}
#>
@ rob263は優れた方法を提供しました。
これは、Linq ToSqlの代わりにEntityFrameworkを使用している場合に提供したい追加のヘルプにすぎません。
EntityFrameworkはOnLoadedイベントをサポートしていません。
代わりに、次の操作を実行できます。
public partial class Person
{
protected override void OnPropertyChanged(string property)
{
if (property == "BirthDate")
{
this._BirthDate= DateTime.SpecifyKind(this._BirthDate, DateTimeKind.Utc);
}
base.OnPropertyChanged(property);
}
}
私はこの方法を使用して、その場でDateTimeKindを指定します。
DateTime myDateTime = new DateTime(((DateTime)myUtcValueFromDb).Ticks, DateTimeKind.Utc);