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