25

次に、Fluent NHibernate とその自動マッピング機能を使用して、次の単純化された POCO クラスをマッピングします。

public class Foo
{    
public virtual int Id { get; set; }    
public virtual datetime CreatedDateTime { get; set; }    
}

CreatedDateTime フィールドは、デフォルトで SQL DateTime にマップされます。ただし、エンティティが正しく作成されていることを確認するテストを行うと、失敗します。これは、DateTime フィールドの精度が SQL データベース全体で維持されていないためです。この背後にある理由は、MS SQL Server の DateTime は、.000、.003、または .007 の増分に丸めることによってミリ秒の精度しか保持できないためであると考えています ( http://msdn.microsoft.com/en-us/libraryを参照)。 /ms187819.aspx )。このため、NHibernate はストアに保存するときにミリ秒を切り捨てます。これにより、.NET DateTime として正しく永続化されたフィールドがミリ秒を保持しているが、保存後に取得された DateTime がミリ秒を失ったため、2 つが真に等しくないことを確認すると、テストが失敗します。

この問題を克服するために、次のマッピングを Foo オブジェクトに追加しました。

public class FooMap : IAutoMappingOverride<Foo>
{
    public void Override(AutoMapping<Foo> mapping)
    {
        mapping.Map(f => f.CreatedDateTime).CustomType("datetime2");     
    }
}

このマッピングにより、NHibernate は CreatedDateTime を SQL 型の datetime2 に保持し、.NET DateTime が可能な完全な精度を格納できることを理解しています。これはうまく機能し、テストはパスします。

ただし、1 つのパスには別の失敗が伴います: スキーマのエクスポートをチェックする私のテストは、次のエラーで失敗するようになりました:

System.ArgumentException : Dialect does not support DbType.DateTime2
Parameter name: typecode

次のスタック トレースを使用:

at NHibernate.Dialect.TypeNames.Get(DbType typecode)
at NHibernate.Dialect.Dialect.GetTypeName(SqlType sqlType)
at NHibernate.Mapping.Column.GetDialectTypeName(Dialect dialect, IMapping mapping)
at NHibernate.Mapping.Table.SqlCreateString(Dialect dialect, IMapping p, String defaultCatalog, String defaultSchema)
at NHibernate.Cfg.Configuration.GenerateSchemaCreationScript(Dialect dialect)
at NHibernate.Tool.hbm2ddl.SchemaExport..ctor(Configuration cfg, IDictionary`2 configProperties)
at NHibernate.Tool.hbm2ddl.SchemaExport..ctor(Configuration cfg)

このコードは、NHibernate.Tool.hbm2ddl.SchemaExport オブジェクトを使用して Execute メソッドを呼び出します。

Fluent v1 と NHibernate v2.1 を使用しています。

また、タイムスタンプへのマッピングを試みDateTimeましたが、挿入が次のように失敗するため、マッピングを機能させることさえできませんでした。

タイムスタンプ列に明示的な値を挿入することはできません。列リストで使用INSERTしてタイムスタンプ列を除外するか、タイムスタンプ列に a を挿入しDEFAULTます。

誰もがSchemeExportをプロパティdatetime2で動作させる方法またはタイムスタンプマッピングを取得する方法を知っていdatetimeますか?

4

5 に答える 5

33

実際、NHibernateリファレンスは、DateTimenhibernateタイプが.NETDateTimeを第2レベルで切り捨てられたSQL日時として格納すると述べています(ミリ秒の粒度なし)

そのため、.NETを切り捨てなしでSQLとして格納するTimestampNHibernateタイプ(type="Timestamp"マッピング内)を提供します。ここで、SQLデータ型は不要であり、1つのテーブルに複数の列がある場合は実際に破損することに注意してください。したがって、NHibernateマッピングの属性と属性を区別することが重要です。DateTimedatetimetimestamptimestampsql-typetype

さらに、フィルターを使用している場合は、フィルター定義にも同じ規則が適用されることに注意してください。パラメーターを指定するDateTimeと、パラメーターの値はミリ秒なしで切り捨てられます。

5.2.2章をご覧ください。基本的な値型表5.3System.ValueTypeマッピング型

于 2010-02-23T11:56:43.770 に答える
6

実際に日付のナノ秒部分を保持しようとしている人は、Nhibernate DateTime2 タイプと同様に SQL 列タイプとして DateTime2 を使用する必要があります。

これを設定するための私の規則は次のとおりです(流暢を使用)

public class DateTimeConvention : IPropertyConvention, IPropertyConventionAcceptance
{

    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Type == typeof(DateTime) || x.Type == typeof(DateTime?));
    }
    public void Apply(IPropertyInstance instance)
    {
        instance.CustomSqlType("DateTime2"); //specify that the sql column is DateTime2
        instance.CustomType("DateTime2"); //set the nhib type as well
    }
}

コンベンションを有効にするには:

 var v = Fluently.Configure()
         .Database(MsSqlConfiguration.MsSql2008
         .ConnectionString(d => d.FromConnectionStringWithKey("connstring"))
         .ShowSql())
         .Mappings(m => m.FluentMappings.AddFromAssemblyOf<IRepository>()
         .Conventions.AddFromAssemblyOf<IRepository>()) //this adds your convention
         .BuildSessionFactory();

これを使用すると、日時を保存するときにナノ秒を維持できます。

于 2015-06-10T10:00:55.410 に答える
1

ビジネス クラスの CreatedDate 監査フィールドで同じ問題に遭遇しました。ユーティリティメソッドの値を使用して時間を設定することで回避しました。お役に立てれば。

     /// <summary>
    /// Return a DateTime with millisecond resolution to be used as the timestamp. This is needed so that DateTime of an existing instance
    /// will equal one that has been persisted and returned from the database. Without this, the times differ due to different resolutions.
    /// </summary>
    /// <returns></returns>
    private DateTime GetTime()
    {
        var now = DateTime.Now;
        var ts = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond, DateTimeKind.Local);
        return ts;
    }
于 2010-01-09T14:40:29.533 に答える
0

以下を使用して楽観的ロックを解決することができました:(datetime2を使用)。

ここから名前 (およびデータ型名の大文字と小文字) を使用したことに注意してください: http://msdn.microsoft.com/en-us/library/system.data.dbtype.aspx 「DateTime2」は私のマッピング コード ( CustomType の下) であり、Sql Server の data-type-case ("datetime2") ではありません。それが違いを生むかどうかはわかりませんが、指摘したかったのです。

流暢なマッピング:

public class DogBreedMap : ClassMap<DogBreed>
{
    public DogBreedMap()
    {
        Id(x => x.DogBreedUUID).GeneratedBy.GuidComb();
        OptimisticLock.Version();
        Version(x => x.Version)
           .Column("MyTimestamp").CustomType("DateTime2");
    }
}




public partial class DogBreed
{

    public DogBreed()
    {
        CommonConstructor();
    }

    private void CommonConstructor()
    {
        this.Version = DateTime.MinValue; /*I don't think this is necessary*/
    }

    public virtual Guid? DogBreedUUID { get; set; }

    public virtual DateTime Version { get; set; }
}

Sql Server 列は次の場所に作成されます。

[MyTimestamp] [datetime2](7) NOT NULL

そして、私の基本的なテストは機能し、(正しく)このような例外を受け取ります(他の誰かが行を更新した場合)

行が別のトランザクションによって更新または削除されました (または、保存されていない値のマッピングが正しくありませんでした): [DogBreed#abcabc1d-abc4-abc9-abcb-abca01140a27]

at NHibernate.Persister.Entity.AbstractEntityPersister.Check(Int32 rows, Object id, Int32 tableNumber, IExpectation expectation, IDbCommand statement)

NHibernate で NHibernate.Persister.Entity.AbstractEntityPersister.Update (オブジェクト id、オブジェクト [] フィールド、オブジェクト [] oldFields、オブジェクト rowId、ブール [] includeProperty、Int32 j、オブジェクト oldVersion、オブジェクト obj、SqlCommandInfo sql、ISessionImplementor セッション)。 Persister.Entity.AbstractEntityPersister.UpdateOrInsert (オブジェクト ID、オブジェクト [] フィールド、オブジェクト [] oldFields、オブジェクト rowId、ブール [] includeProperty、Int32 j、オブジェクト oldVersion、オブジェクト obj、SqlCommandInfo sql、ISessionImplementor セッション) at NHibernate.Persister.Entity .AbstractEntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session) at NHibernate.Action.EntityUpdateAction.Execute() at NHibernate .Engine.ActionQueue。NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource セッション) で NHibernate.Event.Default.DefaultFlushEventListener. OnFlush(FlushEvent イベント) at NHibernate.Impl.SessionImpl.Flush() at NHibernate.Transaction.AdoTransaction.Commit()

于 2010-11-26T21:52:04.283 に答える
0

私のドメインでは、SQL Server の日時からミリ秒を失うことは許容されます。したがって、この静的ヘルパー (nunit 実装) を使用して、持続性テスターに​​許容範囲を許可します。

public static class AssertDateTime
{
    /// <summary>
    /// Checks that the DateTimes are no more than second apart
    /// </summary>
    /// <param name="Expected"></param>
    /// <param name="Actual"></param>
    public static void AreWithinOneSecondOfEachOther(DateTime Expected, DateTime Actual)
    {
        var timespanBetween = Actual.Subtract(Expected);

        if (timespanBetween > TimeSpan.FromSeconds(1))
            Assert.Fail(string.Format("The times were more than a second appart. They were out by {0}. Expected {1}, Actual {2}.", timespanBetween, Expected, Actual));
    }
}
于 2010-02-23T11:25:33.257 に答える