views:

111

answers:

0

hi

When i try to update a property on a item that is in a list from an interceptor, the update is not persisted. Is this possible at all? or am i doing something wrong?
The child is mapped as a list of composite elements.

The context is, i am trying to enforce read consistency for aggregates using Read committed isolation level and optimistic locking(versioning).

In the OnSave and OnFlushDirty methods of the interceptor i increase the aggregate version number, and in the OnLoad method i check that there is consistency in the aggregate numbers of the objects in the aggregate. This fails, because the update was not persisted.

Here are my entity classes:

public class DomainObject
{
    public virtual int Id { get; set; }
    public virtual int Version { get; set; }
    public virtual int AggregateVersion { get; set; }
}

public class Person : DomainObject
{
    private IList<InfoItem> information;
    public virtual string Name { get; set; }
    public virtual IList<InfoItem> Information
    {
        get { return information; }
        set { information = value; } 
    }
}

public class InfoItem : DomainObject
{
    public virtual string Detail { get; set; }      
}

The mapping:

<class name="Person">
    <id name="Id">
        <generator class="native" />
    </id>
    <version name="Version" unsaved-value="0" />
    <property name="AggregateVersion" />
    <property name="Name" type="string" />

    <list name="information" table="PersonInfo" access="field" >
        <key>
            <column name="Id"/>
        </key>
        <index column="Idx"/>
        <composite-element class="InfoItem">
            <property name="AggregateVersion" />
            <property name="Detail" type="string" />
        </composite-element>
    </list>
</class>

Here is the test:

using (ISession s = OpenSession(aggregateInterceptor))
{
    var list = s.CreateQuery("from Person p").List<Person>();
    Person p = list[0];
    // modifying the parent makes the interceptor increase the aggregateversion for the child, but the change is not persisted.
    p.Name = "new name";
    // if we modify the detail direktly, the collection is dirty and will be persisted.
    // p.Information[0].Detail = "new detail from test";
    s.Flush();
}

and, finally the interceptor which updates the item in the list:

internal class AggregateInterceptor : EmptyInterceptor
{
    public override bool OnFlushDirty(object entity, object id, object[] state, object[] previousState, string[] propertyNames, IType[] types)
    {
        Output("Updating: " + entity.GetType().Name + " id: " + id);
        UpdateAggregateVersion(propertyNames, state, entity);
        return true;
        }

    public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
    {
        UpdateAggregateVersion(propertyNames, state, entity);
        return true;
    }

    private static void UpdateAggregateVersion(string[] propertyNames, object[] state, object entity)
    {
        Person symbol = entity as Person;
        if (symbol != null)
        {
            int index = Array.IndexOf(propertyNames, "AggregateVersion"); 
            if (index >= 0)
            {
                int newAggregateVersion = (int)state[index] + 1;
                state[index] = newAggregateVersion;
                int locindex = Array.IndexOf(propertyNames, "information"); 
                if (locindex >= 0)
                {
                    IList<InfoItem> information = ((IList<InfoItem>)state[locindex]);
                    foreach (InfoItem infoItem in information)
                    {
                        infoItem.AggregateVersion = newAggregateVersion;
                    }
                }
            }
        }
    }

public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
    CheckAggregateVersion(entity, id, state, propertyNames);
    return base.OnLoad(entity, id, state, propertyNames, types);
}

private static void Output(string message)
{
    Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " " + message);
}

private static void CheckAggregateVersion(object entity, object id, object[] state, string[] propertyNames)
{
    Person symbol = entity as Person;
    if (symbol != null)
    {
        int index = Array.IndexOf(propertyNames, "AggregateVersion");
        if (index >= 0)
        {
            int locindex = Array.IndexOf(propertyNames, "information"); // DO NOT LOCALIZE
            if (locindex >= 0)
            {
                IList<InfoItem> information = ((IList<InfoItem>)state[locindex]);
                foreach (InfoItem infoItem in information)
                {
                    if (infoItem.AggregateVersion != (int)state[index])
                    {
                        Output(infoItem.GetType() + ": " + infoItem.AggregateVersion);
                        Output(entity.GetType() + ": " + state[index]);
                        throw new AggregateVersionMismatchException(entity, id);
                    }
                }
            }
        }
    }
}

related questions