Order of execution of IXXXEventListener

86 views
Skip to first unread message

Alexander Zaytsev

unread,
May 4, 2017, 9:38:07 AM5/4/17
to nhusers, nhibernate-development
Hi,

I want to know if anyone here expects EventListener of the same type to run in a particular order.

Does anyone have ever implemented and registered two event listeners of the same type to the single ISessionFactory?

Best Regards,
Alexander

Boštjan Markežič

unread,
May 5, 2017, 8:46:06 AM5/5/17
to nhibernate-development, nhu...@googlegroups.com
Hi Alexander,

Yes, I am using two IPreUpdate event listeners in a single ISessionFactory. One is updating the entity audit properties and the other is executing the validation for the entity.
The order somehow matters in this case as I want the validation event listener to be executed as last after all properties are populated.
If you intend to drop support for registering multiple listeners of the same type it won't be a problem for me as I can still create my own mechanism to spread the event across the system if needed.
That is just my opinion.

Best Regards,
Boštjan
 


Dne četrtek, 04. maj 2017 15.38.07 UTC+2 je oseba Alexander Zaytsev napisala:

frederic...@free.fr

unread,
May 5, 2017, 9:01:02 AM5/5/17
to nhibernate-development, nhu...@googlegroups.com
Side note: event listeners have been refactored on Hibernate side. I have seen that while doing NH-4003 which ports Hibernate current way of building sessions. I have not checked if those currently not ported Hibernate changes were impacting their behavior.

Alexander Zaytsev

unread,
May 5, 2017, 8:28:09 PM5/5/17
to nhibernate-development, nhusers
Thanks, Boštjan

Funny :) I'm asking in a context of AsyncGenerator. So, it's how the listeners are currently invoked (an example based on OnPersistEventListener):

private void FirePersistAsync(IDictionary copiedAlready, PersistEvent @event)
{
    using (new SessionIdLoggingContext(SessionId))
    {
        CheckAndUpdateSessionStatus();
        var persistEventListener = listeners.PersistEventListeners;
        for (int i = 0; i < persistEventListener.Length; i++)
        {
            persistEventListener[i].OnPersistAsync(@event, copiedAlready);
        }
    }
}

And this is how the AsyncGenerator converts it to async:

private async Task FirePersistAsync(IDictionary copiedAlready, PersistEvent @event)
{
    using (new SessionIdLoggingContext(SessionId))
    {
        CheckAndUpdateSessionStatus();
        var persistEventListener = listeners.PersistEventListeners;
        for (int i = 0; i < persistEventListener.Length; i++)
        {
            await (persistEventListener[i].OnPersistAsync(@event, copiedAlready)).ConfigureAwait(false);
        }
    }
}

Which is nonoptimal. The code here asynchronously invokes the listeners sequentially one by one. 
I would like to invoke them simultaneously instead by Task.WhenAll:

private async Task FirePersistAsync(IDictionary copiedAlready, PersistEvent @event)
{
    using (new SessionIdLoggingContext(SessionId))
    {
        CheckAndUpdateSessionStatus();
        
        var listeners = listeners.PersistEventListeners
            .Select(l => l.OnPersistAsync(@event, copiedAlready).ConfigureAwait(false));

        await Task.WhenAll(listeners).ConfigureAwait(false);
    }
}

So, if anyone is expecting some interdependencies or sequential order of invocations between the listeners we would not be able to do so.

Best Regards,
Alexander

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Boštjan Markežič

unread,
May 6, 2017, 4:00:14 AM5/6/17
to nhibernate-development, nhu...@googlegroups.com
Here we need to consider one important thing, ISession is not thread safe so using Task.WhenAll could be very dangerous as the ISession instance is passed to the event listeners. 
In my opinion the version with a for loop is the way to go as we are mimic the exact behavior as doing it synchronously, the only difference is that the next event listener may be executed in a different thread because of ConfigureAwait(false). If the ISession relies on the thread context then we should remove also the ConfigureAwait(false) call.
IMHO almost nobody is using more than one event listener of the same type, even if they do, the number will be very small so using the Task.WhenAll is not necessary here. 

Best Regards,
Boštjan

Dne sobota, 06. maj 2017 02.28.09 UTC+2 je oseba Alexander Zaytsev napisala:

Fabio Maulo

unread,
May 6, 2017, 9:55:15 AM5/6/17
to nhibernate-development, nhu...@googlegroups.com
Perhaps its time to think : why the ISession is not thread safe ?
May be you can discover that the work to do to make it thread-safe is not so hard.
Which part of the session-state is not thread safe ?
The weakest class, in term of thread safety, is the StatefulPersistenceContext; make it thread safe and you have most of the problem solved ;)

Great work team, great work.

Abrazos a todos.

frederic...@free.fr

unread,
May 9, 2017, 10:44:40 AM5/9/17
to nhibernate-development, nhu...@googlegroups.com
The session may holds a DbConnection which is not thread safe. Any operation implying the connection will not be thread safe. So I do not think we are not that far from having a thread-safe session.

Michael Powell

unread,
May 9, 2017, 2:52:38 PM5/9/17
to nhibernate-...@googlegroups.com
On Tue, May 9, 2017 at 10:44 AM, <frederic...@free.fr> wrote:
> The session may holds a DbConnection which is not thread safe. Any operation
> implying the connection will not be thread safe. So I do not think we are
> not that far from having a thread-safe session.

It would seem to me that the best you could do is:

lock (connection)
{
// ... do something ...
}

Or other appropriate, memory barrier, strongest possible lock, monitor, etc.
> email to nhibernate-develo...@googlegroups.com.

Frédéric Delaporte

unread,
May 9, 2017, 3:00:15 PM5/9/17
to nhibernate-development
I am really not sure we should try to go that route. TransactionScopes do not play well across threads (and need to be created with a special argument for supporting async/await pattern).

Le mardi 9 mai 2017 20:52:38 UTC+2, Michael Powell a écrit :
> email to .
Reply all
Reply to author
Forward
0 new messages