Замок ActiveRecord - всегда обновляю детей, почему? - PullRequest
1 голос
/ 12 июня 2009

Последние две недели я впервые познакомился с 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;
    }

}

Теперь у них обоих есть поля автоматического идентификатора (об этом заботится SQL Server 2005).

ПРОБЛЕМА:

Если я иду дальше и добавляю нового питомца к пользователю, у которого уже есть домашние животные, и сохраняю пользователя, я вижу, запускаю ли я SQL Profiler, чтобы на каждом из этих питомцев было вызвано UPDATE ... но ни один не изменился вообще.

Я бросил точки останова повсюду и обнаружил, что при сохранении пользователя каждому из питомцев вызывается «OnFlushDirty» (опять же, хотя они никогда не менялись).

Внешний процесс, который просматривает (и иногда изменяет) этих пользователей и домашних животных, в итоге вызывает серьезные проблемы с транзакциями, которых можно полностью избежать, если в приведенном выше сценарии ТОЛЬКО будет добавлено добавленное домашнее животное (а не ОБНОВЛЕНИЕ домашних животных, которые были не изменилось).

ВОПРОС:

Я делаю что-то выше, что является большим нет-нет с точки зрения того, чтобы такая ситуация не возникала?

Спасибо за любую помощь, которую вы можете оказать!

* РЕДАКТИРОВАТЬ 1: OnFlushDirty имеет значение null previousState._values ​​*

РЕДАКТИРОВАТЬ: О! Я почти забыл самую странную часть всего!

Когда OnFlushDirty вызывается для этих Pets, предыдущиеState и currentState существуют ... они оба (будучи словарём) имеют внутреннюю переменную _values, которая должна иметь значения предыдущего и текущего состояний ...

... эта переменная заполнена только для currentStates. Переменная "_values" для previousState установлена ​​в "null". Обратите внимание, что это на всех домашних животных, которые существовали раньше. previousState всегда должен быть заполнен чем-то, верно?

* РЕДАКТИРОВАТЬ 2: после замены Auto Properties ... *

Я заменил списки авто-свойств традиционным частным участником со средствами доступа к свойствам. Похоже, это не имеет значения. Я установил NHProfiler в системе и обнаружил, что NHProfiler не может подключиться к моему веб-приложению, если я запускаю его через IIS (я использую IIS7 / Win7 с Visual Studio 2008).

Я решил, что вместо этого попробую перейти на использование сервера разработки ASP.NET в Visual Studio, чтобы увидеть, увидит ли приложение NHProfiler.

Когда я сделал это, произошли две вещи:

1) NHProfiler увидел мое приложение и начал сбор данных 2) Многочисленные ОБНОВЛЕНИЯ, сделанные на детей, исчезли

Однако, возвращаясь к IIS7 / Win7, множественные обновления продолжают происходить.

Значит ли это, что это потенциально какая-то проблема конфигурации? Насколько я знаю, ничто в моей конфигурации не должно меняться, кроме URL-адреса, на который я перехожу (http://localhost в IIS, http://localhost:(some_random_port) с сервером разработки ASP.NET) при использовании другого сервера типы. Так почему же две ситуации выше внезапно меняются?

Ответы [ 3 ]

1 голос
/ 13 июня 2009

Это очень просто. ActiveRecord не может справиться с C # -автобусами в коллекциях (из-за типа интерфейса). Вместо этого используйте вспомогательное поле и инициализируйте его

private IList<Pet> pets = new List<Pet>();

-Markus

1 голос
/ 31 января 2010

Бывает, что в hibernate вы должны использовать merge (), чтобы этот hibernate «загружал» предыдущие данные отсоединенного объекта, в Castle его эквивалентом является метод SaveCopy ().

1 голос
/ 12 июня 2009

Активная запись замка IIRC основана на NHib.

В таком случае список имеет особую семантику в Nhib, так как он предназначен для упорядоченного списка. Поэтому, даже если у вас не определено свойство position, оно обновляет элементы в упорядоченном списке, как если бы у него был этот столбец.

Я не проверял это, но IIRC, это ваша проблема.

...