Compatibilidad con SQL HierarchyID de SQL 2008 en NHibernate
sql-server-2008 (2)
He dado una respuesta de Needles a la prueba. Es una muy buena respuesta, pero se necesitan algunos cambios para que funcione (al menos en .NET 4). Esto es lo que se me ocurrió para mi proyecto:
Actualización: el siguiente código se puede descargar en GitHub y se actualizará allí. 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
}
}
Cartografía
<?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>
Objeto con 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
}
}
Busqué varias listas NHibernate y no se me ocurrió una respuesta definitiva. El dialecto SQL2008 no parece tener soporte para el tipo de datos HierarchyID, solo nuevos tipos de fecha y hora.
¿Alguien tiene una buena implementación o una solución efectiva? Realmente me gustaría aprovechar HierarchyID en una nueva aplicación mía. El soporte para este interesante y poderoso tipo de datos es muy escaso en las herramientas de MS así que no estoy sorprendido de que NHibernate no tenga soporte.
Hay algunos enfoques que aún no he investigado. Preguntándose si alguien tiene alguna experiencia en lo que funciona, en lo que es más eficiente, etc.
Descripción completa: estoy trabajando con Castle ActiveRecord, pero esto parece un problema NHibernate.
Descargo de responsabilidad: no soy un experto en NHibernate, sin embargo, lo estamos utilizando con Fluent en un próximo proyecto que utiliza SQL Server 2008 R2 y las identificaciones de jerarquía. El siguiente código es lo que estamos usando actualmente en nuestro entorno de desarrollo y no está totalmente probado / refinado. Copié la mayoría del código de otra parte (¡perdí el enlace!)
Necesita crear un tipo definido por el usuario y luego usarlo en sus asignaciones. La asignación a continuación es Fluent, no sé cómo hacerlo usando ActiveRecord pero supongo que debería ser similar.
Tipo definido por el usuario
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; }
}
}
}
Mapeo fluido
Map(e => e.YourSqlHierarchyIdProperty)
.Column("YourSqlHierarchyIdFieldName")
.CustomType<SqlHierarchyIdUserType>();
Leyendo esta publicación:
Castle ActiveRecord: Asignar a IUserType con Clase en C #
ActiveRecord usa un atributo [Propiedad] para mapear tipos definidos por el usuario. Entonces para ti se vería algo como esto:
public class YourDataObject {
[Property(ColumnType="YourNamespace.SqlHierarchyIdUserType, YourNamespace")
public virtual SqlHierarchyId YourSqlHierarchyIdProperty;
}
¡Espero eso ayude!