2012-10-18 35 views
5

Tôi đang cố gắng tạo một kiểu NHibernate IUserType cho kiểu Noda Time LocalTime mà sẽ hợp lý ánh xạ tới loại time trong Sql Server 2008/2012. Tôi có thể nhận được các giá trị tiết kiệm và tải từ cơ sở dữ liệu. Tuy nhiên, tôi không thể viết các truy vấn liên quan đến so sánh của địa phương như lần _session.Query<SchedulingTemplate>().Where(x => x.Start < end && x.End >= start) cung cấp cho các lỗi SqlException (0x80131904): The data types time and datetime are incompatible in the less than operator.Làm cách nào để tạo NHibnerate IUserType cho Thời gian trong Sql Server 2008/2012?

Mã liên quan từ loại của tôi sử dụng là:

public Type ReturnedType 
{ 
    get { return typeof(LocalTime); } 
} 

public override object NullSafeGet(IDataReader rs, string[] names, object owner) 
{ 
    var dbValue = NHibernateUtil.Time.NullSafeGet(rs, names); 
    if(dbValue == null) 
     return null; 

    return LocalDateTime.FromDateTime((DateTime)dbValue).TimeOfDay; 
} 

public override void NullSafeSet(IDbCommand cmd, object value, int index) 
{ 
    if(value == null) 
     NHibernateUtil.Time.NullSafeSet(cmd, null, index); 
    else 
     NHibernateUtil.Time.NullSafeSet(cmd, ((LocalTime)value).LocalDateTime.ToDateTimeUnspecified(), index); 
} 

public override SqlType[] SqlTypes 
{ 
    get { return new[] { SqlTypeFactory.Time }; } 
} 

Vấn đề là mặc dù các mã trên cho thấy cơ sở dữ liệu loại là một thời gian, nó tạo ra các truy vấn sau đây (mỗi Sql Profiler):

exec sp_executesql N'select [...] from [SchedulingTemplate] scheduling0_ where scheduling0_.Start<@p0 and scheduling0_.[End]>[email protected]',N'@p0 datetime,@p1 datetime',@p0='1753-01-01 20:00:00',@p1='1753-01-01 06:00:00'

(lưu ý tôi bỏ qua các lựa chọn danh sách cho ngắn gọn)

Lưu ý rằng loại và giá trị của các tham số đang được xử lý là datetime.

Điều này có vẻ rất giống với hai lỗi NH đã bị đóng https://nhibernate.jira.com/browse/NH-2661https://nhibernate.jira.com/browse/NH-2660.

Tôi đã cố gắng sử dụng NHibernateUtil.TimeAsTimeSpan và điều đó dường như không hoạt động. Nó tạo ra chính xác cùng một truy vấn khiến tôi ngạc nhiên. Tôi nghĩ có lẽ vấn đề được mô tả trong NH-2661 cũng tồn tại đối với các loại người dùng và không được sửa cho điều đó?

Tôi đang sử dụng NHibernate v3.3.1.400 và Noda Time 1.0.0-beta2

+1

cho đến khi điều này được khắc phục, bạn phải triển khai SqlType của riêng mình. Sao chép mã từ TimeType và sửa nó. – Firo

Trả lời

4

Sau @ lời khuyên Firo, tôi làm việc kể từ thời điểm SqlType và đến với điều này:

using NHibernate; 
using NHibernate.Dialect; 
using NHibernate.SqlTypes; 
using NHibernate.Type; 
using NodaTime; 
using NodaTime.Text; 
using System; 
using System.Data; 
using System.Data.SqlClient; 

[Serializable] 
public class LocalTimeType : PrimitiveType, IIdentifierType 
{ 
    private readonly LocalTimePattern _timePattern = LocalTimePattern.CreateWithInvariantCulture("h:mm:ss tt"); 

    public LocalTimeType() : base(SqlTypeFactory.Time) { } 

    public override string Name 
    { 
     get { return "LocalTime"; } 
    } 

    public override object Get(IDataReader rs, int index) 
    { 
     try 
     { 
      if (rs[index] is TimeSpan) //For those dialects where DbType.Time means TimeSpan. 
      { 
       var time = (TimeSpan)rs[index]; 
       return LocalTime.Midnight + Period.FromTicks(time.Ticks); 
      } 

      var dbValue = Convert.ToDateTime(rs[index]); 
      return LocalDateTime.FromDateTime(dbValue).TimeOfDay; 
     } 
     catch (Exception ex) 
     { 
      throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex); 
     } 
    } 

    public override object Get(IDataReader rs, string name) 
    { 
     return Get(rs, rs.GetOrdinal(name)); 
    } 

    public override Type ReturnedClass 
    { 
     get { return typeof(LocalTime); } 
    } 

    public override void Set(IDbCommand st, object value, int index) 
    { 
     var parameter = ((SqlParameter)st.Parameters[index]); 
     parameter.SqlDbType = SqlDbType.Time; // HACK work around bad behavior, M$ says not ideal, but as intended, NH says this is a bug in MS may work around eventually 
     parameter.Value = new TimeSpan(((LocalTime)value).TickOfDay); 
    } 

    public override bool IsEqual(object x, object y) 
    { 
     return Equals(x, y); 
    } 

    public override int GetHashCode(object x, EntityMode entityMode) 
    { 
     return x.GetHashCode(); 
    } 

    public override string ToString(object val) 
    { 
     return _timePattern.Format((LocalTime)val); 
    } 

    public object StringToObject(string xml) 
    { 
     return string.IsNullOrEmpty(xml) ? null : FromStringValue(xml); 
    } 

    public override object FromStringValue(string xml) 
    { 
     return _timePattern.Parse(xml).Value; 
    } 

    public override Type PrimitiveClass 
    { 
     get { return typeof(LocalTime); } 
    } 

    public override object DefaultValue 
    { 
     get { return new LocalTime(); } 
    } 

    public override string ObjectToSQLString(object value, Dialect dialect) 
    { 
     return "'" + _timePattern.Format((LocalTime)value) + "'"; 
    } 
} 

Các mã chủ chốt là trong Set phương pháp mà là nói:

var parameter = ((SqlParameter)st.Parameters[index]); 
parameter.SqlDbType = SqlDbType.Time; 

này là cần thiết bởi vì các nhà cung cấp dữ liệu MS mất thiết lập DbType-DbType.Time có nghĩa là loại cơ bản phải là DateTime. Bạn phải đặt SqlDbType để thời gian hoạt động.

+0

Chuyển đổi từ 'TimeSpan' thành' LocalTime' sẽ sạch hơn nhiều so với nó. Tôi đã nêu vấn đề 148 cho việc này. Chuyển đổi từ 'DateTime' sang' LocalTime' * là * đơn giản hơn hiển thị - bạn chỉ cần đi qua 'LocalDateTime':' LocalTime time = LocalDateTime.FromDateTime (dateTime) .TimeOfDay; ' –

+0

Cảm ơn @JonSkeet Tôi đã thực hiện thay đổi sử dụng 'LocalDateTime.FromDateTime' –

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