Castle ActiveProject retrieving stale data from FindAllByProperty

I have an older WinForms 4.x application which is using Castle.ActiveRecord 3.0.0 RC (which is I think the latest available version) on top of NHibernate 3.1.0.4000 (which is not the latest but it was current at that time).

I've noticed an odd data inconsistency behaviour inside one form. It creates a TransactionScope for the lifetime of the form and doesn't commit it until the end. It fetches a list of objects and provides some editing UI for them. One of those is a checkbox for the IsPrimary boolean property.

Only one object is supposed to have IsPrimary == true, so when you toggle the checkbox it does:

var others = MyData.FindAllByProperty("IsPrimary", true)
        .Where(x => x.Name != current.Name).ToList();
foreach (var data in others)
{
    data.IsPrimary = false;
    data.Save();
}
current.IsPrimary = true;
current.Save();

(Which probably could be done more efficiently, but ignore that for now. current is the object which is having its checkbox ticked. And they do all have unique names.)

This works correctly most of the time, but if you open the window, tick the checkbox on some other item (so it does the above and correctly sets the previous one to false), and then without closing the window you try to tick the first item again, you end up with two objects set IsPrimary.

The problem appears to be that FindAllByProperty is returning the original object state outside of the form's transaction, ignoring any changes.

If I replace the call with this code:

var others = MyData.FindAll().Where(x => x.IsPrimary)
        .Where(x => x.Name != current.Name).ToList();

Then it returns the correct result (including the changes made inside the form's transaction).

Is this a known bug? Is there a workaround other than using FindAll()?


Edit: FWIW, using explicit criteria has the same result (it returns stale data, not the correct data):

var criteria = DetachedCriteria.For(typeof(MyData))
    .SetResultTransformer(CriteriaSpecification.DistinctRootEntity)
    .Add(Restrictions.Eq("IsPrimary", true))
    .Add(Restrictions.Not(Restrictions.IdEq(current.Id)));
var others = MyData.FindAll(criteria);
// still stale
728x90

1 Answers Castle ActiveProject retrieving stale data from FindAllByProperty

here are playing three things together:

  • without commiting the data is not changed inside of the database.
  • when possible nhibernate convertes restrictins into SQL and the database-server runs the checks. ONLY if possible.
  • a session has a chace and if a record (primary) key is returned that is already in the chace it will ignore the record (values) and just reuse the object from the Cache.

This means Restrictions.Eq("IsPrimary", true) or Where(x => x.IsPrimary) will (I think) run in the database-server and can never see any data only changed in the application (aka. without commit, aka. transient data, aka. dirty data/objects). This means your list of objects you set IsPrimary=false is the worong list if more then one object in your application/session/chace has IsPrimary=true but in the database still IsPrimary=false !

You'd either have to do one of the following things, but all are bad in ther own way:

  • load all objects without restriction
  • or scann through the sesssion manually to find all transient/dirty objects
  • or commit after every Change of IsPrimary

If possible I'd suggest you to switch from a boolean field in the table/class to a refercence field of the type of your data-object in a different table/class with only one entry and stor the primary object in that other table/class. It will automaticaly only allow one primary AND will cause a concurrency if two sessions/users try to create a primary at the same time (better then having two primaris :D ).

Perhaps your data-structure already has a kind of Father/supperior class to your data class and that one should know IT's Primary.

Greetings Juy Juka

4 months ago