Search code examples
c#.netnhibernatefluent-nhibernatefluent-nhibernate-mapping

How to configure Fluent NHibernate to return null instead of proxies for missing objects when using ReferencesAny


As per the title, I have an object which has a ReferencesAny association, but it's possible that the referenced object may have been deleted. If this has occurred, currently the missing object is initialized with a proxy which then throws ObjectNotFound if accessed. I want to be able to have this return null instead, in a similar fashion to setting not-found to "ignore" on other properties. I understand the differences between using Session.Get and Session.Load but this is occurring as a result of a LINQ query.

So, my question is, how can I specify that null should be returned for missing objects for an "any" association?


Solution

  • I am using xml configuration, but I guess that my hint could help you anyway... NHibernate provides for those incomplete mappings an attribute not-found which is by default set to "exception". In this example is reference many-to-one mapping, but it could be used even for one-to-many

    <many-to-one name="MyReferencedObject" not-found="exception" />
    

    but it can be changed to ignore

    <many-to-one name="MyReferencedObject" not-found="ignore" />
    

    In that case the result will be NULL. (But be careful, NHibernate will always execute a SQL select statement, because such a "NULL" value won't be cached)


    EDIT: based on the comment below, I have to say, that my suggestion is not working for <any> mapping. Sorry for that, I should read more carefully... But, please, let me provide you with suggestion, how to fix it.


    We can introduce event listener. More can be found here http://nhibernate.info/doc/nh/en/index.html#objectstate-events

    Solution in a nutshell:

    Introduce the event listener

    public class PostLoadListener 
               : NHibernate.Event.Default.DefaultPostLoadEventListener
    { ... }
    

    and inject it into the "session-factory" configuration.

    <event type="post-load">
        <listener class="MyLib.PostLoadListener, MyLib"/>
    </event>
    

    The trick would be inside of the overriden method OnPostLoad

    public override void OnPostLoad(PostLoadEvent @event)
    {
        base.OnPostLoad(@event);
    
        // the entity with <any> mapping 
        ConvertToNull(@event.Entity as MyAuditEntity); 
    }
    
    protected virtual void ConvertToNull(MyAuditEntity item)
    {
        if (item == null)
        {
            return;
        }
        try
        {
            // access some property to check that reference is not a PROXY
            var id = item.AnyEntity.ID;
        }
        catch
        {
            // replace proxy with null
            item.AnyEntity = null; 
        }
    }
    

    Of course, this is not the answer like not-found="ignore". But this workaround will do that job.