Hi,

From time to time we experience asynchronizations between the EntitySet<t> and our own list. After debugging pretty long today I found that the CollectionChange sometimes comes with oldItems that are still in the EntitySet at the time the event is fired.

Here is the stack when that happens:

Diartis.KLIB.Model.ObservableEntityList<Diartis.KLIB.Model.LastName>.EntitySetCollectionChanged(object sender = {Xtensive.Storage.EntitySet<Diartis.KLIB.Model.LastName>}, System.Collections.Specialized.NotifyCollectionChangedEventArgs e = {System.Collections.Specialized.NotifyCollectionChangedEventArgs}) Line 37 C#
  Xtensive.Storage.dll!Xtensive.Storage.EntitySetBase.NotifyCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedAction action = Remove, Xtensive.Storage.Entity item = {Xtensive.Storage.Internals.Key<int>}, int? index = null) Line 385 + 0x84 bytes C#
  Xtensive.Storage.dll!Xtensive.Storage.EntitySetBase.SystemRemove(Xtensive.Storage.Entity item = {Xtensive.Storage.Internals.Key<int>}, int? index = null) Line 250 + 0x1f bytes C#
  Xtensive.Storage.dll!Xtensive.Storage.EntitySetBase.Remove.AnonymousMethod__a() Line 584 C#
  Xtensive.Core.dll!Xtensive.Core.ExceptionAggregator.Execute(System.Action action = {Method = {System.Reflection.RuntimeMethodInfo}}) Line 106 + 0xf bytes C#
  Xtensive.Storage.dll!Xtensive.Storage.ReferentialIntegrity.RemovalContext.ProcessFinalizers() Line 115 + 0x68 bytes C#
  Xtensive.Storage.dll!Xtensive.Storage.ReferentialIntegrity.RemovalProcessor.Remove(System.Collections.Generic.IEnumerable<Xtensive.Storage.Entity> entities = {Xtensive.Core.Collections.EnumerableUtils.One<Xtensive.Storage.Entity>}) Line 95 C#
  Xtensive.Storage.dll!Xtensive.Storage.Entity.Remove() Line 258 C#
  KLIBDatabase.dll!Diartis.KLIB.Model.AbstractEntity.OnDelete() Line 627 + 0x9 bytes C#
  KLIBDatabase.dll!Diartis.KLIB.Model.LastName.OnDelete() Line 467 + 0xa bytes C#
  KLIBDatabase.dll!Diartis.KLIB.Model.AbstractEntity.Delete() Line 600 + 0x1b bytes C#
  KLIBLibrary.dll!Diartis.KLIB.KLIBLibrary.Lists.DeleteAll(System.Collections.IList list = Count = 1) Line 45 + 0x3e bytes C#
  KLIBDatabase.dll!Diartis.KLIB.Model.AbstractEntity.Delete() Line 597 + 0x9 bytes C#
  KLIBViewModel.dll!Diartis.KLIB.KLIBViewModel.VMAbstractEntity.Delete() Line 571 + 0x28 bytes C#
  KLIBUnitTests.dll!Diartis.KLIB.KLIBUnitTests.ModelTests.DeleteTest() Line 902 + 0x14 bytes C#

At that exact moment the following test returns true: m_EntitySet.Contains((LastName)e.OldItems[0])==true

Regards Paul Sinnema Diartis AG

asked Nov 19 '10 at 10:18

Paul%20Sinnema's gravatar image

Paul Sinnema
261868896

edited Nov 19 '10 at 12:55

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412


2 Answers:

Just to prove that it is probably a DO bug. The following produces an 'Entity is removed' Exception in DO code.

((Person)((VMPerson)person).ModelEntity).Z_LastNameList.Count

answered Nov 19 '10 at 10:23

Paul%20Sinnema's gravatar image

Paul Sinnema
261868896

edited Nov 19 '10 at 12:56

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

Is the DisconnectedState attached when this code is running?

(Nov 19 '10 at 12:57) Alex Yakunin Alex%20Yakunin's gravatar image

If so, probably, that's a side effect of another bug.

(Nov 19 '10 at 12:58) Alex Yakunin Alex%20Yakunin's gravatar image

Yep, at the moment we're using DisconnectedState only.

(Nov 19 '10 at 15:19) Paul Sinnema Paul%20Sinnema's gravatar image

Alex,

I've created a test that illustrates what goes wrong. Code is below. In the 'person.Z_LastNameList.Remove(lastName2)' the bug is produced. It is exactly how we remove items from the EntitySet when items are removed from our set.

Regards, Paul

(Nov 20 '10 at 03:32) Paul Sinnema Paul%20Sinnema's gravatar image

Any news here?

(Nov 28 '10 at 10:55) Paul Sinnema Paul%20Sinnema's gravatar image

No news for now, but we'll investigate this in few days.

(Nov 28 '10 at 15:48) Alex Yakunin Alex%20Yakunin's gravatar image
using System;
using System.Collections.Specialized;
using DOProject1.Model;
using Xtensive.Storage;
using Xtensive.Storage.Configuration;

namespace DOProject1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (Session session = Session.Open(Domain.Build(DomainConfiguration.Load("Default"))))
            {
                DisconnectedState disconnectedState = new DisconnectedState();
                disconnectedState.Attach(session);

                Person person = new Person();
                person.Z_LastNameList.CollectionChanged += LastNameListCollectionChanged;
                LastName lastName1 = new LastName();

                lastName1.Z_Person = person;

                System.Diagnostics.Debug.Assert(person.Z_LastNameList.Count == 1, "LastNameList count == 1");

                LastName lastName2 = new LastName();

                person.Z_LastNameList.Add(lastName2);

                System.Diagnostics.Debug.Assert(person.Z_LastNameList.Count == 2, "LastNameList count == 2");

                lastName1.Remove();

                System.Diagnostics.Debug.Assert(person.Z_LastNameList.Count == 1, "LastNameList count == 1");

                person.Z_LastNameList.Remove(lastName2);

                System.Diagnostics.Debug.Assert(person.Z_LastNameList.Count == 0, "LastNameList count == 0");
            }
            Console.WriteLine("Test is finished");
            Console.ReadKey();
        }

        static void LastNameListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            EntitySet<LastName> set = (EntitySet<LastName>)sender;

            switch (e.Action)
            {
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    System.Diagnostics.Debug.Assert(set.Contains((LastName)e.NewItems[0]), "set contains newitem");
                    break;

                case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
                    break;

                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    // The Contains() of the set throws an 'Entity is removed' Exception?
                    foreach (var item in set)
                    {
                        if (object.ReferenceEquals(item, e.OldItems[0]))
                        {
                            System.Diagnostics.Debug.Assert(false, "set not contains newitem");
                        }
                    }

                    break;

                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
                    break;

                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    break;

                default:
                    break;
            }
        }
    }
}

using System;
using Xtensive.Core.IoC;
using Xtensive.Storage;
using Xtensive.Storage.Configuration;

namespace DOProject1.Model
{
    [Service(typeof(KeyGenerator), Name = "AbstractEntity")]
    public class AbstractEntityKeyGenerator : CachingKeyGenerator<int>
    {
        [ServiceConstructor]
        public AbstractEntityKeyGenerator(DomainConfiguration configuration)
            : base(configuration)
        {
        }
    }

    [Serializable]
    [HierarchyRoot, KeyGenerator(Name = "AbstractEntity")]
    public abstract partial class AbstractEntity : Entity
    { // START partial class
        #region DataObjectsFields

        [Key]
        [Field(Nullable = false), FieldMapping("Id")]
        public int Z_Id { get; private set; }

        [Field(Nullable = false), FieldMapping("CreatedOn")]
        public DateTime Z_CreatedOn { get; set; }

        [Field(Nullable = false), FieldMapping("CreatedBy")]
        public int Z_CreatedBy { get; set; }

        [Field(Nullable = false), FieldMapping("ChangedOn")]
        public DateTime Z_ChangedOn { get; set; }

        [Field(Nullable = false), FieldMapping("ChangedBy")]
        public int Z_ChangedBy { get; set; }

        [Field(Nullable = false), FieldMapping("IsDefault")]
        public bool Z_IsDefault { get; set; }

        #endregion //END region DataObjectsFields
    }

    [Serializable]
    public partial class LastName : AbstractEntity
    { // START partial class
        #region DataObjectsFields

        [Field(Nullable = true), FieldMapping("Name")]
        public string Z_Name { get; set; }

        [Field(Nullable = false), FieldMapping("PersonId")]
        [Association(OnOwnerRemove = OnRemoveAction.Clear, OnTargetRemove = OnRemoveAction.Clear)]
        public Person Z_Person { get; set; }

        #endregion //END region DataObjectsFields
    }

    [Serializable]
    public partial class Person : AbstractEntity
    { // START partial class
        #region DataObjectsFields

        [Field(Nullable = true), FieldMapping("PersonnelNumber")]
        public string Z_PersonnelNumber { get; set; }

        [Field(Nullable = true), FieldMapping("Nickname")]
        public string Z_Nickname { get; set; }

        [Field(Nullable = true), FieldMapping("FirstNames")]
        public string Z_FirstNames { get; set; }

        [Field(Nullable = true), FieldMapping("BirthDate")]
        public DateTime? Z_BirthDate { get; set; }

        [Field(Nullable = true), FieldMapping("DeathDate")]
        public DateTime? Z_DeathDate { get; set; }

        [Field(Nullable = true), FieldMapping("AHVNumberOld")]
        public string Z_AHVNumberOld { get; set; }

        [Field(Nullable = true), FieldMapping("AHVNumberNew")]
        public string Z_AHVNumberNew { get; set; }

        [Field(Nullable = true), FieldMapping("Title")]
        public string Z_Title { get; set; }

        [Field(Nullable = false), FieldMapping("IsUnknown")]
        public bool Z_IsUnknown { get; set; }

        [Field(Nullable = false), FieldMapping("WantsToStayAnonymous")]
        public bool Z_WantsToStayAnonymous { get; set; }

        [Field(Nullable = false), FieldMapping("IsUser")]
        public bool Z_IsUser { get; set; }

        [Field(Nullable = true), FieldMapping("WindowsLogonName")]
        public string Z_WindowsLogonName { get; set; }

        [Field(Nullable = true), FieldMapping("UILanguage")]
        public string Z_UILanguage { get; set; }

        [Field(LazyLoad = true)]
        [Association(OnOwnerRemove = OnRemoveAction.Clear, OnTargetRemove = OnRemoveAction.Clear, PairTo = "Z_Person")]
        public EntitySet<LastName> Z_LastNameList { get; set; }

        #endregion //END region DataObjectsFields
    }
}

answered Nov 20 '10 at 03:29

Paul%20Sinnema's gravatar image

Paul Sinnema
261868896

Paul, could you send me a test project + DB via Skype?

(Nov 22 '10 at 07:47) Alex Yakunin Alex%20Yakunin's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA