1

我有一个使用遗留数据库 (DB2) 的 NHibernate 实现,我被要求修改它,我遇到了一个问题,即让 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 ...


旧数据库的表中的列是请求表中的外键。包含这些外键的列不可为空,因此当引用对象中的请求对象属性为空时,自定义 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();
    }
}


我被要求修改映射,以便我们可以创建新的请求对象并使用生成器生成 ID。我创建了生成器类并修改了请求对象映射以将生成器用于 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 上的生成器和用户定义类型一起工作?

4

1 回答 1

1

该类NHibernate 来源TableGenerator表明,它的Configure方法期望列类型是PrimitiveType. 在您的情况下,情况似乎并非如此,因为您只是实现IEnhancedUserType(可能会在内部包装,但不是由 a 包装PrimitiveType)。

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

于 2013-03-10T17:52:11.510 回答