Persisting a Castle DynamicProxy that's not associated with a NH Session

我的梦境 提交于 2019-12-13 04:48:21

问题


My session uses an NHInterceptor to add INotifyPropertyChanged support to models.

// 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);
    }
}

The ProxyFactory uses Castle to create proxies that add change notification functionality. That means all my objects come from the DB as Castle Proxies, which are AFAIK transparent.

Whenever I pass one of those NH Generated MVVM-proxies into Session.Save(), all's fine.

Now, as data driven Applications go, I also need to create new instances and save them. I can create instances of the model type and save them via the session all right. Creating a MVVM proxy instance (using Ninject to ensure that the same SessionFactory and ProxyFactory instances are used all over) and throwing this into Session.Save() results in the following:


"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: 

Any ideas what goes wrong here (or what I might have forgotten)?

EDIT : Now got it working, but why do I have to add recognition logic (see code below) to the interceptor for instances created outside, while instances created inside can be persisted as the are?

    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);
    }

回答1:


Implementing the GetEntityName method did the trick.

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(Intercepting Entity Creation) demonstrating the use of dynamic proxies to implement (WPF) change notification. As seen there you'll have to use the same proxy generator in NHibernate and outside to implement the recognition of the proxies for NHibernate (see class DataBindingIntercepter method GetEntityName) .

why do I have to add recognition logic (see code below) to the interceptor for instances created outside, while instances created inside can be persisted as the are?

Methods on ISession only add instances to dictionary to search for dirty ones on Flush. Since all instances loaded are automaticly part of this dictionary the session immediatly "knows" the entity and does nothing in SaveOrUpdate. For other instances coming from outside it first has to get the appropriate mapping (using the entitiyname which defaults to the class fullname) to know which properties form the primary key.



来源:https://stackoverflow.com/questions/13160453/persisting-a-castle-dynamicproxy-thats-not-associated-with-a-nh-session

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!