我有一个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上的生成器和用户定义类型一起工作?
答案 0 :(得分:1)
NHibernate sources of the TableGenerator
class表明其Configure
方法要求列类型为PrimitiveType
的子类。在你的情况下,情况似乎并非如此,因为你只是实现了IEnhancedUserType
(可能是内部包装,但是不是PrimitiveType
。)
我无法解释其原因,但在我的一个项目中,我面临类似的要求,即在自定义NHibernate类型中实现0 / null魔术并使用自定义id生成器。在我的项目中,自定义“id”类型直接实现IUserType
,id生成器直接实现IIdentifierGenerator
和IConfigurable
,即它不扩展NHibernate的TableGenerator
。也许你应该对你的id生成器做同样的事情,以便解决NHibernate的TableGenerator
的列类型限制。