【问题标题】:Persisting a Castle DynamicProxy that's not associated with a NH Session保持与 NH 会话无关的 Castle DynamicProxy
【发布时间】:2012-12-17 23:02:37
【问题描述】:

我的会话使用 NHInterceptor 向模型添加 INotifyPropertyChanged 支持。

// I use the session generated here to fetch Data
public class SessionServiceImpl : ISessionService
{
    [Inject]
    public ISessionFactory SessionFactory { get; set; }

    [Inject]
    public NhChangeNotificationInterceptorImpl ChangeNotificationInterceptor { get; set; }

    public ISession GetSession() // reduced code here
    {
        return SessionFactory.OpenSession(ChangeNotificationInterceptor);
    }
}

// This is the interceptor implementation
public class NhChangeNotificationInterceptorImpl : EmptyInterceptor, IInterceptor
{
    [Inject]
    public ISessionFactory SessionFactory { get; set; }

    [Inject]
    public ViewModelProxyFactory ProxyFactory { get; set; }

    public override object Instantiate(string entityTypeName, EntityMode entityMode, object id)
    {
        Type type = Type.GetType(entityTypeName); 

        if (type == null) { /* Throw Exception*/ }
        bool isViewModel = false;
        while (type != typeof(object))
        {
            Type tempType = type.BaseType;
            if (tempType == typeof(ViewModelBase))
            {
                isViewModel = true;
                break;
            }
        }

        if (entityMode == EntityMode.Poco && isViewModel)
        {
            var instance = ProxyFactory.CreateProxy(type);
            SessionFactory.GetClassMetadata(entityTypeName).SetIdentifier(instance, id, entityMode);
            return instance;
        }
        return base.Instantiate(entityTypeName, entityMode, id);
    }
}

ProxyFactory 使用 Castle 创建添加更改通知功能的代理。这意味着所有我的对象都来自作为 Castle 代理的数据库,它们是 AFAIK 透明的。

每当我将其中一个 NH 生成的 MVVM 代理传递给 Session.Save() 时,一切都很好。

现在,随着数据驱动应用程序的发展,我还需要创建新实例并保存它们。我可以创建模型类型的实例并通过会话保存它们。创建一个 MVVM 代理实例(使用 Ninject 确保全部使用相同的 SessionFactory 和 ProxyFactory 实例)并将其放入 Session.Save() 会导致以下结果:


"NHibernate.MappingException".
  Message=No persister for: Castle.Proxies.FieldDescriptionProxy
  Source=NHibernate
  StackTrace:
       at NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(String entityName)
       at NHibernate.Impl.SessionImpl.GetEntityPersister(String entityName, Object obj)
       at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
       at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
       at NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
       at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
       at NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
       at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
       at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
       at NHibernate.Impl.SessionImpl.Save(Object obj)
       at Interpretation.UI.Service.Impl.Dao.FieldDao.SaveFields(IList`1 fields, ISession session) in C:\...\FieldDao.cs:Zeile 51.
  InnerException: 

你知道这里出了什么问题(或者我可能忘记了什么)吗?

编辑: 现在让它工作了,但是为什么我必须为外部创建的实例添加识别逻辑(见下面的代码)到拦截器中,而在内部创建的实例可以保持原样?

    public override string GetEntityName(object entity)
    {
        Type type = entity.GetType();
        if (type.FullName.StartsWith("Castle.Proxies") &&
            type.FullName.EndsWith("Proxy"))
        {
            return type.BaseType.FullName;
        }
        return base.GetEntityName(entity);
    }

【问题讨论】:

  • NH 使用标记接口 IHibernateProxy 来识别代理。你能检查一下在 NHibernate 之外生成的代理是否也实现了这个接口?
  • 不,Castle 生成的代理没有实现 INHibernateProxy。而且我想我也不想这样——至少,我不打算重写 NH 代理机制,而只是为 MVVM 更改通知添加另一层......

标签: nhibernate interceptor castle castle-dynamicproxy


【解决方案1】:

实现 GetEntityName 方法成功了。

public override string GetEntityName(object entity)
{
    Type type = entity.GetType();
    if (type.FullName.StartsWith("Castle.Proxies") &&
        type.FullName.EndsWith("Proxy"))
    {
        return type.BaseType.FullName;
    }
    return base.GetEntityName(entity);
}

【讨论】:

    【解决方案2】:

    article and sample code(拦截实体创建)演示了使用动态代理来实现 (WPF) 更改通知。如那里所见,您必须在 NHibernate 和外部使用相同的代理生成器来实现对 NHibernate 代理的识别(请参阅类 DataBindingIntercepter 方法 GetEntityName)。

    为什么我必须为外部创建的实例添加识别逻辑(见下面的代码)到拦截器,而内部创建的实例可以按原样保留?

    ISession 上的方法仅将实例添加到字典中以在 Flush 上搜索脏实例。由于加载的所有实例都自动成为该字典的一部分,因此会话立即“知道”实体并且在 SaveOrUpdate 中不执行任何操作。对于来自外部的其他实例,它首先必须获取适当的映射(使用默认为类全名的实体名称)才能知道哪些属性构成主键。

    【讨论】:

    • 我添加了更多代码来描述我的工作。我在 NHibernate 内部之外使用相同的 Proxyfactory。事实上,我现在得到了这个工作,但不明白为什么。请查看我的编辑。
    猜你喜欢
    • 2012-04-10
    • 1970-01-01
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-21
    • 2015-01-16
    • 2013-07-17
    相关资源
    最近更新 更多