5

SqlServer2008/2012のタイプに論理的にマップされるNodaTimeタイプIUserTypeのNHibernateを作成しようとしています。データベースから値の保存と読み込みを取得できます。ただし、エラーが発生するような現地時間の比較を含むクエリを作成することはできませんLocalTimetime_session.Query<SchedulingTemplate>().Where(x => x.Start < end && x.End >= start)SqlException (0x80131904): The data types time and datetime are incompatible in the less than operator.

私のユーザータイプの関連コードは次のとおりです。

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

問題は、データベースタイプが時間であることを示す上記のコードにもかかわらず、次のクエリを生成することです(SQLプロファイラーごと)。

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

(簡潔にするために選択リストを省略したことに注意してください)

パラメータのタイプと値がとして扱われていることに注意してくださいdatetime

これは、 https ://nhibernate.jira.com/browse/NH-2661およびhttps://nhibernate.jira.com/browse/NH-2660でクローズされた2つのNHバグと非常によく似ているようです。

使ってみましたがNHibernateUtil.TimeAsTimeSpan、うまくいかなかったようです。それは私を驚かせたのとまったく同じクエリを生成しました。NH-2661で説明されている問題は、ユーザータイプにも存在し、修正されていないと思いますか?

NHibernatev3.3.1.400とNodaTime1.0.0-beta2を使用しています

4

1 に答える 1

4

@Firoのアドバイスに従って、私はSqlTypeの時から働き、これを思いついた:

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) + "'";
    }
}

キーコードは、次のようSetなメソッドにあります。

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

これが必要なのは、MSデータプロバイダーが、基になるタイプがであることを意味するようにを設定するDbTypeためです。が機能するまでの時間を設定する必要があります。DbType.TimeDateTimeSqlDbType

于 2012-11-09T21:29:19.450 に答える