Question

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);
    }
Was it helpful?

Solution

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

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top