Dear All:
Hi! These last two weeks have been my first experience with Castle ActiveRecord, and with the ActiveRecord pattern in general. I'm working on a large system that uses it frequently, and I have been finding some strange SQL transaction issues (such as the one below) as I work on it. I'll give a simplified version of the one that has me totally stumped:
THE BACKGROUND:
I have an ActiveRecord class, let's call it User.
Let's say that this user has many "Pet" objects.
[ActiveRecord]
public class User: PersistentBase<User>
{
//...
[PrimaryKey]
public long Id
{
get;
set;
}
/// <summary>
/// Date and time the object was first persisted to the database
/// </summary>
[Property, ValidateNonEmpty]
public DateTime CreationDate
{
get;
set;
}
/// <summary>
/// Date and time the object was last persisted to the database
/// </summary>
[Property, ValidateNonEmpty]
public DateTime ModificationDate
{
get;
set;
}
/// <summary>
/// Property used for optimistic concurrency
/// </summary>
[Version]
public int LockCount { get; set; }
[HasMany(typeof(Pet), Cascade = ManyRelationCascadeEnum.SaveUpdate, Lazy = false, OrderBy = "Id")]
public IList<Pet> Pets { get; private set; }
//...
protected override bool BeforeSave(IDictionary state)
{
bool retval = base.BeforeSave(state);
DateTime now = DateTime.Now;
state["CreationDate"] = now;
state["ModificationDate"] = now;
return retval;
}
/// <summary>
/// Called when a dirty object is going to be updated in the db. Use this
/// hook to update ModificationDate.
/// </summary>
/// <param name="id"></param>
/// <param name="previousState"></param>
/// <param name="currentState"></param>
/// <param name="types"></param>
/// <returns></returns>
protected override bool OnFlushDirty(object id, IDictionary previousState, IDictionary currentState, IType[] types)
{
bool retval = base.OnFlushDirty(id, previousState, currentState, types);
currentState["ModificationDate"] = DateTime.Now;
return retval;
}
}
[ActiveRecord]
public class Pet : PersistentBase<Pet>
{
[PrimaryKey]
public long Id
{
get;
set;
}
/// <summary>
/// Date and time the object was first persisted to the database
/// </summary>
[Property, ValidateNonEmpty]
public DateTime CreationDate
{
get;
set;
}
/// <summary>
/// Date and time the object was last persisted to the database
/// </summary>
[Property, ValidateNonEmpty]
public DateTime ModificationDate
{
get;
set;
}
/// <summary>
/// Property used for optimistic concurrency
/// </summary>
[Version]
public int LockCount { get; set; }
//...
[BelongsTo("OwnerId")]
public User User { get; set; }
//...
protected override bool BeforeSave(IDictionary state)
{
bool retval = base.BeforeSave(state);
DateTime now = DateTime.Now;
state["CreationDate"] = now;
state["ModificationDate"] = now;
return retval;
}
/// <summary>
/// Called when a dirty object is going to be updated in the db. Use this
/// hook to update ModificationDate.
/// </summary>
/// <param name="id"></param>
/// <param name="previousState"></param>
/// <param name="currentState"></param>
/// <param name="types"></param>
/// <returns></returns>
protected override bool OnFlushDirty(object id, IDictionary previousState, IDictionary currentState, IType[] types)
{
bool retval = base.OnFlushDirty(id, previousState, currentState, types);
currentState["ModificationDate"] = DateTime.Now;
return retval;
}
}
Now, both of them have automatic Id fields (taken care of by SQL Server 2005).
THE PROBLEM:
If I go ahead and add a new pet to a User who already has existing pets and save the User, I see if I run the SQL Profiler that every single one of the pets has had UPDATE called on them... but not a single one was changed at all.
I threw breakpoints everywhere, and found that, when I save the User, each of the pets has "OnFlushDirty" called (again, though they never changed).
An external process that's looking at (and occasionally modifying) these Users and Pets ends up causing severe transaction problems, that could be avoided entirely if the scenario above would ONLY insert the Pet that was added (and not UPDATE the pets that weren't changed).
THE QUESTION:
Am I doing something above that is a big no-no in terms of making sure such a situation doesn't happen?
Thank you for any help you can provide!
* EDIT 1: OnFlushDirty has null previousState._values *
EDIT: OH! I almost forgot the strangest part of all!
When OnFlushDirty gets called on these Pets, the previousState and currentState exists... they both (being a Dictionary) have an internal variable _values which should have the values of the previous and current states...
... only currentStates has this variable populated. previousState's "_values" variable is set to "null". Note that this is on all of the pets that existed before. previousState should always be populated with something, right?
* EDIT 2: After replacing Auto Properties... *
I replaced the Auto Property lists with a traditional private member with property accessors. It didn't seem to make a difference. I put NHProfiler on the system, and found that NHProfiler couldn't connect to my web app if I was running it through IIS (I'm using IIS7/Win7 with Visual Studio 2008).
I figured I'd try changing to using Visual Studio's "ASP.NET Development Server" instead to see if NHProfiler would see the app then.
Two things happened when I did this:
1) NHProfiler saw my app and began collecting data 2) The multiple UPDATES being done on the children went away
However, switching back to IIS7/Win7, the multiple updates continue to happen.
Does this mean that it's potentially some kind of configuration problem? As far as I know, nothing in my configuration should change other than the URL I'm navigating to (http://localhost in IIS, http://localhost:(some_random_port) with the ASP.NET Development Server) when using the different server types. So why do the two situations above suddenly change?