城堡ActiveRecord - 总是更新孩子,为什么?

时间:2009-06-11 20:30:08

标签: c# database activerecord transactions castle-activerecord

过去两周是我第一次使用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)。那么为什么上述两种情况会突然改变呢?

3 个答案:

答案 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()方法。