New Posts New Posts RSS Feed: Problem with event handlers on cloned entities
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Problem with event handlers on cloned entities

 Post Reply Post Reply
Author
stephenmcd1 View Drop Down
DevForce MVP
DevForce MVP


Joined: 27-Oct-2009
Location: Los Angeles, CA
Posts: 166
Post Options Post Options   Quote stephenmcd1 Quote  Post ReplyReply Direct Link To This Post Topic: Problem with event handlers on cloned entities
    Posted: 13-Feb-2013 at 9:48am
We've been playing around with different ways to quickly move entities from one Entity Manager to another (see this thread).  We've tried using the Clone method but run into problems where event handlers that subscribed to the original entity get raised when changes are made to the newly cloned entity.  It seems to be an issue with the way the EntityAspect is cloned - it starts with a MemberwiseClone which will end up giving the new aspect the same delegate used in the original aspect.

Here is some test code that might explain my problem more clearly:
        public void CloneTests()
        {
            var firstEntity = new MyEntity();

            ((INotifyDataErrorInfo)firstEntity).ErrorsChanged += (_, __) => Assert.Fail("ErrorsChanged fired on first entity");
            firstEntity.PropertyChanged += (_, __) => Assert.Fail("PropertyChanged fired on first entity");

            //Make a copy of the original entity
            var secondEntity = (MyEntity)((ICloneable)firstEntity).Clone();

            //Either force property change or change the validation errors on this *second* entity.  Both these lines will fail because
            //  they will end up executing the code that subscribed to these events on the first entity!
            secondEntity.EntityAspect.ForcePropertyChanged(new PropertyChangedEventArgs("something"));
            secondEntity.EntityAspect.ValidationErrors.Add(new VerifierResult(false));
        }

I'm hoping this isn't just an unsupported scenario.  Obviously, if I wait until after the clone to attach the event handlers, everything works as expected.  But in our code, it's not always easy to do that.  And I haven't found an easy way to work around this in our code (and the reflection restrictions in Silverlight do not help!).
Back to Top
stephenmcd1 View Drop Down
DevForce MVP
DevForce MVP


Joined: 27-Oct-2009
Location: Los Angeles, CA
Posts: 166
Post Options Post Options   Quote stephenmcd1 Quote  Post ReplyReply Direct Link To This Post Posted: 13-Feb-2013 at 11:19am
After doing some very ugly hacking in our code to try to work around the issue above, I've run into another problem with the Clone logic.  And I don't think there will be any way I can work around this one.  Luckily, it seems even more clearly a bug and probably even easier to fix than the issue above.

The problem happens when you clone an entity that already has validation errors.  In that case, the Entity Aspect cloning logic will create a new collection to hold the errors - which is correct.  But it passes the old entity aspect to the collection so things get a bit messed up.  The relevant code is in EntityAspect.CloneCore() and looks like this:
    if (this._validationErrors != null)
    {
        aspect._validationErrors = new VerifierErrorsCollection(this, this._validationErrors);
    }

Note that is is giving 'aspect' (the new aspect) a new VerifierErrorsCollection.  But it's passing 'this' (the source aspect) to the collection!  So the new aspect is holding onto a collection and the collection thinks it is associated with the old aspect!  It seems that this same kind of bug exists in other places in that method (the OriginalValuesMap and BackupValuesMap fields jump out).

Here is some test code to help explain things:
        public void CloneTests2()
        {
            //Make an entity with validation errors
            var firstEntity = new MyEntity();
            firstEntity.EntityAspect.ValidationErrors.Add(new VerifierResult(false));

            //Make a copy of the original entity
            var secondEntity = (MyEntity)((ICloneable)firstEntity).Clone();

            //The validations errors will correctly get copied to the cloned entity.
            Assert.AreEqual(1, secondEntity.EntityAspect.ValidationErrors.Count);//Pass

            //But the VerifierErrorsCollection on the cloned entity isn't quite setup right.  For example...

            //See if ErrorsChanged fires on the second entity
            var errorChangeFiredOnSecondEntity = false;
            ((INotifyDataErrorInfo)secondEntity).ErrorsChanged += (_, __) => errorChangeFiredOnSecondEntity = true;
            
            //Clear out the errors on the second entity to see what happens
            secondEntity.EntityAspect.ValidationErrors.Clear();

            //The error was correctly removed
            Assert.AreEqual(0, secondEntity.EntityAspect.ValidationErrors.Count);//Pass

            //However, this line will fail because ErrorsChanged was not raised on the second entity.  In fact, it was raised on the 
            //  first entity - even though the errors on the first entity weren't touched.
            Assert.IsTrue(errorChangeFiredOnSecondEntity);//Fail

            //Further support for the VerifierErrorsCollection being messed up....let's check out the _entityAspect field on the collection

            //On the first entity, the aspect is correct
            Assert.AreSame(firstEntity.EntityAspect, GetAspect(firstEntity.EntityAspect.ValidationErrors));//Pass

            //However, these two lines will fail.  We'd expect the aspect of the second collection to be the same as the second 
            //  entity's aspect...but it's not!  And in fact, the aspect is still pointing to the first entity
            Assert.AreSame(secondEntity.EntityAspect, GetAspect(secondEntity.EntityAspect.ValidationErrors));//Fail
            Assert.AreNotSame(firstEntity.EntityAspect, GetAspect(secondEntity.EntityAspect.ValidationErrors));//Fail
        }

        private EntityAspect GetAspect(EntityAspect.VerifierErrorsCollection collection)
        {
            //Ugly code to get at the private field
            return (EntityAspect)typeof(EntityAspect.VerifierErrorsCollection)
                                     .GetField("_entityAspect", BindingFlags.Instance | BindingFlags.NonPublic)
                                     .GetValue(collection);
        }
Back to Top
sbelini View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 13-Aug-2010
Location: Oakland
Posts: 786
Post Options Post Options   Quote sbelini Quote  Post ReplyReply Direct Link To This Post Posted: 15-Feb-2013 at 2:26pm
Hi stephenmcd1,

We are investigating these problems and will keep you informed.

sbelini.

Edited by sbelini - 15-Feb-2013 at 2:26pm
Back to Top
sbelini View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 13-Aug-2010
Location: Oakland
Posts: 786
Post Options Post Options   Quote sbelini Quote  Post ReplyReply Direct Link To This Post Posted: 18-Feb-2013 at 1:48pm
Hi stephenmcd1,

I wanted to let you know that we have fixed these issues.
They will be available in the next release of DevForce 2010.

sbelini
Back to Top
stephenmcd1 View Drop Down
DevForce MVP
DevForce MVP


Joined: 27-Oct-2009
Location: Los Angeles, CA
Posts: 166
Post Options Post Options   Quote stephenmcd1 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Feb-2013 at 8:42am
That is great news.  Thanks for the update.
Back to Top
stephenmcd1 View Drop Down
DevForce MVP
DevForce MVP


Joined: 27-Oct-2009
Location: Los Angeles, CA
Posts: 166
Post Options Post Options   Quote stephenmcd1 Quote  Post ReplyReply Direct Link To This Post Posted: 25-Feb-2013 at 3:21pm
Any estimate on when the next release will be available?  We are looking to upgrade soon.
Back to Top
sbelini View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 13-Aug-2010
Location: Oakland
Posts: 786
Post Options Post Options   Quote sbelini Quote  Post ReplyReply Direct Link To This Post Posted: 25-Feb-2013 at 4:49pm
We're planning a new release mid-March.
Back to Top
stephenmcd1 View Drop Down
DevForce MVP
DevForce MVP


Joined: 27-Oct-2009
Location: Los Angeles, CA
Posts: 166
Post Options Post Options   Quote stephenmcd1 Quote  Post ReplyReply Direct Link To This Post Posted: 15-Mar-2013 at 4:09pm
Any update on when this release will be available.  We are eagerly awaiting the release and I'd say it is now technically mid-March :-).

Thanks,
-Stephen
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 15-Mar-2013 at 4:51pm
We plan to release it next Tuesday, 3/19.
Back to Top
stephenmcd1 View Drop Down
DevForce MVP
DevForce MVP


Joined: 27-Oct-2009
Location: Los Angeles, CA
Posts: 166
Post Options Post Options   Quote stephenmcd1 Quote  Post ReplyReply Direct Link To This Post Posted: 15-Mar-2013 at 5:18pm
Great, thanks!
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down