New Posts New Posts RSS Feed: ForcePropertyChanged does not fire interceptors
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

ForcePropertyChanged does not fire interceptors

 Post Reply Post Reply
Author
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Topic: ForcePropertyChanged does not fire interceptors
    Posted: 11-Aug-2009 at 9:37am
Hi,
 
We use the Server Push Notification to push changes from a client to all other clients that have registered to the Notification Service. Below is the Code we use on the Client to update it's cache.
 
Now everything seems to be working as expected but somehow the changes are not reflected back on the Masks. As you can see we use the 'entity.EntityAspect.ForcePropertyChanged(null);' (in the switch with 'case EntityState.Modified') to try and force all interceptors in the viewmodel to fire (which in turn would change the values in the DependecyProperties we use in WPF). This however doesn't happen. Any clues on why not?
 
Or even better, is there another way to provoke the Interceptors to fire. The 'ForcePropertyChanged' would result in the Entity getting state 'modified' which is not what we want.
 
We know that using 'OverwriteChanges' as MergeStrategy is not favoured. We will work on that too.
 
Regards,
Paul Sinnema
Diartis AG
 

/// <summary>

/// Gets called from the server if it has something to tell

/// </summary>

/// <param name="userToken"></param>

/// <param name="args"></param>

private void PushNotificationCalled(object userToken, params Object[] args)

{

// We should not handle our own changes here

//if (!userToken.Equals(m_GuidUserToken))

//{

//}

MemoryStream stream = (MemoryStream)args[0];

stream.Seek(0, SeekOrigin.Begin);

BinaryFormatter bf = new BinaryFormatter();

EntityContainer ec = (EntityContainer)bf.Deserialize(stream);

KLIBEntityManager manager = VMContext.DefaultManager;

Entity entity;

EntityKey key;

foreach (EntityInfo ei in ec.List)

{

switch (ei.State)

{

case EntityState.Added:

key = new EntityKey(ei.Type, ei.KeyValues);

// manager.GetBy

manager.DefaultQueryStrategy = QueryStrategy.DataSourceOnly;

entity = (Entity)key.ToQuery().FirstOrNullEntity();

if (!entity.EntityAspect.IsNullEntity)

{

manager.AddEntity(entity);

}

manager.DefaultQueryStrategy = QueryStrategy.Normal;

break;

case EntityState.Deleted:

key = new EntityKey(ei.Type, ei.KeyValues);

entity = (Entity)manager.FindEntity(key, /*includeDeleted=*/true);

if (!entity.EntityAspect.IsNullEntity)

{

manager.RemoveEntity(entity);

}

break;

case EntityState.Modified:

key = new EntityKey(ei.Type, ei.KeyValues);

entity = (Entity)manager.FindEntity(key, /*includeDeleted=*/false);

manager.RefetchEntity(entity, MergeStrategy.OverwriteChanges);

entity.EntityAspect.ForcePropertyChanged(null);

; break;

}

}

}

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: 12-Aug-2009 at 11:17am
ForcePropertyChanged(null) just fires a PropertyChanged event for the item itself (the property name is an empty string or null).  Listeners, like bindings, may call the property getters on the item, and in doing so the get property interceptors will be called, but firing the event won't itself force the interceptors to be called, and also won't change the EntityState of the item.
 
Unfortunately, there is no way to force the interceptors to fire.  You can, with some pain, obtain the interceptor actions and invoke them, but this is difficult because these methods take generic arguments and require that you already know the properties and data types (e.g, Employee.EmployeeIdDataProperty.GetterInterceptor.GetActions() or PropertyInterceptorManager.CurrentInstance.GetActions<TArgs>(type, property, mode)). 
 
A somewhat heavy handed approach would be to invoke the getters (and setters) to force the interceptors to be called.  (In the next release, setting a value to itself will not dirty the item as it does now.)   Something like this will invoke the getters for data properties (not navigation properties) -
 
      var props = _entityManager.MetadataStore.GetEntityMetadata(typeof(Employee)).DataProperties;
      foreach (DataEntityProperty p in props) {
        p.GetValue(anEmployee);
      }
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 13-Aug-2009 at 8:25am
Hi Kim,
 
Since we use T4 for Generating standard functionality for each Entity (like the 'Create()') we're able to use the Employee.EmployeeIdDataProperty.GetterInterceptor.GetActions() construct you propose.  I've generated something like this:
 
  public void Refresh()
  {
   AbstractEntity.IdEntityProperty.GetterInterceptor.GetActions();
   AbstractEntity.CreatedOnEntityProperty.GetterInterceptor.GetActions();
   AbstractEntity.CreatedByEntityProperty.GetterInterceptor.GetActions();
   AbstractEntity.ChangedOnEntityProperty.GetterInterceptor.GetActions();
   AbstractEntity.ChangedByEntityProperty.GetterInterceptor.GetActions();
   AbstractEntity.DeletedOnEntityProperty.GetterInterceptor.GetActions();
   AbstractEntity.DeletedByEntityProperty.GetterInterceptor.GetActions();
  }
It compiles but I still need to test the code though. I'll tell you how it goes.
 
Regards,
Paul Sinnema
Diartis AG
 


Edited by Sinnema - 13-Aug-2009 at 8:25am
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: 13-Aug-2009 at 10:51am

That's cool that you're using T4!  The GetActions() call only retrieves the actions, however; it doesn't invoke them.  You'll also need to create interceptor arguments appropriate to the property and call each action:


      var actions = AbstractEntity.IdEntityProperty.GetterInterceptor.GetActions();
      var args = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,int>(AbstractEntity.IdEntityProperty, anEntity, null);
      foreach (var a in actions) {
        a.Action(args);
      }
After going to all this trouble, though, you're doing almost what the GetValue() call does - really everything but returning the property value - so it might be easier to call GetValue(), unless you what to exclude certain actions.
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 14-Aug-2009 at 12:07am
Kim,
 
Yeah.... T4 is realy cool. It's not easy to program in T4 though. You've always got 2 mingled languages (C# in the Template and C# being generated) but the result is pretty cool. Here's what I generate now. I've still not been able to test this. I'll let you know the result.
 
        // [DebuggerNonUserCode]
  public void Refresh()
  {
   var actionsId = AbstractEntity.IdEntityProperty.GetterInterceptor.GetActions();
   var argsId = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsId)
   {
    a.Action(argsId);
   }
     
   var actionsCreatedOn = AbstractEntity.CreatedOnEntityProperty.GetterInterceptor.GetActions();
   var argsCreatedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsCreatedOn)
   {
    a.Action(argsCreatedOn);
   }
     
   var actionsCreatedBy = AbstractEntity.CreatedByEntityProperty.GetterInterceptor.GetActions();
   var argsCreatedBy = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsCreatedBy)
   {
    a.Action(argsCreatedBy);
   }
     
   var actionsChangedOn = AbstractEntity.ChangedOnEntityProperty.GetterInterceptor.GetActions();
   var argsChangedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsChangedOn)
   {
    a.Action(argsChangedOn);
   }
     
   var actionsChangedBy = AbstractEntity.ChangedByEntityProperty.GetterInterceptor.GetActions();
   var argsChangedBy = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsChangedBy)
   {
    a.Action(argsChangedBy);
   }
     
   var actionsDeletedOn = AbstractEntity.DeletedOnEntityProperty.GetterInterceptor.GetActions();
   var argsDeletedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime?>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsDeletedOn)
   {
    a.Action(argsDeletedOn);
   }
     
   var actionsDeletedBy = AbstractEntity.DeletedByEntityProperty.GetterInterceptor.GetActions();
   var argsDeletedBy = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32?>(AbstractEntity.IdEntityProperty, this, null);
   
   foreach ( var a in actionsDeletedBy)
   {
    a.Action(argsDeletedBy);
   }
     
  }
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: 14-Aug-2009 at 6:55am
Looks good, except be sure to use the correct property when you create the args -

var actionsCreatedOn = AbstractEntity.CreatedOnEntityProperty.GetterInterceptor.GetActions();
var argsCreatedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime>(AbstractEntity.CreatedOnEntityProperty, this, null);

//etc

  
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 14-Aug-2009 at 12:28pm
Hi Kim,
 
That's probably why there are no action found at all. I'll try it and let you know on monday how it goes.
 
Cheers,
Paul Sinnema
Diartis AG
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 14-Aug-2009 at 11:55pm
Kim,
 
I've changed the code as suggested. But it still doesn't find any Interceptors. I guess it's because we don't have any 'Get' Interceptors. We only have 'After Set' interceptors in our ViewModel. Below is what we generate in the ViewModel to intercept the Set actions on the Model. These Actions take care of forwarding the Value of the Properties to the WPF UI.
 
Regards
Paul Sinnema
Diartis AG
==================================================================
 
This is what we generate for the ViewModel
 
==================================================================
 
   #region Add Actions for Getter/Setters
   
   // Allproperties Count      : 8
   // --------------------------------
            // Property                 : Id
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_IdAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, Int32>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.Id,
          PropertyInterceptorMode.AfterSet,
          IdSetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_IdAction);
   m_Id.Value = m_AbstractEntity.Id;
   m_Id.ValueChanged += IdOnValueChanged;
            // Property                 : CreatedOn
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_CreatedOnAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, DateTime>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.CreatedOn,
          PropertyInterceptorMode.AfterSet,
          CreatedOnSetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_CreatedOnAction);
   
            // Property                 : CreatedBy
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_CreatedByAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, Int32>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.CreatedBy,
          PropertyInterceptorMode.AfterSet,
          CreatedBySetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_CreatedByAction);
   m_CreatedBy.Value = m_AbstractEntity.CreatedBy;
   
            // Property                 : ChangedOn
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_ChangedOnAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, DateTime>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.ChangedOn,
          PropertyInterceptorMode.AfterSet,
          ChangedOnSetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_ChangedOnAction);
   
            // Property                 : ChangedBy
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_ChangedByAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, Int32>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.ChangedBy,
          PropertyInterceptorMode.AfterSet,
          ChangedBySetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_ChangedByAction);
   m_ChangedBy.Value = m_AbstractEntity.ChangedBy;
   
            // Property                 : DeletedOn
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_DeletedOnAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, DateTime?>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.DeletedOn,
          PropertyInterceptorMode.AfterSet,
          DeletedOnSetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_DeletedOnAction);
   
            // Property                 : DeletedBy
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_DeletedByAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, Int32?>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.DeletedBy,
          PropertyInterceptorMode.AfterSet,
          DeletedBySetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_DeletedByAction);
   m_DeletedBy.Value = m_AbstractEntity.DeletedBy;
   
            // Property                 : UserToken
            // MustGenerateInterceptor  : True
            // Navigation               : False
   m_UserTokenAction = new PropertyInterceptorAction<DataEntityPropertySetInterceptorArgs<DomainModel.AbstractEntity, Guid?>>(
          typeof(DomainModel.AbstractEntity),
          DomainModel.AbstractEntity.EntityPropertyNames.UserToken,
          PropertyInterceptorMode.AfterSet,
          UserTokenSetter);
   PropertyInterceptorManager.CurrentInstance.AddAction(m_UserTokenAction);
   m_UserToken.ValueChanged += UserTokenOnValueChanged;
   #endregion
 
==================================================================
 
This is what I generate now:
 
==================================================================
 
// [DebuggerNonUserCode]
  public void Refresh()
  {
   var actionsId = AbstractEntity.IdEntityProperty.GetterInterceptor.GetActions();
   var argsId = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32>(AbstractEntity.IdEntityProperty, this, null);
   foreach ( var a in actionsId) { a.Action(argsId); }
     
   var actionsCreatedOn = AbstractEntity.CreatedOnEntityProperty.GetterInterceptor.GetActions();
   var argsCreatedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime>(AbstractEntity.CreatedOnEntityProperty, this, null);
   foreach ( var a in actionsCreatedOn) { a.Action(argsCreatedOn); }
     
   var actionsCreatedBy = AbstractEntity.CreatedByEntityProperty.GetterInterceptor.GetActions();
   var argsCreatedBy = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32>(AbstractEntity.CreatedByEntityProperty, this, null);
   foreach ( var a in actionsCreatedBy) { a.Action(argsCreatedBy); }
     
   var actionsChangedOn = AbstractEntity.ChangedOnEntityProperty.GetterInterceptor.GetActions();
   var argsChangedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime>(AbstractEntity.ChangedOnEntityProperty, this, null);
   foreach ( var a in actionsChangedOn) { a.Action(argsChangedOn); }
     
   var actionsChangedBy = AbstractEntity.ChangedByEntityProperty.GetterInterceptor.GetActions();
   var argsChangedBy = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32>(AbstractEntity.ChangedByEntityProperty, this, null);
   foreach ( var a in actionsChangedBy) { a.Action(argsChangedBy); }
     
   var actionsDeletedOn = AbstractEntity.DeletedOnEntityProperty.GetterInterceptor.GetActions();
   var argsDeletedOn = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,DateTime?>(AbstractEntity.DeletedOnEntityProperty, this, null);
   foreach ( var a in actionsDeletedOn) { a.Action(argsDeletedOn); }
     
   var actionsDeletedBy = AbstractEntity.DeletedByEntityProperty.GetterInterceptor.GetActions();
   var argsDeletedBy = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Int32?>(AbstractEntity.DeletedByEntityProperty, this, null);
   foreach ( var a in actionsDeletedBy) { a.Action(argsDeletedBy); }
     
   var actionsUserToken = AbstractEntity.UserTokenEntityProperty.GetterInterceptor.GetActions();
   var argsUserToken = new DataEntityPropertyGetInterceptorArgs<AbstractEntity,Guid?>(AbstractEntity.UserTokenEntityProperty, this, null);
   foreach ( var a in actionsUserToken) { a.Action(argsUserToken); }
     
  }
Back to Top
Sinnema View Drop Down
Groupie
Groupie
Avatar

Joined: 23-Mar-2009
Location: Muri AG, EU, CH
Posts: 54
Post Options Post Options   Quote Sinnema Quote  Post ReplyReply Direct Link To This Post Posted: 17-Aug-2009 at 12:54pm
Kim,
 
I've solved the problem just partly. I've written an Interface that is added to the partial Classes generated by DevForce. This interface has a 'Refetch' Method which is called when the Server Push tells our PushNotification Routine on the Client an Entity has changed. The Refetch in turn refetches the Entity and fires an Event called 'Refetched'. Our ViewModel in turn registers itself for this Event and fires a Refresh routine. This routine, which is generated, simply takes all the values from the Entity and fill the Dependency properties of WPF (pfew.... hope you understand what I've been writing here).
 
The problem is however solved only partly. We have a property called 'FullName' which is a calculated field (which has been giving us some headache already, see thread: http://www.ideablade.com/forum/forum_posts.asp?TID=1345). The Fullname is not in the EF Model and thus not in the DevForce model. We can only generate code for properties that are in the EF Model so no refresh is done for the 'FullName' property.
 
Could you give me an example on how to find all registered 'Set' Interceptors without invoking them. I would like to write a routine that gathers all 'Set' interceptors and then gets the values from the properties of these Interceptors without invoking them. What we needs is:
  • A routine to get all the 'Set' interceptors (including our 'FullName' interceptor) and...
  • a routine to get the values from the properties without invoking any interceptors using the values from the previous routine
I guess that our 'FullName' interceptor we wrote for a calculated field will fire it's 'BeforeGet' too when we try to get it's value.
 
Regards,
Paul Sinnema
Diartis AG
 


Edited by Sinnema - 17-Aug-2009 at 1:04pm
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: 18-Aug-2009 at 10:01am
Hi Paul,
 
Entity properties have both a GetterInterceptor and a SetterInterceptor (except for NavigationListEntityProperties, which don't have setters) so you can always obtain the actions from the appropriate interceptor.  Calling setter actions is a little tricky, however, since you need to determine what property value, if any, you pass to the action.
 
Here's how to obtain and invoke the setter actions for a property.  
 
      var actions = AbstractEntity.IdEntityProperty.SetterInterceptor.GetActions();
 
      // Now set up arguments and invoke -
      var args = new DataEntityPropertySetInterceptorArgs<AbstractEntity, int>(AbstractEntity.IdEntityProperty, this, null);
      args.Value = ???????
      args.Tag = "dummy call";
      foreach (var a in actions) {
        a.Action(args);
      }
 
I'm not really sure if this is what you need, though.  You said you want to "get the values from the properties without invoking any interceptors", which may be something entirely different.  Setter interceptor actions are code that runs before or after a property value is set, but the value comes from elsewhere.  Getter actions would be the code run before or after the value is obtained from the "backing store".  In both cases, if you invoke the interceptor actions separate from the actual property getter or setter, then you need to be careful with the arguments received by the action (e.g., post-get actions will not have an args.Value since the actual get wasn't performed).
 
It is possible to temporarily "skip" actions.  Using AddSkipKey and RemoveSkipKey on the interceptor you can indicate the named action to be skipped.  You must provide a key name on each action, and to add or remove multiple actions you need to call the method for each key.
 
      // Assume an action with a key name of "A"
      AbstractEntity.IdEntityProperty.GetterInterceptor.AddSkipKey("A");
      // Action won't be called when do the get now
      var name = anEntity.Id;
      // Add the action back
      AbstractEntity.IdEntityProperty.GetterInterceptor.RemoveSkipKey("A");
 
I'm not certain how the performance is with this, so you may need to do some timings if this is in a performance critical piece of your code. 
 
Another option is to use the Tags property in the getter or setter arguments to indicate what the action should do.  You could set the Tag when building the arguments, as shown at the beginning of this post.

 
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down