locked
ChangeInterceptor doesn't fire when entities are changed by my Service Operation. RRS feed

  • Question

  • I defined Change Interceptor and Service Operation in my service class like this.

    [WebInvoke]
    public void ChangeProducts(int id)
    {
        Products product = (from p in this.CurrentDataSource.Products
                           where p.ProductID == id
                           select p).First();
    
        product.Title = "Test";
        this.CurrentDataSource.SaveChanges();
    }
    
    [ChangeInterceptor("Products")]
    public void OnChangeProducts(Products p, UpdateOperations op)
    {
        log.Debug("OnChangeProducts fired.");
    }
    


    This Change Interceptor works perfectlly when updated using default uri based operations.
    But it doesn't fire when updated by Service Operation.

    Is this behavior correct?
    I want to use Change Interceptor as Trigger of database.
    Tuesday, June 9, 2009 3:31 AM

Answers

  • I think the behavior is correct because your are accessing to the entities through this.CurrentDataSource (that is a Entity Framework context) in the service operator and the change interceptor catchs the HTTP verbs over a entity set through a rest service (add = POST, delete = DELETE, update = PUT,...).

    When you access to the entities through this.CurrentDataSource, you are not using http verbs, then the change interceptor will catch nothing.

    It could be better to use the INotifyPropertyChanges methods that all entities implement.

    For example, in this example code:

            [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
            [global::System.Runtime.Serialization.DataMemberAttribute()]
            public int IdMaster
            {
                get
                {
                    return this._IdMaster;
                }
                set
                {
                    this.OnIdMasterChanging(value);
                    this.ReportPropertyChanging("IdMaster");
                    this._IdMaster = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
                    this.ReportPropertyChanged("IdMaster");
                    this.OnIdMasterChanged();
                }
    You can see that this property of the Master entity has a this.OnIdMasterChanged();

    that has this signature: partial void OnIdMasterChanged();

    You can use this method and suscribe to this change events to have your triggers. But, if you like a trigger in the data base, you must use a trigger IN your database, not in your business logic layer...

    To use this method, you must extend the partial class of the Master entity:

    public partial class Master 
        {
            partial void OnIdMasterChanged()
            {
                OnPropertyChanged("IdMaster");
            }
        }
    And then, you can subscribe to this event.

    I hope that this will be helpful for your purposes... :)

    Tuesday, June 9, 2009 2:33 PM

All replies

  • I remember seeing this issue back then, and I remember we've came to the conclusion that if you defined the service op, you have access to the change interceptor so it's up to you to call them. We can certainly look at the issue again if you can provide more detail of why calling the interceptors won't work for you.

    Regards,

    PQ
    Peter Q. http://blogs.msdn.com/peter_qian
    Tuesday, June 9, 2009 8:06 AM
    Answerer
  • I think the behavior is correct because your are accessing to the entities through this.CurrentDataSource (that is a Entity Framework context) in the service operator and the change interceptor catchs the HTTP verbs over a entity set through a rest service (add = POST, delete = DELETE, update = PUT,...).

    When you access to the entities through this.CurrentDataSource, you are not using http verbs, then the change interceptor will catch nothing.

    It could be better to use the INotifyPropertyChanges methods that all entities implement.

    For example, in this example code:

            [global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
            [global::System.Runtime.Serialization.DataMemberAttribute()]
            public int IdMaster
            {
                get
                {
                    return this._IdMaster;
                }
                set
                {
                    this.OnIdMasterChanging(value);
                    this.ReportPropertyChanging("IdMaster");
                    this._IdMaster = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
                    this.ReportPropertyChanged("IdMaster");
                    this.OnIdMasterChanged();
                }
    You can see that this property of the Master entity has a this.OnIdMasterChanged();

    that has this signature: partial void OnIdMasterChanged();

    You can use this method and suscribe to this change events to have your triggers. But, if you like a trigger in the data base, you must use a trigger IN your database, not in your business logic layer...

    To use this method, you must extend the partial class of the Master entity:

    public partial class Master 
        {
            partial void OnIdMasterChanged()
            {
                OnPropertyChanged("IdMaster");
            }
        }
    And then, you can subscribe to this event.

    I hope that this will be helpful for your purposes... :)

    Tuesday, June 9, 2009 2:33 PM
  • Peter,

    I see. This isn't expected behavior and normaly this case should works, right?

    In this case I expected output the log message of OnChangeProduct method.
    Of course, this logging works perfectly other situation(e.g called by http based operation).

    In addition, the ChangeProducts method works perfectly too (Updated product.Title was "Test").
    But Change Interceptor does not fired when using Service Operation.

    Tuesday, June 9, 2009 2:50 PM
  • Vicente,

    Thank you for the example and advice.

    > the change interceptor catchs the HTTP verbs over a entity set through a rest service

    Well.. I certainly think so.
    But I could not find the details of specification.
    Where did you know it?

    > if you like a trigger in the data base, you must use a trigger IN your database, not in your business logic layer...

    Yes, I think so.
    But I want to use user identity in my logic with SQL Server authentication mode.

    Meanwhile, I'll try to implement the logic based on your example.
    Thanks again :)

    #I don't speak English very well, so please excuse me for any mistakes I might have made.
    Tuesday, June 9, 2009 3:53 PM
  • You can find more information here  : http://msdn.microsoft.com/en-us/library/system.data.services.changeinterceptorattribute.aspx

    In the Remarks you can read that : The ADO.NET Data Services provides the infrastructure required for service developers to write per-entity change processing rules and validation.

    Then, the change interceptor method will only catch changes provocated by the ADO.NET data service :)

    Thanks :)
    Tuesday, June 9, 2009 4:10 PM
  • Hi blms,

    What I meant is, in this case the behaviour is expected and it's up to you to call the change interceptors, just as Vicente's example shows.

    Regards,

    PQ

    Peter Q. http://blogs.msdn.com/peter_qian
    Tuesday, June 9, 2009 6:17 PM
    Answerer
  • You should think of the service operation as if it were a 'black box' to the ADO.NET Data Services runtime. It calls your code, you do whatever it is that you want to happen, and that's it. It won't fire query or change interceptors.

    Service operations are often referred as an 'escape hatch' for the service author when the other mechanisms aren't sufficient. Trying to combine them would get pretty convoluted pretty quickly.
    Matt Meehan, ADO.NET Data Services (Astoria)
    Tuesday, June 9, 2009 6:23 PM
    Moderator
  • Matt, this two phrases about that are a big true about ADO.NET data services :)

    Thanks!

    Tuesday, June 9, 2009 9:55 PM