2010-01-09 23 views
22

tôi sau đó sử dụng thành thạo NHibernate và tính năng automapping của nó để lập bản đồ đơn giản lớp POCO sau:DateTime chính xác trong NHibernate và hỗ trợ cho DateTime2 trong NHibernate SchemeExport

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

Trường CreatedDateTime sẽ ánh xạ một DateTime SQL bởi mặc định. Tuy nhiên nếu tôi làm một bài kiểm tra để kiểm tra xem thực thể đó có được tạo đúng cách không. Điều này là do độ chính xác của trường DateTime không được duy trì thông qua cơ sở dữ liệu SQL. Tôi undersatnd lý do đằng sau này là một MS SQL Server DateTime chỉ có thể giữ độ chính xác milisecond bằng cách làm tròn đến gia số của .000, .003, hoặc .007 (xem http://msdn.microsoft.com/en-us/library/ms187819.aspx). Vì lý do này NHibernate cắt ngắn miliseconds khi lưu vào cửa hàng. Điều này dẫn đến kiểm tra của tôi thất bại khi kiểm tra các trường tồn tại chính xác như .NET DateTime của tôi giữ miliseconds của nó nhưng DateTime retrived sau khi lưu đã mất miliseconds của nó và do đó hai không thực sự bằng nhau.

Để khắc phục vấn đề này tôi đã thêm các bản đồ sau đây để các đối tượng Foo:

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

Tôi hiểu rằng bản đồ này làm cho NHibernate tồn các CreatedDateTime đến một loại SQL của datetime2, có thể lưu trữ chính xác đầy đủ mà một NET DateTime có thể. Điều này làm việc một điều trị và thử nghiệm bây giờ vượt qua.

Tuy nhiên với một đường chuyền đến một thất bại: thử nghiệm của tôi để kiểm tra việc xuất khẩu schema hiện không thành công với các lỗi sau:

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

với một chồng dấu vết của:

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) 

Mã này sử dụng Đối tượng NHibernate.Tool.hbm2ddl.SchemaExport để gọi phương thức Execute.

Tôi đang sử dụng Fluent v1 và NHibernate v2.1.

Tôi cũng đã cố gắng lập bản đồ của tôi DateTime để một dấu thời gian nhưng có thể thậm chí không được ánh xạ làm việc như chèn thất bại trong đó nêu:

Không thể chèn một giá trị rõ ràng vào một cột timestamp. Sử dụng INSERT với danh sách cột để loại trừ cột dấu thời gian hoặc chèn DEFAULT vào cột dấu thời gian.

Có ai biết cách để SchemeExport làm việc với datetime2 HOẶC làm cách nào để có được ánh xạ dấu thời gian hoạt động cho thuộc tính datetime?

+1

NHibernate không cắt ngắn mili giây vì máy chủ sql. Kiểm tra câu trả lời của tôi – Jaguar

+1

Chỉ định thời gian khách hàng hoạt động cho tôi cũng như khi tôi sử dụng SQL Server. Cảm ơn vì điều đó! Nó không thành công với PostgreSQL, mặc dù, với "System.InvalidCastException: Không thể cast DateTime2 vào bất kỳ DbType hợp lệ" – Manfred

Trả lời

1

Tôi đã gặp phải vấn đề tương tự với trường kiểm định CreatedDate trên các lớp học kinh doanh của mình. Tôi đã làm việc xung quanh nó bằng cách thiết lập thời gian bằng cách sử dụng giá trị từ một phương thức tiện ích. Hi vọng điêu nay co ich.

 /// <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; 
    } 
+0

Cảm ơn Jamie, do đó, bạn chỉ cần sử dụng phương pháp này trong thử nghiệm của bạn? Tôi đã thử phương pháp của bạn, tuy nhiên vì nó vẫn sử dụng mili giây, tôi có cùng lỗi. Bạn có nghĩa vụ phải sử dụng 0 thay vì bây giờ.Millisecond? – j3ffb

+0

Câu hỏi hay, tôi đã suy nghĩ điều tương tự khi tôi đăng nó. Tôi cần phải viết một số bài kiểm tra nhiều hơn để kiểm tra, nhưng thay thế ngay bây giờ.Millisecond với 0 nên làm các trick. –

+0

Vâng, chúc mừng. Nếu mọi thứ khác thất bại tôi có thể quay trở lại sql datetimes và sử dụng giải pháp này vì tôi có thể không thực sự cần mức độ chính xác này. Tuy nhiên nó vẫn sẽ là tuyệt vời để xem làm thế nào bạn có thể bảo tồn chính xác chính xác cho refernce tương lai. – j3ffb

0

Trong miền của tôi, có thể chấp nhận mất mili giây từ thời gian biểu trong SQL Server. Vì vậy, tôi cho phép dung sai trong xét nghiệm kiên trì của tôi sử dụng helper tĩnh này (thực hiện 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)); 
    } 
} 
31

Trên thực tế các bang tham khảo NHibernate rằng DateTime nhibernate loại sẽ lưu trữ.NET DateTime như một datetime SQL cắt ngắn ở cấp thứ hai (không granularity millisecond)

Như vậy nó cung cấp các Timestamp NHibernate loại (type="Timestamp" trong bản đồ) mà sẽ lưu trữ một NET DateTime như một SQL datetime mà không cần cắt ngắn. Lưu ý ở đây rằng một kiểu dữ liệu SQL timestampkhông phải là cần thiết và sẽ phá vỡ ngắt nếu bạn có nhiều hơn một cột timestamp trong một bảng. Do đó, điều quan trọng là phân biệt giữa các thuộc tính sql-typetype trong ánh xạ NHibernate. Ngoài ra, lưu ý rằng nếu bạn đang làm việc với bộ lọc, quy tắc tương tự áp dụng ở định nghĩa bộ lọc: Nếu bạn chỉ định tham số DateTime, giá trị của thông số sẽ bị cắt bớt mà không có mili giây.

Khám phá chapter 5.2.2. Basic value types, Table 5.3 System.ValueType Mapping Types.

+0

bất kỳ ý tưởng làm thế nào để nói với NHibernate không cắt ngắn đó? –

+0

cắt ngắn cái nào? – Jaguar

+0

@asbjornu no, nó không phải là hạn chế của NHibernate. Để làm rõ: Loại "Dấu thời gian" SQL chỉ được phép một lần cho mỗi bảng. Loại "Dấu thời gian" NHibernate được cho phép bao nhiêu tùy thích. Nhận sự thật của bạn ngay trước khi downvoting – Jaguar

0

Tôi đã có thể lấy khóa lạc quan của mình bằng cách sử dụng bên dưới: (sử dụng datetime2).

Lưu ý, tôi đã sử dụng tên (và trường hợp của các kiểu dữ liệu tên) từ đây: http://msdn.microsoft.com/en-us/library/system.data.dbtype.aspx "DateTime2" là trong mã lập bản đồ của tôi (dưới CustomType) và không phải là kiểu dữ liệu hợp cụ thể Sql Server ("datetime2 "). Tôi không chắc liệu điều đó có tạo ra sự khác biệt hay không nhưng tôi muốn chỉ ra điều đó.

Mapping thạo:

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

Cột Sql Server được tạo ra tại địa chỉ:

[MyTimestamp] [datetime2](7) NOT NULL 

Và xét nghiệm cơ bản của tôi làm việc và tôi (chính xác) nhận được một ngoại lệ như thế này (khi ai đó đã updted hàng)

Hàng đã được cập nhật hoặc xóa bởi một giao dịch khác (hoặc bản đồ giá trị chưa được lưu không chính xác): [DogBreed # abcabc1d-abc4-abc9-abcb-abca01140a27]

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

tại NHibernate.Persister.Entity.AbstractEntityPersister.Update (Object id, Object [] lĩnh vực, Object [] OLDFIELDS, Object ROWID, Boolean [] includeProperty, Int32 j, Object oldversion, Object obj, SqlCommandInfo sql , Phiên ISessionImplementor) tại NHibernate.Persister.Entity.AbstractEntityPersister.UpdateOrInsert (Trường đối tượng, đối tượng [], Object [] oldFields, đối tượng rowId, Boolean [] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor phiên) tại NHibernate.Persister.Entity.AbstractEntityPersister.Update (Object id, Object [] trường, Int32 [] dirtyFields, Boolean hasDirtyCollection, Object [] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session) tại NHibernate.Action.EntityUpdateAction.Execute() tại NHibernate.Engine.ActionQueue.Execute (IExecutable thực thi) tại NHibernate.Engine.ActionQueue.ExecuteActions (danh sách IList) tại NHibernate.Engine.ActionQueue.ExecuteActions() tại NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions (phiên IEventSource) tại NHibernate.Event.Default.DefaultFlushEventListener.OnFlush (sự kiện FlushEvent) tại NHibernate.Impl.SessionImpl.Flush() tại NHibernate.Transaction.AdoTransaction.Cam kết()

5

Đối với bất kỳ ai muốn thực sự giữ phần nano giây của ngày, bạn sẽ phải sử dụng DateTime2 làm loại cột sql cũng như kiểu Nhibernate DateTime2.

Dưới đây là ước của tôi cho thiết lập này (sử dụng thành thạo)

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 
    } 
} 

Và để kích hoạt các quy ước:

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

Sử dụng này, bạn sẽ nhận được để giữ nano giây khi lưu trữ datetimes của bạn.

Các vấn đề liên quan