具有自定义UserType的NHibernate ID Generator

时间:2013-01-14 17:36:05

标签: nhibernate nhibernate-mapping

我有一个NHibernate实现与遗留数据库(DB2)一起使用,我被要求修改,我遇到了一个问题,即使用id生成器来处理定义为客户用户类型的id属性

表中的数据映射到类似于以下的类:

// CLASS FILE
public class Request {
    public virtual int Id { get; set; }
    ... other data properties ...
}

//MAPPING FILE
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="LibraryName" namespace="LibraryName.Domain" default-cascade="none">
    <class name="Request" schema="..." table="..." where="...">
        <id name="Id" column="..." type="LibraryName.Domain.DB2UserTypes.NullableIntegerType, LibraryName" />

        ... other data property mappings ...


旧数据库具有表,这些表是作为Request表中的外键的列。包含这些外键的列不可为空,因此当引用对象中的Request对象属性为null时,自定义NullableIntegerType用于将零写入外键列。

// NullableIntegerType Definition
public class NullableIntegerType : IEnhancedUserType
{
    private static readonly SqlType[] SQL_TYPES = { NHibernate.NHibernateUtil.Int32.SqlType };
    public SqlType[] SqlTypes { get { return SQL_TYPES; } }

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

    public object DeepCopy(object value) { return value; }
    public bool IsMutable { get { return false; } }
    public object Assemble(object cached, object owner) { return cached; }
    public object Disassemble(object value) { return value; }
    public object Replace(object original, object target, object owner) { return original; }
    public int GetHashCode(object obj) { return obj.GetHashCode(); }

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

    public object NullSafeGet(IDataReader dr, string[] names, object owner)
    {
        object obj = NHibernate.NHibernateUtil.Int32.NullSafeGet(dr, names[0]);
        if (obj == null)
            return null;
        else
        {
            int result = (int)obj;

            if ((result == 0) || (result == 9999999))
                return null;
            else
            {
                return int.Parse(result.ToString());
            }
        }
    }

    public void NullSafeSet(IDbCommand cmd, object obj, int index)
    {
        if (obj == null)
            NHibernateUtil.Int32.NullSafeSet(cmd, 0, index);
        else
            NHibernateUtil.Int32.NullSafeSet(cmd, obj, index);
    }

    public object FromXMLString(string xml)
    {
        return int.Parse(xml);
    }

    public string ToXMLString(object obj)
    {
        return ((int)obj).ToString();
    }

    public string ObjectToSQLString(object obj)
    {
        return ((int)obj).ToString();
    }
}


我被要求修改映射,以便我们可以创建新的Request对象并使用生成器生成ID。我创建了生成器类并修改了Request对象映射以使用Id的生成器。

// GENERATOR CLASS
public class RequestGenerator : TableGenerator
{
    public override object Generate(NHibernate.Engine.ISessionImplementor session, object obj)
    {
        using (IDbCommand command = session.Connection.CreateCommand())
        {
            command.CommandText = @"...";
            command.CommandType = CommandType.StoredProcedure;

            var parameter = command.CreateParameter();
            parameter.DbType = DbType.Int32;
            parameter.ParameterName = "@generatedId";
            parameter.Value = 0;
            parameter.Direction = ParameterDirection.InputOutput;
            command.Parameters.Add(parameter);

            command.ExecuteNonQuery();

            return Convert.ToInt32(parameter.Value);
        }
    }
}

//MODIFIED MAPPING FILE
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="LibraryName" namespace="LibraryName.Domain" default-cascade="none">
    <class name="Request" schema="..." table="..." where="...">
        <id name="Id" column="..." type="LibraryName.Domain.DB2UserTypes.NullableIntegerType, LibraryName" >
            <generator class="LibraryName.Domain.DB2Generators.RequestGenerator, LibraryName" />
        </id>

        ... other data property mappings ...


当我尝试测试修改后的映射时,在我的代码运行之前我得到一个错误,NHibernate无法实例化ID生成器。

// Sample Code
Request request = new Request() { Id = 0, ... other properties }
session.Save(request);
session.Flush();

// Exception Details and Stack Trace
NHibernate.MappingException was unhandled by user code
Message=could not instantiate id generator: LibraryName.Domain.DB2Generators.RequestGenerator, LibraryName
Source=NHibernate
StackTrace:
    at NHibernate.Id.IdentifierGeneratorFactory.Create(String strategy, IType type, IDictionary`2 parms, Dialect dialect)
    at NHibernate.Mapping.SimpleValue.CreateIdentifierGenerator(Dialect dialect, String defaultCatalog, String defaultSchema, RootClass rootClass)
    at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
    at NHibernate.Cfg.Configuration.BuildSessionFactory()
    at LibraryName.Infrastructure.NH.NHibernateSessionFactory.createSessionFactory() in C:\...\NHibernate\NHibernateSessionFactory.cs:line 23
    at LibraryName.Infrastructure.NH.NHibernateSessionFactory.get_SessionFactory() in C:\...\NHibernate\NHibernateSessionFactory.cs:line 15
    at LibraryName.Infrastructure.NH.NHibernateSessionFactoryProvider.CreateInstance(IContext context) in C:\...\NHibernate\NHibernateSessionFactoryProvider.cs:line 13
    at Ninject.Activation.Provider`1.Create(IContext context)
    at Ninject.Activation.Context.Resolve()
    at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
    at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
    at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
    at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters)
    at LibraryName.Infrastructure.InternalMvcModule.<Load>b__0(IContext c) in C:\...\Ninject\InternalMvcModule.cs:line 16
    at Ninject.Activation.Providers.CallbackProvider`1.CreateInstance(IContext context)
    at Ninject.Activation.Provider`1.Create(IContext context)
    at Ninject.Activation.Context.Resolve()
    at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
    at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
    at Ninject.Planning.Targets.Target`1.GetValue(Type service, IContext parent)
    at Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent)
    at Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target)
    at Ninject.Activation.Providers.StandardProvider.<>c__DisplayClass4.<Create>b__2(ITarget target)
    at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
    at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
    at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
    at Ninject.Activation.Providers.StandardProvider.Create(IContext context)
    at Ninject.Activation.Context.Resolve()
    at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding)
    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
    at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
    at Ninject.Web.Mvc.NinjectDependencyResolver.GetService(Type serviceType)
    at System.Web.Mvc.DefaultControllerFactory.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType)


    InnerException: System.ArgumentException
    Message=type is not a ValueTypeType
    Parameter name: type
    Source=NHibernate
    ParamName=type
    StackTrace:
        at NHibernate.Id.TableGenerator.Configure(IType type, IDictionary`2 parms, Dialect dialect)
        at NHibernate.Id.IdentifierGeneratorFactory.Create(String strategy, IType type, IDictionary`2 parms, Dialect dialect)
        InnerException: 

如果从id映射中删除自定义类型(type =“LibraryName.Domain.DB2UserTypes.NullableIntegerType,LibraryName”),则生成器正常工作,我的对象保存到数据库。如何使Id上的生成器和用户定义类型一起工作?

1 个答案:

答案 0 :(得分:1)

NHibernate sources of the TableGenerator class表明其Configure方法要求列类型为PrimitiveType的子类。在你的情况下,情况似乎并非如此,因为你只是实现了IEnhancedUserType(可能是内部包装,但是不是PrimitiveType。)

我无法解释其原因,但在我的一个项目中,我面临类似的要求,即在自定义NHibernate类型中实现0 / null魔术并使用自定义id生成器。在我的项目中,自定义“id”类型直接实现IUserType,id生成器直接实现IIdentifierGeneratorIConfigurable,即它不扩展NHibernate的TableGenerator。也许你应该对你的id生成器做同样的事情,以便解决NHibernate的TableGenerator的列类型限制。