locked
How to use computed properties on OData client objects RRS feed

  • Question

  • I've got an OData client in a Silverlight for Windows Phone project. One of the client entities is something like this:

            class Person {
                private string _name;
                private DateTime? _diedOn;
    
                [DataMember]
                public DateTime? DiedOn {
                    get { return _diedOn; }
                    set { _diedOn = value; OnPropertyChanged("DiedOn"); }
                }
    
                [DataMember]
                public string Name {
                    get { return _name; }
                    set { _name = value; OnPropertyChanged("Name"); }
                }
    
                public bool IsDead {
                    get { return _diedOn != null; }
                }
            }
    
    


    This works fine. But how can I do data binding via INotifyPropertyChanged on the computed IsDead property? If I add the code below...

                partial void OnDiedOnChanged() {
                    OnPropertyChanged("IsDead");
                }
    
    


    ...then when I attempt to save a new Person to the database I get "InvalidOperationException - The closed type Person does not have a corresponding IsDead settable property." I've tried adding a setter to IsDead that does nothing - no code inside - but then I get a more cryptic exception. I tried adding an [IgnoreDataMember] attribute to IsDead but this has no effect and shouldn't be necessary since the data contract is opt-in.

    Friday, October 14, 2011 12:01 AM

Answers

  • Now that I know this isn't just a Windows Phone issue, I was able to find a solution by searching about Silverlight. The solution works in my Silverlight client, though I haven't tested it in the Windows Phone client. A solution can be found here:

    http://blogs.msdn.com/b/phaniraj/archive/2008/12/11/customizing-serialization-of-entities-in-the-ado-net-data-services-client-library.aspx

    I made my calculated property public so data-binding would work. I added the custom [DoNotSerialize] attribute to it. Finally, it appears that the client-side property must have a setter; in my case the setter is a stub with nothing inside it. For Windows Phone, it is important to re-attach the special event handler to the data context each time the data context is deserialized; the OnContextCreated partial method is not called in that case.

    I'm surprised there isn't a more straightforward approach toward solving this problem. It seems to me that since the data contract is opt-in none of this should be necessary.


    • Marked as answer by JustinMag Monday, October 17, 2011 4:23 PM
    • Edited by JustinMag Monday, October 17, 2011 4:55 PM
    Monday, October 17, 2011 4:23 PM

All replies

  • Hi JustinMag,

    Welcome!
    We will do some more pending research  about your problem and come back as soon as possible, Thanks for understanding.
    Have a nice day.


    Alan Chen[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, October 14, 2011 7:23 AM
    Moderator
  • Hi,

    To hook up the INotifyPropertyChanged you would need to add a setter which calls the OnPropertyChanged method.

    To remove the property from serialization (when you call SaveChanges) currently no attribute will actually help. You will need to make the property internal/private for that to work. Otherwise the serialization will try to write out all the public properties.

    Thanks,


    Vitek Karas [MSFT]
    Friday, October 14, 2011 9:29 AM
    Moderator
  • I can't get it to work. If I change the computed property to internal and do this...

            internal bool IsDead {
                get { return _diedOn != null; }
            }
    
            partial void OnDiedOnChanged() {
                OnPropertyChanged("IsDead");
            }
    
    


    ...then when I submit changes I get the error "The closed type Person does not have a corresponding IsDead settable property. Well of course, since that property is computed. If I add a setter to the computed property that does nothing or is empty, I get the same error. If I try this approach...

            internal bool IsDead {
                get { return _diedOn != null; }
                set { OnPropertyChanged("IsDead"); }
            }
    
            partial void OnDiedOnChanged() {
                IsDead = true; // value is ignored
            }
    
    

    ...I get the same error. I tried actually storing the value of the computed property (ugly) like this but get the same error.

            bool _isDead;
    
            internal bool IsDead {
                get { return _isDead; }
                set { _isDead = value; OnPropertyChanged("IsDead"); }
            }
    
            partial void OnDiedOnChanged() {
                IsDead = DiedOn != null;
            }
    
    


     

     



    Friday, October 14, 2011 7:54 PM
  • The exception is happening after BeginSaveChanges. Below is the top of the call stack. I noticed the "ignoreMissingProperties" parameter. I'm not sure what this represents exactly, but when I tried setting _dataContext.ignoreMissingProperties to true (it is false by default) the error still happens. So I'm still totally stuck figuring out how to use client-side-only properties that support INotifyPropertyChanged.

    System.Data.Services.Client.dll!System.Data.Services.Client.ClientType.GetProperty(string propertyName, bool ignoreMissingProperties) + 0x2e bytes

    System.Data.Services.Client.dll!System.Data.Services.Client.BindingEntityInfo.GetPropertyValue(object source, string sourceProperty, out System.Data.Services.Client.BindingEntityInfo.BindingPropertyInfo bindingPropertyInfo) + 0x3e bytes

    System.Data.Services.Client.dll!System.Data.Services.Client.BindingObserver.OnPropertyChanged(object source, System.ComponentModel.PropertyChangedEventArgs eventArgs) + 0x31 bytes

    WindowsPhone.dll!WindowsPhone.GtdDataService.Person.OnPropertyChanged(string property) Line 992 + 0x12 bytes C#

    WindowsPhone.dll!WindowsPhone.GtdDataService.Person.IsDead.set(string value) Line 21 + 0x13 bytes C#

    WindowsPhone.dll!WindowsPhone.GtdDataService.Person.OnDiedOnChanged() Line 25 + 0x13 bytes C#

    WindowsPhone.dll!WindowsPhone.GtdDataService.Person.DiedOn.set(System.DateTime? value) Line 778 + 0x6 bytes C#

    Saturday, October 15, 2011 3:43 PM
  • I created a new Solution with the bare bones code needed to show this problem. If anyone wants to look at it let me know. I also discovered two things:

    1. If the accessor to the computed property is "internal" then Silverlight can't bind to it.
    2. Although I'm getting an exception in the EndSaveChanges method, it appears the data is actually being saved to the database.

    So I'm still stumped here. How can you extend the oData client classes with calculated properties that support data binding on Windows Phone?

    Sunday, October 16, 2011 6:08 PM
  • I created a regular Silverlight client (not Windows Phone) and the same problem happens.
    Monday, October 17, 2011 3:39 PM
  • Now that I know this isn't just a Windows Phone issue, I was able to find a solution by searching about Silverlight. The solution works in my Silverlight client, though I haven't tested it in the Windows Phone client. A solution can be found here:

    http://blogs.msdn.com/b/phaniraj/archive/2008/12/11/customizing-serialization-of-entities-in-the-ado-net-data-services-client-library.aspx

    I made my calculated property public so data-binding would work. I added the custom [DoNotSerialize] attribute to it. Finally, it appears that the client-side property must have a setter; in my case the setter is a stub with nothing inside it. For Windows Phone, it is important to re-attach the special event handler to the data context each time the data context is deserialized; the OnContextCreated partial method is not called in that case.

    I'm surprised there isn't a more straightforward approach toward solving this problem. It seems to me that since the data contract is opt-in none of this should be necessary.


    • Marked as answer by JustinMag Monday, October 17, 2011 4:23 PM
    • Edited by JustinMag Monday, October 17, 2011 4:55 PM
    Monday, October 17, 2011 4:23 PM