Поддержка SQL 2008 HierarchyID в NHibernate
Искал различные списки NHibernate и не нашел окончательного ответа. Диалект SQL2008, похоже, не поддерживает тип данных HierarchyID - только новые типы даты и времени.
Есть ли у кого-нибудь хорошая реализация или эффективный обходной путь? Я бы очень хотел использовать HierarchyID в своем новом приложении. Поддержка этого интересного и мощного типа данных крайне отсутствует в собственных инструментах MS, поэтому я не удивлен, что NHibernate не имеет поддержка.
Есть Некоторые подходы там, в которые я еще не вникал. Интересно, есть ли у кого-нибудь опыт в том, что работает, что более эффективно и т. д.'
Полное раскрытие: я работаю с Castle ActiveRecord, но это похоже на проблему NHibernate.
2 ответа:
Отказ от ответственности: я не являюсь экспертом 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>();
Читая этот пост:
Castle ActiveRecord: сопоставление с классом IUserType wihtin в C#
ActiveRecord использует атрибут [Property] для отображения пользовательских типов. Так что для вас это будет выглядеть примерно так:
public class YourDataObject { [Property(ColumnType="YourNamespace.SqlHierarchyIdUserType, YourNamespace") public virtual SqlHierarchyId YourSqlHierarchyIdProperty; }
Надеюсь, это поможет!
Я дал тестовый прогон ответа Нидлза. Это очень хороший ответ, но есть некоторые изменения, необходимые для его функционирования (по крайней мере, в .NET 4). Вот что я придумал для своего проекта:
Update: следующий код можно скачать на 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 } }