NHibernate:无效的转换(检查您的映射是否有 属性 类型不匹配);

NHibernate: Invalid Cast (check your mapping for property type mismatches);

我有一个 class ProductCategoryModel:

public class ProductCategoryModel
{
    public virtual int id { get; set; }
    public virtual string name { get; set; }
    public virtual int parentId { get; set; }
    public virtual Iesi.Collections.Generic.ISet<ProductCategoryModel> subCategory { get; set; }
}

这是我的映射 xml:

    <?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Shop.Domain.Model.ProductCategory" assembly="Shop.Domain">
  <class name="ProductCategoryModel">
    <id name="id" column="id">
      <generator class="native" />
    </id>
    <property name="name" not-null="true" length="50" type="string" column="name"/>
    <many-to-one name="parentId" not-null="true" class="ProductCategoryModel" column="parentId"/>
    <set name="subCategory" inverse="true">
      <key column="parentId"/>
      <one-to-many class="ProductCategoryModel"/>
    </set>
  </class>
</hibernate-mapping>

我的 table 数据库包含:

id: 2, name: "...", parentId: 0
id: 3, name: "...", parentId: 2 
id: 4, name: "...", parentId: 2

现在我想 select 我的 id 为 2 的类别:

session1.Get<ProductCategoryModel>(id);

但是 Nhibernate 抛出异常:

        NHibernate.PropertyAccessException was unhandled
  HResult=-2146232832
  Message=Invalid Cast (check your mapping for property type mismatches); setter of Shop.Domain.Model.ProductCategory.ProductCategoryModel
  Source=NHibernate
  StackTrace:
       w NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object entity, Object[] values)
       w NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValues(Object entity, Object[] values)
       w NHibernate.Persister.Entity.AbstractEntityPersister.SetPropertyValues(Object obj, Object[] values, EntityMode entityMode)
       w NHibernate.Engine.TwoPhaseLoad.InitializeEntity(Object entity, Boolean readOnly, ISessionImplementor session, PreLoadEvent preLoadEvent, PostLoadEvent postLoadEvent)
       w NHibernate.Loader.Loader.InitializeEntitiesAndCollections(IList hydratedObjects, Object resultSetId, ISessionImplementor session, Boolean readOnly)
       w NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer)
       w NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies, IResultTransformer forcedResultTransformer)
       w NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
       w NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister)
       w NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId)
       w NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session)
       w NHibernate.Persister.Entity.AbstractEntityPersister.Load(Object id, Object optionalObject, LockMode lockMode, ISessionImplementor session)
       w NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
       w NHibernate.Event.Default.DefaultLoadEventListener.DoLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
       w NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
       w NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
       w NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType)
       w NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType)
       w NHibernate.Impl.SessionImpl.Get(String entityName, Object id)
       w NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id)
       w NHibernate.Impl.SessionImpl.Get[T](Object id)
       w Shop.Infrastructure.Repositories.ProductCategoryIM.GetCategoryById(Int32 id) w f:\Uczelnia\!Semestr VI\Kurs aplikacji bazodanowych\KubutShop2\KubutShop\Shop.Infrastructure\Repositories\ProductCategoryIM.cs:wiersz 58
       w Shop.Application.Front.FrontService.GetProductCategoryById(Int32 id) w f:\Uczelnia\!Semestr VI\Kurs aplikacji bazodanowych\KubutShop2\KubutShop\Shop.Application\Front\Front.cs:wiersz 139
       w Presentation.ConsoleApp.Program.Main(String[] args) w f:\Uczelnia\!Semestr VI\Kurs aplikacji bazodanowych\KubutShop2\KubutShop\Presentation.ConsoleApp\Program.cs:wiersz 40
       w System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       w System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       w Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       w System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       w System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       w System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       w System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       w System.Threading.ThreadHelper.ThreadStart()
  InnerException: System.InvalidCastException
       HResult=-2147467262
       Message=Nie można rzutować obiektu typu 'NHibernate.Collection.Generic.PersistentGenericSet`1[Shop.Domain.Model.ProductCategory.ProductCategoryModel]' na typ 'Iesi.Collections.Generic.ISet`1[Shop.Domain.Model.ProductCategory.ProductCategoryModel]'.
       Source=Shop.Domain
       StackTrace:
            w (Object , Object[] , SetterCallback )
            w NHibernate.Bytecode.Lightweight.AccessOptimizer.SetPropertyValues(Object target, Object[] values)
            w NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object entity, Object[] values)
       InnerException: 

Message=Nie można rzutować obiektu typu 'NHibernate.Collection.Generic.PersistentGenericSet1[Shop.Domain.Model.ProductCategory.ProductCategoryModel]' na typ Iesi.Collections.Generic.ISet1[Shop.Domain.Model.ProductCategory.ProductCategoryModel]'.

意思是这样的:

Message=Cannot cast 'NHibernate.Collection.Generic.PersistentGenericSet1[Shop.Domain.Model.ProductCategory.ProductCategoryModel]' to type Iesi.Collections.Generic.ISet1[Shop.Domain.Model.ProductCategory.ProductCategoryModel]'

我做错了什么?我读了很多类似的主题,但我仍然不知道错误在哪里。 P.S。 对不起我糟糕的英语 ;)

对于最新的 4.0+ NHibernate 版本,我们应该牢记真正重要的变化,在此处描述,在 Q & A

让我引用一点(来自release notes):

** Known BREAKING CHANGES from NH3.3.3.GA to 4.0.0.GA

NHibernate now targets .Net 4.0. Many uses of set types from Iesi.Collections have now been changed to use corresponding types from the BCL. The API for these types are slightly different.

所以 - 现在我们要使用 System 而不是 iesi 东西,例如:

System.Collections.Generic.ISet<T>

第二件事,非常重要,我们需要创建双向映射,one-to-manymany-to-one。这意味着我们不能

// not iesi
// we need System
using System.Collections.Generic;
using System.Collections.ObjectModel;

...

public class ProductCategoryModel
{
    ...
    // could be used just as navigation property
    public virtual int parentId { get; set; }
    // This is must with inverse="true"
    public virtual ProductCategoryModel Parent { get; set; }
    // the System.Collections.Generic
    public virtual ISet<ProductCategoryModel> SubCategories { get; set; }
    ...

和映射:

...
// readonly
<property  name="parentId" not-null="true" column="parentId" type="string"
                                      insert="false" update="false" />
// inverse end
<many-to-one name="Parent" not-null="true" class="ProductCategoryModel" column="parentId"/>

<set name="SubCategories" inverse="true" batch-size="25">
  <key column="parentId"/>
  <one-to-many class="ProductCategoryModel"/>
</set>
...

正如我们所见,我们可以为 many-to-one (参考)property (valueType or string) 进行映射。其中之一必须是只读的 (insert="false" update="false")

注意:不要忘记,因为我们使用反向,所以我们必须在 C# 中设置关系的两边...