【问题标题】:Castle ActiveRecord - Always updating children, why?Castle ActiveRecord - 总是更新孩子,为什么?
【发布时间】:2009-06-11 20:30:07
【问题描述】:

这两周是我第一次使用 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 *

编辑:哦!我差点忘了最奇怪的部分!

当在这些 Pets 上调用 OnFlushDirty 时,previousState 和 currentState 存在...它们(作为字典)都有一个内部变量 _values,它应该具有先前和当前状态的值...

... 只有 currentStates 填充了这个变量。 previousState 的“_values”变量设置为“null”。请注意,这适用于以前存在的所有宠物。 previousState 应该总是填充一些东西,对吧?

* 编辑 2:替换自动属性后... *

我用带有属性访问器的传统私有成员替换了自动属性列表。这似乎没有什么不同。我将 NHProfiler 放在系统上,发现如果我通过 IIS 运行 NHProfiler (我在 Visual Studio 2008 中使用 IIS7/Win7),NHProfiler 无法连接到我的 Web 应用程序。

我想我会尝试改用 Visual Studio 的“ASP.NET 开发服务器”来查看 NHProfiler 是否会看到该应用程序。

当我这样做时发生了两件事:

1) NHProfiler 看到我的应用并开始收集数据 2) 对孩子进行的多次更新消失了

但是,切换回 IIS7/Win7 后,多个更新仍在继续。

这是否意味着它可能存在某种配置问题?据我所知,在使用不同的服务器类型时,除了我导航到的 URL(IIS 中的http://localhost,ASP.NET 开发服务器中的http://localhost:(some_random_port))之外,我的配置中的任何内容都不应更改。那么为什么上面的两种情况会突然发生变化呢?

【问题讨论】:

    标签: c# database activerecord transactions castle-activerecord


    【解决方案1】:

    IIRC 城堡 activerecord 依赖于 NHib。

    在这种情况下,列表在 Nhib 中具有特殊的语义,因为它打算将其作为有序列表。因此,即使您可能没有定义位置属性,它也会更新有序列表中的元素,就好像它具有该列一样。

    我还没有验证这一点,但是 IIRC,这是你的问题。

    【讨论】:

    • 哦,有趣!我会尝试用更通用的机制替换它...... ICollection 会停止这个问题吗?或者可能只是 IEnumerable?
    • 查看此链接:hibernate.org/hib_docs/nhibernate/html_single/… 并注意以“除 ISet 和 bag 之外的所有集合类型都有一个索引列”开头的段落还有更多信息,但我相信这应该让你开始了。
    • 啊,谢谢!我深深地感激它!我现在正在阅读文档,我会试一试,告诉你进展如何!
    • 啊……呸。我经历并改变了一切以尝试让它工作......它现在正在使用 ISets,但它仍在更新所有宠物。 =(
    • 你的级联设置是什么?
    【解决方案2】:

    这很简单。 ActiveRecord 无法处理集合中的 C#-autoproperties(因为接口类型)。改为使用支持字段并使用

    对其进行初始化
    private IList<Pet> pets = new List<Pet>();
    

    -马库斯

    【讨论】:

    • 哦,我不知道!那是在某个地方的文档中吗?我没有看到任何提及这一点,但这绝对是件好事。我会在星期一回来后立即试一试,然后告诉你!你知道有什么好的资源可以让我更多地了解 ActiveRecord 中的这些“陷阱”吗?
    • 在这方面一定有一些很好的资源......我还没有找到它。要么我没有在 Castle 文档中找到正确的位置,要么他们缺少这些信息......而且我找不到一个好的外部参考资料可以让我了解这样的事情。 =(
    • 是的,不是这样。我用显式属性替换了程序中的所有集合自动属性,它没有任何区别......它仍在为未更改的子级引发 OnFlushDirty 事件,previousState._value 仍然为空。
    • 这里,我更新一下OP,我发现了一些奇怪的情况,可能会影响正在发生的事情。
    【解决方案3】:

    碰巧在hibernate中你必须使用merge()来使hibernate“加载”分离对象的previuos数据,在Castle中相当于SaveCopy()方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-21
      相关资源
      最近更新 更多