db4o Tranparent Persistence не сохраняет более поздние объекты в моей собственной коллекции ActivatableCollection <T> - PullRequest
2 голосов
/ 24 июля 2011

Я перевожу свои собственные ActivatableCollection<T> для db4o, но много пользуюсь встроенной реализацией ActivatableList<T>. Я сталкиваюсь с проблемой, когда прозрачное постоянство, кажется, не работает правильно. В тестовом коде ниже:

[Fact]
void CanStoreActivatableCollection()
{
    var planets = new ActivatableCollection<Planet>();

    var pagingMemoryStorage = new PagingMemoryStorage();
    var config = Db4oEmbedded.NewConfiguration();
    config.Common.Add(new TransparentActivationSupport());
    config.Common.Add(new TransparentPersistenceSupport());
    config.File.Storage = pagingMemoryStorage;

    var objectContainer = Db4oEmbedded.OpenFile(config, "Memory.yap");

    planets.Add(new Planet("Mercury"));

    objectContainer.Store(planets);

    planets.Add(new Planet("Venus"));
    planets.Add(new Planet("Earth"));

    objectContainer.Commit();
    objectContainer.Close();

    config = Db4oEmbedded.NewConfiguration();
    config.Common.Add(new TransparentActivationSupport());
    config.Common.Add(new TransparentPersistenceSupport());
    config.File.Storage = pagingMemoryStorage;

    objectContainer = Db4oEmbedded.OpenFile(config, "Memory.yap");
    planets = objectContainer.Query<ActivatableCollection<Planet>>().FirstOrDefault();
    Assert.NotNull(planets);
    Assert.Equal(3, planets.Count);
    objectContainer.Close();
}

Планета "Меркурий" хранится, но не "Венера" ​​и "Земля". Если я перехожу из ActivatableCollection в ActivatableList, то все 3 планеты сохраняются.

Чего мне не хватает? Насколько я могу судить, моя ActivatableCollection - это лишь минимальная реализация ActivatableList.

Ниже моя реализация ActivatableCollection:

public class ActivatableCollection<T>
    : ICollection<T>
    , IActivatable
    , INotifyCollectionChanged
{
    List<T> _list;

    List<T> List
    {
        get
        {
            if (_list == null)
                _list = new List<T>();
            return _list;
        }
    }

    public ActivatableCollection()
    {
    }

    public int Count
    {
        get
        {
            ActivateForRead();
            return List.Count;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            ActivateForRead();
            return ((IList) List).IsReadOnly;
        }
    }

    public void Add(T t)
    {
        ActivateForWrite();
        List.Add(t);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, t));
    }

    public void Clear()
    {
        ActivateForWrite();
        List.Clear();
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public bool Contains(T t)
    {
        ActivateForRead();
        return List.Contains(t);
    }

    public void CopyTo(T[] array, int index)
    {
        ActivateForRead();
        List.CopyTo(array, index);
    }

    public IEnumerator<T> GetEnumerator()
    {
        ActivateForRead();
        return List.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public bool Remove(T t)
    {
        ActivateForWrite();
        bool removed = List.Remove(t);
        if (removed)
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, t));
        return removed;
    }

    [Transient]
    private IActivator _activator;

    public virtual void Bind(IActivator activator)
    {
        if (_activator == activator)
            return;
        if (activator != null && _activator != null)
            throw new InvalidOperationException();
        _activator = activator;
    }

    public virtual void Activate(ActivationPurpose purpose)
    {
        if (_activator == null)
            return;
        _activator.Activate(purpose);
    }

    protected virtual void ActivateForRead()
    {
        Activate(ActivationPurpose.Read);
    }

    protected virtual void ActivateForWrite()
    {
        Activate(ActivationPurpose.Write);
    }

    [Transient]
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, e);
    }
}

Я также попытался скопировать код из GenericTypeHandlerPredicate и зарегистрировать мою коллекцию ActivatableCollection для использования GenericCollectionTypeHandler. Это приводит к сбою в GenericTypeFor (), вызывая InvalidOperationException () при сохранении «Меркурия».

1 Ответ

2 голосов
/ 28 июля 2011

Просто хочу упомянуть мои ответы на форумах db4o также здесь, для людей с похожей проблемой:

Первая часть проблемы: с точки зрения db4o, ничего не изменилось в объекте 'ActivatableCollection' ипоэтому никакие изменения не сохраняются.Вот что происходит:

  1. Когда вы добавляете элементы, коллекция ActivatableCollection помечается как измененная.

  2. Когда вы фиксируете изменения, они сохраняются,Однако «ActivatableCollection» содержит ссылку на тот же объект.db4o сохраняет только изменения в объекте ActivatableCollection, который является ссылкой на список.Поскольку это то же самое, фактическое изменение не сохраняется.

  3. Список ActivatableCollection никогда не обновляется, поскольку он не помечен как «измененный»

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

Вторая часть проблемы: почему он не работает даже при регистрации GenericCollectionTypeHandler для этого типа?Здесь мы попадаем в детали реализации.GenericCollectionTypeHandler имеет внутренний список поддерживаемых типов, который не включает самодельный «ActivatableCollection».GenericCollectionTypeHandler на самом деле не является частью общедоступного API и предназначен только для внутреннего использования.

Обходной путь / исправление

Просто используйте ActivatableList<T> вместо List<T>.тогда все работает нормально.

...