如何在NHibernate中正确映射无符号数据类型

本文关键字:映射 无符号 数据类型 NHibernate | 更新日期: 2023-09-27 17:50:12

我想知道如何将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 );

…我将得到这个错误,这是OK的,因为SQL Server CE不知道unsigned类型

FluentNHibernate.Cfg。FluentConfigurationException:创建SessionFactory时使用了无效或不完整的配置。查看PotentialReasons集合和InnerException获取更多细节。
系统。ArgumentException: Dialect不支持DbType。UInt16

Using CustomSqlType - works part

所以,我去强迫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字节。

两者都应该足以存储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不做这个转换,而是从un16转换到Int32 ?

如何在NHibernate中正确映射无符号数据类型

与这个答案类似,我创建了一个将UInt16存储在Int16中的用户类型。代价是,如果我们超过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;
    }
}

SQL Server(包括CE)不支持unsigned类型,所以没有有效的类型可以转换。例外是byte/tinyint。

你可以编写一个自定义的IUserType实现来完成ushort和int的转换,但是在你的代码中使用int会简单得多。

参见:使用小数据类型(例如short而不是int)是否会减少内存使用?