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 ...


レガシーデータベースには、リクエストのテーブルへの外部キーである列を持つテーブルがあります。これらの外部キーを含む列はnull許容ではないため、参照オブジェクトの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を生成できるように、マッピングを変更するように依頼されました。ジェネレータークラスを作成し、IDにジェネレーターを使用するようにRequestオブジェクトマッピングを変更しました。

// 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(おそらく内部的にラップされますが、そうではない何かによってPrimitiveType)。

この理由を説明することはできませんが、私のプロジェクトの1つで、同様の要件に直面しています。つまり、カスタムNHibernateタイプで0 / nullマジックを実装し、カスタムIDジェネレーターを使用します。私のプロジェクトでは、カスタムの「id」タイプが直接実装IUserTypeし、idジェネレーターが直接実装IIdentifierGeneratorします。IConfigurableつまり、NHibernateのを拡張しませんTableGenerator。たぶん、NHibernateの列タイプの制限を回避するために、idジェネレーターで同じことを行う必要がありますTableGenerator

于 2013-03-10T17:52:11.510 に答える