locked
Creating a new resource initiates a POST request which initializes all properties RRS feed

  • Question

  • I'm consuming a set of data services with the help of a service reference and using the call

    svc.AddToXXX(new XXX() {//some properties})

    to create a new resource of type XXX. However unlike the update resource call, which uses a MERGE request, this results in a POST request which ends up initializing all unspecified properties with their default value. Is there a way to force it to only initialize the specified properties?

     

    Thanks,
    Nitesh Baranwal

    Monday, July 26, 2010 9:05 PM

Answers

  • Hi,

    There is no easy way to do that. You would have to implement property level tracking on your own. Basically either the entity itself or something else needs to remember which properties were modified and which were not.

    You can either modify the entity (the XXX class) and remember which properties changed in some private variable. Then you can use that variable in the code Phani posted to only send the properties which were modified.

    The other approach is if your entity supports INotifyPropertyChange, then something external can sign up for notification when properties are modified and track this outside of the entity. But the overall outcome is the same.

    You can't really use the modified types in this case, would be too cumbersome.

    Thanks,


    Vitek Karas [MSFT]
    Thursday, July 29, 2010 9:33 PM
    Moderator

All replies

  • Hi,

    The right question to ask is: What should it do then? I don't know your service, but most services either store stuff as objects, in which case if you new up an object all its properties will be initialized with their default values, or the service will store data in the DB in which case inserting a row will initialize all the columns to their default values.

    So what should the properties you don't send from the client be set to?

    With MERGE it's a different story because the entity already exists and has all the properties set to something, so MERGE will only modify the properties you want (PUT would behave a lot like POST in that it would first reset everything to default and then apply your changes).

    Thanks,


    Vitek Karas [MSFT]
    Tuesday, July 27, 2010 9:12 AM
    Moderator
  • Hi Vitek,

    Thanks for your reply. I am using an ORM (NHibernate), so creating a resource through the service news up an object with all its properties initialized to their default values.

    The difference lies in that when we create a similar object in our app, we only initialize the specified properties and do not touch the other ones at all. When this object is saved, the uninitialized properties are set to NULL in DB. I was hoping I could mimic this behavior for creating a resource through data services (either through POST or any other verb, if can be used). 

     

    Thanks,
    Nitesh Baranwal

    Tuesday, July 27, 2010 2:13 PM
  • Hi,

    The behavior is going to depend on two pieces:

    #1 The client sending the values to the server. Currently our .NET client will always send all the properties on the CLR type used by it. SImply put we don't do property level tracking. So if you don't want some value to be sent to the server, the property must not be on the type. You can use different CLR type for this (type which only has the properties you want to modify). ANother approach would be to filter the payload when it goes out to the server, but that's a bit hacky. Anyway here's an example: http://blogs.msdn.com/b/phaniraj/archive/2008/12/11/customizing-serialization-of-entities-in-the-ado-net-data-services-client-library.aspx

    #2 The server uses some implementation of IUpdatable (or IDataServiceUpdateProvider). For POST the server will call IUpdatable.CreateResource and then IUpdatable.SetValue for each of the properties in the payload. So in short you need to make sure tha the CreateResource implementation sets all the properties to their default values (not nulls) for this to work.

    Thanks,


    Vitek Karas [MSFT]
    Tuesday, July 27, 2010 3:53 PM
    Moderator
  • Thanks again for your helpful tips Vitek. The blog by Phani looks interesting.

    However, both the approaches you suggest would provide a solution for a different problem - not including some specific properties in the payload or initializing some specific properties with our own default values. 

    My issue is slightly different in the sense that I am not looking at a few specific properties, but properties just not specified by the client.

    e.g. svc.AddToXXX(new XXX() { Prop1 = Val1, Prop4 = Val4, Prop5 = Val5});

    For the above new object, I do not want to send default values for Prop2 and Prop3 as would be by the POST request. So, essentially, behave like MERGE action. Like I mentioned before, using NHibernate, we will create and save this new object with NULL values for unspecified properties, Prop2 and Prop3 in the database.

    I know that we can enforce an update to use a POST request. Is there a reason why MERGE is prohibited from being used to create a resource?


    Thanks,
    Nitesh Baranwal
    Tuesday, July 27, 2010 8:33 PM
  • Hi,

    Actually I am describing the problem you have, we just don't understand each other :-)

    First about the verbs:
    POST can NOT be used to update an existing entity. Mainly because in OData POST is only allowed to a URL specifying an entity set (not a single entity).
    MERGE is intended to update a single entity. Currently we don't allow MERGE to also create that entity. I don't know the exact reasons for this decision, but POST should work just fine.

    In both cases the server will only modify the properties sent in the payload. So in fact by sending a POST and only including properties Prop1 and Prop4 in the payload, the server will not modify the other properties and leave their value to whatever default value was set by CreateResource. So for POST the server does this:
    CreateResource
    SetValue* (for all properties in the payload)

    MERGE behaves in a similar way:
    SetValue* (for all properties in the payload)

    PUT (for completeness):
    ResetResource
    SetValue* (for all properties in the payload)

    The actual problem you're running into is, that our .NET client always sends all the properties it sees on the client side class representing the entity. We don't provide a way to pick and choose which property to include in the payload. To make this work the way you want, you need to somehow force the client to only send the properties you want to modify and not include the rest.

    This can be done in to ways:
    1) Use a different CLR type on the client, type which only declared properties you want to change. That way the client library will only send those values.
    2) Use Phani's "hack" to remove the properties you don't want to modify from the payload before it's sent to the server.

    Does it make sense?

    Thanks,


    Vitek Karas [MSFT]
    Wednesday, July 28, 2010 8:59 AM
    Moderator
  • Vitek,

    Thanks for the detailed explanation of verbs and associated calls on server. It does clear the picture a lot and give me a better understanding of what the actual problem is. Though, I still do not understand how I can use any of the approaches you have mentioned dynamically -

    e.g.

    the client first calls svc.AddToXXX(new XXX() { Prop1 = Val1, Prop4 = Val4, Prop5 = Val5});

    then it calls svc.AddToXXX(new XXX() { Prop2 = Val2, Prop3 = Val3, Prop5 = Val5});

    In the above cases, there is no fixed set of properties that I want to avoid but just want to avoid the properties not specified by the client. 

    Thursday, July 29, 2010 8:53 PM
  • Hi,

    There is no easy way to do that. You would have to implement property level tracking on your own. Basically either the entity itself or something else needs to remember which properties were modified and which were not.

    You can either modify the entity (the XXX class) and remember which properties changed in some private variable. Then you can use that variable in the code Phani posted to only send the properties which were modified.

    The other approach is if your entity supports INotifyPropertyChange, then something external can sign up for notification when properties are modified and track this outside of the entity. But the overall outcome is the same.

    You can't really use the modified types in this case, would be too cumbersome.

    Thanks,


    Vitek Karas [MSFT]
    Thursday, July 29, 2010 9:33 PM
    Moderator