如何使用NHibernate正确映射无符号数据类型

时间:2014-03-14 13:37:02

标签: c# sql-server nhibernate fluent-nhibernate

我想知道如何在没有大量模糊的情况下将UInt16值映射到相应的数据库类型。

我们正在使用Microsoft SQL Server Compact 3.5,NHibernate和FluentNHibernate进行配置。

直接进场 - 会导致错误

public class Identificator
{
    public Identificator( ushort componentType, ushort componentID ) { ... }

    public virtual ushort ComponentType { get; protected set; }
    public virtual ushort ComponentID { get; protected set; }

    ...
}

public class IdentificatorMapping : ClassMap<Identificator>
{
    public IdentificatorMapping()
    {
        this.Map( x => x.ComponentType );
        this.Map( x => x.ComponentID );

        ...
    }
}

在数据库中实际创建新对象时......

// Create Object
Identificator persistableObjectOK = new Identificator( 0x0001, 0x0001 );

// Persist it
ISession session = GetSession();
session.Save( persistableObjectOK );

...我会得到那个错误,这是正常的,因为SQL Server CE不知道无符号类型

  

FluentNHibernate.Cfg.FluentConfigurationException:创建SessionFactory时使用了无效或不完整的配置。检查PotentialReasons集合,以及InnerException以获取更多详细信息   System.ArgumentException:Dialect不支持DbType.UInt16

使用CustomSqlType - 部分工作

所以,我强迫NHibernate在数据库上取一个4字节长的整数,所以应该覆盖UInt16的数据范围

public class IdentificatorMapping : ClassMap<Identificator>
{
    public IdentificatorMapping()
    {
        this.Map( x => x.ComponentType ).CustomSqlType( "INTEGER" );
        this.Map( x => x.ComponentID ).CustomSqlType( "INTEGER" );

        ...
    }
}

结果是工作......只要我低于32767! 超过这个价值,整个事情就会爆发。

奇怪的是,在应用程序中,它是无符号的2个字节。在数据库中,它被标记为4个字节。

两者都应足以存储多达65536

然而,NHibernate尚未确信。在某个地方,数据库会尝试将UInt16转换为Int16。

System.Convert.ToInt16(UInt16 value)
System.UInt16.System.IConvertible.ToInt16(IFormatProvider provider)
System.Data.SqlServerCe.Accessor.set_Value(Object value)
System.Data.SqlServerCe.SqlCeCommand.FillParameterDataBindings(Boolean verifyValue)
System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)
NHibernate.Id.Insert.AbstractSelectingDelegate.PerformInsert(SqlCommandInfo insertSQL, ISessionImplementor session, IBinder binder)
NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object[] fields, Boolean[] notNull, SqlCommandInfo sql, Object obj, ISessionImplementor session)
NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object[] fields, Object obj, ISessionImplementor session)
NHibernate.Action.EntityIdentityInsertAction.Execute()
NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
NHibernate.Event.Default.AbstractSaveEventListener.PerformSave(Object entity, Object id, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj)
NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
NHibernate.Engine.Cascade.CascadeToOne(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
NHibernate.Event.Default.AbstractSaveEventListener.CascadeBeforeSave(IEventSource source, IEntityPersister persister, Object entity, Object anything)
NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
NHibernate.Event.Default.AbstractSaveEventListener.PerformSave(Object entity, Object id, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
NHibernate.Impl.SessionImpl.Save(Object obj)

具体问题:我怎么能说NHibernate不做这个转换,而是从UIn16转换为Int32呢?

2 个答案:

答案 0 :(得分:0)

SQL Server(包括CE)不支持无符号类型,因此没有要转换的有效类型。例外是byte / tinyint。

您可以编写一个自定义IUserType实现来为ushort和int进行转换,但在代码中使用int会更简单。

另请参阅:Does using small datatypes (for example short instead of int) reduce memory usage?

答案 1 :(得分:0)

与此answer类似,我创建了一个在Int16中存储UInt16的用户类型。 折价是如果我们超过32K,数据库中的表示可能看起来不对(即显示负值)。

using System;
using NHibernate;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;

public class UInt16UserType : IUserType
{
    public Object NullSafeGet( System.Data.IDataReader rs, String[] names, Object owner )
    {
        Int16? i = (Int16?) NHibernateUtil.Int16.NullSafeGet( rs, names[0] );
        return (UInt16?) i;
    }

    public void NullSafeSet( System.Data.IDbCommand cmd, Object value, int index )
    {
        UInt16? u = (UInt16?) value;
        Int16? i = (Int16?) u;
        NHibernateUtil.Int16.NullSafeSet( cmd, i, index );
    }

    public Type ReturnedType
    {
        get
        {
            return typeof(Nullable<UInt16>);
        }
    }

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

    public Object Assemble( Object cached, Object owner )
    {
        return cached;
    }

    public Object DeepCopy( Object value )
    {
        return value;
    }

    public Object Disassemble( Object value )
    {
        return value;
    }

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

    public bool IsMutable
    {
        get
        {
            return false;
        }
    }

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

    public new bool Equals( Object x, Object y )
    {
        if (Object.ReferenceEquals( x, y ))
        {
            return true;
        }

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

        if (!(x is UInt16 && y is UInt16))
        {
            return false;
        }

        UInt16 a = (UInt16) x;
        UInt16 b = (UInt16) y;

        bool result = a == b;

        return result;
    }
}