Действительно ли NHibernate обеспечивает прозрачную устойчивость - PullRequest
0 голосов
/ 24 августа 2011

Начиная использовать Nhibernate для обеспечения устойчивости, обещая, что он уважает вашу модель домена, я попытался реализовать диспетчер отношений для своих объектов домена. По сути, чтобы СУХОЙ мой код в отношении управления двунаправленными отношениями один ко многим и многими ко многим, я решил, чтобы эти отношения управлялись отдельным классом. Когда установлено свойство «один ко многим» или «много к одному», запись для двух объектов создается в словаре, ключом является либо одна сторона со значением коллекции для хранения многих сторон, либо много сторон со значением одна сторона.

Отношение один ко многим для конкретной комбинации типов выглядит следующим образом:

public class OneToManyRelation<TOnePart, TManyPart> : IRelation<IRelationPart, IRelationPart>
    where TOnePart : class, IRelationPart
    where TManyPart : class, IRelationPart
{
    private readonly IDictionary<TOnePart, Iesi.Collections.Generic.ISet<TManyPart>> _oneToMany;
    private readonly IDictionary<TManyPart, TOnePart> _manyToOne;

    public OneToManyRelation()
    {
        _manyToOne = new ConcurrentDictionary<TManyPart, TOnePart>();
        _oneToMany = new ConcurrentDictionary<TOnePart, Iesi.Collections.Generic.ISet<TManyPart>>();
    }

    public void Set(TOnePart onePart, TManyPart manyPart)
    {
        if (onePart == null || manyPart == null) return;
        if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
        else _manyToOne[manyPart] = onePart;
    }

    public void Add(TOnePart onePart, TManyPart manyPart)
    {
        if (onePart == null || manyPart == null) return;

        if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
        else _manyToOne[manyPart] = onePart;
        if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, new HashedSet<TManyPart>());
        _oneToMany[onePart].Add(manyPart);
    }

    public Iesi.Collections.Generic.ISet<TManyPart> GetManyPart(TOnePart onePart)
    {
        if (!_oneToMany.ContainsKey(onePart)) _oneToMany[onePart] = new HashedSet<TManyPart>();
        return _oneToMany[onePart];
    }

    public TOnePart GetOnePart(TManyPart manyPart)
    {
        if(!_manyToOne.ContainsKey(manyPart)) _manyToOne[manyPart] = default(TOnePart);
        return _manyToOne[manyPart];
    }

    public void Remove(TOnePart onePart, TManyPart manyPart)
    {
        _manyToOne.Remove(manyPart);
        _oneToMany[onePart].Remove(manyPart);
    }

    public void Set(TOnePart onePart, Iesi.Collections.Generic.ISet<TManyPart> manyPart)
    {
        if (onePart == null) return;
        if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, manyPart);
        else _oneToMany[onePart] = manyPart;
    }

    public void Clear(TOnePart onePart)
    {
        var list = new HashedSet<TManyPart>(_oneToMany[onePart]);
        foreach (var manyPart in list)
        {
            _manyToOne.Remove(manyPart);
        }
        _oneToMany.Remove(onePart);
    }

    public void Clear(TManyPart manyPart)
    {
        if (!_manyToOne.ContainsKey(manyPart)) return;
        if (_manyToOne[manyPart] == null) return;

        _oneToMany[_manyToOne[manyPart]].Remove(manyPart);
        _manyToOne.Remove(manyPart);
    }
}

На многих сторонах фрагмент кода выглядит так:

    public virtual SubstanceGroup SubstanceGroup
    {
        get { return RelationProvider.SubstanceGroupSubstance.GetOnePart(this); } 
        protected set { RelationProvider.SubstanceGroupSubstance.Set(value, this); }
    }

С одной стороны, поэтому в данном случае SubstanceGroup фрагмент выглядит следующим образом:

    public virtual ISet<Substance> Substances
    {
        get { return RelationProvider.SubstanceGroupSubstance.GetManyPart(this); }
        protected set { RelationProvider.SubstanceGroupSubstance.Set(this, value); }
    }

Просто используя мои доменные объекты, это отлично работает. В объекте предметной области мне просто нужно сослаться на абстрактную фабрику, которая получает соответствующее отношение, и я могу установить отношение с одной стороны, которая автоматически становится двунаправленной.

Однако, когда NH запускает проблему, я получаю дубликаты ключей в своих словарях. Каким-то образом NH устанавливает свойство отношения с нулевым значением (!) С новой копией (?) Объекта домена. Поэтому, когда объект домена сохраняется, у меня есть две записи этого объекта домена, например, многогранная часть отношения, т.е. словарь _manyToOne.

Эта проблема заставляет меня терять волосы, я не понимаю, что происходит ??

Ответы [ 2 ]

3 голосов
/ 24 августа 2011

Чтобы ответить на ваш первый, очень общий вопрос: «Действительно ли NHibernate обеспечивает прозрачную устойчивость», я просто могу сказать: нет ничего идеального. NH старается изо всех сил быть максимально прозрачным, стараясь максимально снизить его сложность.

Существуют некоторые предположения, особенно в отношении коллекций: коллекции и их реализации не считаются частью модели вашего домена. NH предоставляет свои собственные реализации коллекции. От вас ожидается не только использование интерфейсов, таких как ISet и IList. Вы также должны использовать экземпляры, данные NH, когда объект считывается из базы данных, и никогда не заменять его своим собственным. (Я не знаю, для чего фактически используется ваш класс отношений, поэтому я не знаю, в этом ли здесь проблема.)

Объекты домена уникальны в одном и том же экземпляре сеанса. Если вы каждый раз получаете новые экземпляры объектов домена, вы, вероятно, внедрили анти-шаблон «сеанс на вызов», который создает новый сеанс для каждого взаимодействия с базой данных.

Понятия не имею, что вы на самом деле делаете. Как на самом деле используется этот OneToManyRelation? Что вы делаете, когда NH ведет себя не так, как ожидалось? Это очень специфическая проблема для вашей конкретной реализации.

0 голосов
/ 26 августа 2011

Кроме комментариев о «запутанном коде» и «какого черта ты делаешь». Проблема заключалась в том, что я заменял постоянные коллекции NH, как показано в следующем фрагменте кода:

public void Add(TOnePart onePart, TManyPart manyPart)
{
    if (onePart == null || manyPart == null) return;

    if (!_manyToOne.ContainsKey(manyPart)) _manyToOne.Add(manyPart, onePart);
    else _manyToOne[manyPart] = onePart;
    if (!_oneToMany.ContainsKey(onePart)) _oneToMany.Add(onePart, new HashedSet<TManyPart>());
    _oneToMany[onePart].Add(manyPart);
}

Я создаю новый набор Hashed для многих частей. И это была проблема. Если бы только установил множество частей с приходом коллекции (в случае реализации NH с сохранением коллекции), то это сработало бы.

Как новичок NH, эта замена коллекций специальной реализацией от NH стала важным источником ошибок. Так же, как предупреждение другим новичкам NH.

...