Поддержка SQL 2008 HierarchyID в NHibernate - PullRequest
21 голосов
/ 30 июля 2010

Обыскивал различные списки NHibernate и не нашел окончательного ответа. Диалект SQL2008 не поддерживает тип данных HierarchyID - только новые типы даты и времени.

У кого-нибудь есть хорошая реализация или эффективный обходной путь? Я действительно хотел бы использовать HierarchyID в моем новом приложении. Поддержка этого интересного и мощного типа данных крайне отсутствует в собственных инструментах MS, поэтому я не удивлен, что NHibernate не имеет поддержки.

Есть некоторые подходов , которые я еще не изучил. Хотите знать, есть ли у кого-то опыт в том, что работает, что является более производительным и т. Д. '

Полное раскрытие: я работаю с Castle ActiveRecord, но это похоже на проблему NHibernate.

Ответы [ 2 ]

10 голосов
/ 18 июня 2012

Я дал ответ Иглам тестовый прогон.Это очень хороший ответ, но для его функционирования необходимо внести некоторые изменения (по крайней мере, в .NET 4).Вот что я придумал для своего проекта:

Обновление: следующий код можно скачать на GitHub и там он будет обновлен. NHiberntate.HierarchyId.UserType

SqlHierarchyId IUserType

namespace NHibernate.UserTypes
{
    using SqlTypes;
    using System;
    using System.Data;
    using System.Data.SqlTypes;
    using Microsoft.SqlServer.Types;

    public class HierarchyId : IUserType
    {
        #region Properties

        public SqlType[] SqlTypes
        {
            get { return new[] { NHibernateUtil.String.SqlType }; }
        }

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

        public bool IsMutable
        {
            get { return true; }
        }

        #endregion Properties

        #region Methods

        new public bool Equals(object x, object y)
        {
            if (ReferenceEquals(x, y)) return true;
            if (x == null || y == null) return false;

            return x.Equals(y);
        }

        public int GetHashCode(object x)
        {
            return x.GetHashCode();
        }

        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            object prop1 = NHibernateUtil.String.NullSafeGet(rs, names[0]);

            if (prop1 == null) return null;

            return SqlHierarchyId.Parse(new SqlString(prop1.ToString()));
        }

        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            if (value == null)
                ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;

            else if (value is SqlHierarchyId)
                ((IDataParameter)cmd.Parameters[index]).Value = ((SqlHierarchyId)value).ToString();
        }

        public object DeepCopy(object value)
        {
            if (value == null) return null;

            return SqlHierarchyId.Parse(((SqlHierarchyId)value).ToString());
        }

        public object Replace(object original, object target, object owner)
        {
            return DeepCopy(original);
        }

        public object Assemble(object cached, object owner)
        {
            return DeepCopy(cached);
        }

        public object Disassemble(object value)
        {
            return DeepCopy(value);
        }

        #endregion Methods
    }
}

Отображение

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DataLayer" namespace="NHibernate.Map">
    <class name="NHibernate.Map.OrganizationUnit, DataLayer" table="`orgunit`">

        <property name="HierarchyId" column="`ou_hid`" type="NHibernate.UserTypes.HierarchyId, DataLayer" />
        ...

    </class>
</hibernate-mapping>

Объект с HierarchyId

namespace NHibernate.Map
{
    using Microsoft.SqlServer.Types;

    public class OrganizationUnit
    {
        #region Fields

        private SqlHierarchyId _hierarchyId;
        ...

        #endregion Fields

        #region Properties

        public virtual SqlHierarchyId HierarchyId
        {
            get { return _hierarchyId; }
            set { _hierarchyId = value; }
        }
        ...

        #endregion Properties
    }
}
9 голосов
/ 29 мая 2012

Отказ от ответственности: я не эксперт NHibernate, однако мы используем его вместе с Fluent в будущем проекте, который использует SQL Server 2008 R2 и идентификаторы иерархии. Код ниже - это то, что мы используем в настоящее время в нашей среде разработки и не полностью протестировано / уточнено. Я скопировал большую часть кода из другого места (извините, я потерял ссылку!)

Вам необходимо создать пользовательский тип, а затем использовать его в своих сопоставлениях. Приведенное ниже сопоставление свободно, я не знаю, как это сделать с помощью ActiveRecord, но я предполагаю, что это должно быть похоже!

Определяемый пользователем тип

namespace YourNamespace {
    public class SqlHierarchyIdUserType : IUserType {
    public bool Equals(object x, object y) {
        if(ReferenceEquals(x, y))
            return true;

        if(x == null || y == null)
            return false;

        return x.Equals(y);
    }

    public int GetHashCode(object x) {
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner) {
        object prop1 = NHibernateUtil.String.NullSafeGet(rs, names[0]);

        if(prop1 == null)
            return null;

        return SqlHierarchyId.Parse(new SqlString(prop1.ToString()));
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index) {
        if(value == null) {
            ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
        } else {
            if(value is SqlHierarchyId) {
                SqlHierarchyId hId = (SqlHierarchyId)value;
                ((IDataParameter)cmd.Parameters[index]).Value = hId.ToString();
            }
        }
    }

    public object DeepCopy(object value) {
        if(value == null)
            return null;

        var sourceTarget = (SqlHierarchyId)value;
        SqlHierarchyId copy = SqlHierarchyId.Parse(sourceTarget.ToString());

        return copy;

    }

    public object Replace(object original, object target, object owner) {
        return DeepCopy(original);
    }

    public object Assemble(object cached, object owner) {
        return DeepCopy(cached);
    }

    public object Disassemble(object value) {
        return DeepCopy(value);
    }

    public SqlType[] SqlTypes {
        get { return new[] { NHibernateUtil.String.SqlType }; }
    }

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

    public bool IsMutable {
        get { return true; }
    }
}
}

Свободное отображение

Map(e => e.YourSqlHierarchyIdProperty)
    .Column("YourSqlHierarchyIdFieldName")
    .CustomType<SqlHierarchyIdUserType>();

Чтение этого поста:

Замок ActiveRecord: сопоставление IUserType с классом в C #

ActiveRecord использует атрибут [Property] для сопоставления пользовательских типов. Так что для вас это будет выглядеть примерно так:

public class YourDataObject {
    [Property(ColumnType="YourNamespace.SqlHierarchyIdUserType, YourNamespace")
    public virtual SqlHierarchyId YourSqlHierarchyIdProperty;
}

Надеюсь, это поможет!

...