I needed to get this to work and after some two days of trying
different avenues, I came up with the following.

Since the problem is I cannot access the session inside an event
listener (and I believe this is specifically limited for the
IPreUpdateEventListener event listener; for the insert and delete, it
does not seem to be a problem), I figured I simply do not access the
session inside the event listener, but rather queue required actions.
I have a helper class named SessionUtil to which I added a method
Queue(Action<ISession> action). In the event listener, I use this as
follows:

if ((bool)GetOld(@event, "IsOpen") != (bool)Get(@event, "IsOpen"))
{
    bool difference = (bool)Get(@event, "IsOpen") ? 1 : -1;
    Parent parent = (Parent)Get(@event, "Parent");

    SessionUtil.QueuePerform(session =>
    {
        session.Lock(parent, LockMode.None);

        parent.OpenChildCount += difference;

        session.SaveOrUpdate(parent);
    });
}

1) I check whether the IsOpen flag of the child record has changed;
2) If so, I determine whether the open child count must be incremented
or decremented;
3) I queue the action to update the parent record.

These queued actions are then executed after the flush phase has been
completed. Once there are no queued actions left after the session
flush has completed, I close the session and commit the transaction.

The SessionUtil class is below.

public static class SessionUtil
{
    [ThreadStatic]
    public static ISession CurrentSession { get; set; }

    [ThreadStatic]
    private static List<Action<ISession>> _queuedActions = null;

    public static SessionFactory SessionFactory { get; private set; }

    public static void BeginSession()
    {
        CurrentSession = SessionFactory.OpenSession();

        CurrentSession.BeginTransaction(IsolationLevel.Serializable);
    }

    public static void EndSession()
    {
        var transaction = CurrentSession.Transaction;

        try
        {
            bool success = false;

            /*
             * This is here to support the event system. When entities
change
             * inside the event system, this can make the session
dirty. Some
             * changes howver cannot be done inside the flush. These
normally
             * involve accessing other objects than the object
actually being
             * inserted, updated or deleted. These actions must then
be
             * scheduled using the QueuePerform.
             */

            int retries = 3;

            while (CurrentSession.IsDirty() || (_queuedActions != null
&& _queuedActions.Count > 0))
            {
                if (--retries < 0)
                    throw new Exception("Maximum number of retries
reached");

                if (_queuedActions != null)
                {
                    var actions = _queuedActions;

                    _queuedActions = null;

                    foreach (var action in actions)
                        action(CurrentSession);
                }

                _queuedActions = new List<Action<ISession>>();

                CurrentSession.Flush();
            }

            _queuedActions = null;

            transaction.Commit();

            success = true;
        }
        finally
        {
            if (!success)
                transaction.Rollback();

            CurrentSession.Dispose();
            CurrentSession = null;

            transaction.Dispose();
        }
    }

    public static void Queue(Action<ISession> action)
    {
        if (_queuedActions == null && CurrentSession == null)
            throw new Exception("QueuePerform can only be called from
inside event listeners");

        if (_queuedActions == null)
            action(CurrentSession);
        else
            _queuedActions.Add(action);
    }
}


On May 14, 7:13 pm, pvginkel <[email protected]> wrote:
> Yes, thank you for your reaction. I'm going to take a look at this
> soon.
>
> On May 13, 7:02 pm, Diego Mijelshon <[email protected]> wrote:
>
>
>
>
>
> > Did you read my email?
> > I just told you how to maintain a calculated field without hacks.
>
> >    Diego
>
> > On Thu, May 13, 2010 at 11:53, pvginkel <[email protected]> wrote:
> > > The reason I would like this to work is because the value I am
> > > calculating is a value that is being used very often. It is used to
> > > display a flag in a grid with many records that update quite often.
>
> > > What I'm wondering is why this is regarded as hacking. I though that
> > > the concept of stored calculated fields was something quite normal.
> > > The only problem I have is that I do not know where to update this
> > > value.
>
> > > Do you have alternatives to the interceptor or the event listener
> > > mechanism? Or is there a way to implement this using the interceptor
> > > or event listener mechanism?
>
> > > On May 13, 4:08 pm, Diego Mijelshon <[email protected]> wrote:
> > > > I think using an interceptor for this is the wrong approach.
> > > > Instead, you can define your property as follows:
>
> > > >   public virtual int ChildCount
> > > >   {
> > > >     get { return Children.Count(c => c.IsOpen); }
> > > >   }
>
> > > > And map it as
>
> > > >   <property name="ChildCount" access="readonly" />
>
> > > > And you're done. No hacking.
> > > > This is exactly how I'm doing it now.
>
> > > >    Diego
>
> > > > On Wed, May 12, 2010 at 15:14, pvginkel <[email protected]> wrote:
> > > > > I am using NHibernate 2.1.2.4000 with LinFu proxy generater and I have
> > > > > the following problem.
>
> > > > > Basically, I have a parent child mapping:
>
> > > > >  <class name="Parent">
> > > > >    <id name="Id">
> > > > >      <generator class="native" />
> > > > >    </id>
> > > > >    <property name="ChildCount" />
> > > > >    <set name="Children" inverse="true">
> > > > >      <key column="ParentId" />
> > > > >      <one-to-many class="Child" />
> > > > >    </set>
> > > > >  </class>
>
> > > > >  <class name="Child">
> > > > >    <id name="Id" column="Id">
> > > > >      <generator class="native" />
> > > > >    </id>
> > > > >    <property name="IsOpen"/>
> > > > >    <many-to-one name="Parent" column="ParentId" />
> > > > >  </class>
>
> > > > > When I a child is added, deleted or the IsOpen flag changes, the
> > > > > ChildCount must be updated. I do this in an interceptor. Creating or
> > > > > deleting the child works perfectly, and the count gets correctly
> > > > > updated. Changing the child, updating the ChildCount in the
> > > > > OnFlushDirty interceptor method however does not work.
>
> > > > > I've tried everything I could think of. In a few of my tries, I got an
> > > > > exception in the transaction.Commit that the object could not be
> > > > > reassociated because it had a dirty collection.
>
> > > > > My current hypothesis is that NHibernate specifically disallows these
> > > > > kinds of updated to e.g. prevent against circular references and never
> > > > > ending cascade update cycles. If this is the case, does anybody have
> > > > > an alternative to implement these kinds of cascade updates for cached
> > > > > field values?
>
> > > > > I'm quite out of ideas and would like some assistance.
>
> > > > > --
> > > > > You received this message because you are subscribed to the Google
> > > Groups
> > > > > "nhusers" group.
> > > > > To post to this group, send email to [email protected].
> > > > > To unsubscribe from this group, send email to
> > > > > [email protected]<nhusers%[email protected]
> > > > >  >
> > > <nhusers%[email protected]<nhusers%252bunsubscr...@googlegroup
> > >  s.com>>
> > > > > .
> > > > > For more options, visit this group at
> > > > >http://groups.google.com/group/nhusers?hl=en.
>
> > > > --
> > > > You received this message because you are subscribed to the Google 
> > > > Groups
> > > "nhusers" group.
> > > > To post to this group, send email to [email protected].
> > > > To unsubscribe from this group, send email to
> > > [email protected]<nhusers%[email protected]
> > >  >
> > > .
> > > > For more options, visit this group athttp://
> > > groups.google.com/group/nhusers?hl=en.
>
> > > --
> > > You received this message because you are subscribed to the Google Groups
> > > "nhusers" group.
> > > To post to this group, send email to [email protected].
> > > To unsubscribe from this group, send email to
> > > [email protected]<nhusers%[email protected]
> > >  >
> > > .
> > > For more options, visit this group at
> > >http://groups.google.com/group/nhusers?hl=en.
>
> > --
> > You received this message because you are subscribed to the Google Groups 
> > "nhusers" group.
> > To post to this group, send email to [email protected].
> > To unsubscribe from this group, send email to 
> > [email protected].
> > For more options, visit this group 
> > athttp://groups.google.com/group/nhusers?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups 
> "nhusers" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> For more options, visit this group 
> athttp://groups.google.com/group/nhusers?hl=en.

-- 
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/nhusers?hl=en.

Reply via email to