过去两周是我第一次使用Castle ActiveRecord,以及一般的ActiveRecord模式。我正在研究一个经常使用它的大型系统,我一直在寻找一些奇怪的SQL事务问题(例如下面的问题)。我将给出一个让我完全难过的简化版本:
背景:
我有一个ActiveRecord类,我们称之为User。
假设这个用户有很多“宠物”对象。
[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;
}
}
现在,它们都有自动Id字段(由SQL Server 2005处理)。
问题:
如果我继续向已经拥有宠物的用户添加新宠物并保存用户,我会看到我是否运行了SQL Profiler,其中每只宠物都有UPDATE调用它们...但是根本没有改变任何一个。
我到处乱扔断点,发现当我保存用户时,每只宠物都会调用“OnFlushDirty”(再次,虽然它们从未改变过)。
正在查看(偶尔修改)这些用户和宠物的外部流程最终会导致严重的交易问题,如果上面的情况只会插入已添加的宠物(而不是更新宠物,则可以完全避免)没改变。
问题:
我是否做了以上某些事情,确保这种情况不会发生,这是一个很大的禁忌?
感谢您提供任何帮助!
*编辑1:OnFlushDirty为null previousState._values *
编辑:OH!我几乎忘记了最奇怪的部分!当在这些Pets上调用OnFlushDirty时,previousState和currentState存在......它们(作为一个字典)都有一个内部变量_values,它应该具有前一个和当前状态的值......
...只有currentStates填充了此变量。 previousState的“_values”变量设置为“null”。请注意,这是在之前存在的所有宠物上。应始终使用某些内容填充previousState,对吧?
*编辑2:更换自动属性后... *
我将Auto Property列表替换为带有属性访问器的传统私有成员。它似乎没有什么区别。我把NHProfiler放在系统上,发现NHProfiler无法连接到我的网络应用程序,如果我通过IIS运行它(我在Visual Studio 2008中使用IIS7 / Win7)。
我想我会尝试更改为使用Visual Studio的“ASP.NET开发服务器”来查看NHProfiler是否会看到该应用程序。
当我这样做时发生了两件事:
1)NHProfiler看到了我的应用并开始收集数据 2)对孩子们进行的多次更新消失了
但是,切换回IIS7 / Win7后,会继续发生多次更新。
这是否意味着它可能存在某种配置问题?据我所知,在使用不同的服务器类型时,我的配置中的任何内容都不会更改,而是导航到IIS中的http://localhost,使用ASP.NET开发服务器http://localhost:(some_random_port)。那么为什么上述两种情况会突然改变呢?
答案 0 :(得分:1)
IIRC城堡activerecord依赖于NHib。
在这种情况下,列表在Nhib中具有特殊语义,因为它打算将它作为有序列表。因此,即使您可能没有定义位置属性,它也会更新有序列表中的元素,就像它具有该列一样。
我没有证实这一点,但是IIRC,这是你的问题。
答案 1 :(得分:1)
这很简单。 ActiveRecord无法处理集合中的C#-autoproperties(因为接口类型)。请改用支持字段并使用
进行初始化private IList<Pet> pets = new List<Pet>();
-Markus
答案 2 :(得分:1)
碰巧在hibernate中你必须使用merge()来使hibernate“加载”分离对象的previuos数据,在Castle中等效的是SaveCopy()方法。