locked
Persisting Object Graphs, Client vs. Server RRS feed

  • Question

  • Context: Ef 4.1, Code First, Pocos, DbContext, DataService via WCF, DataServiceContext on the client

    If I have a simple model, say a User object with a many to many relationship to a Role object (noting that this uses tables with names Users, Roles, and UserRoles as the junction table), it seems that the amount of client code required to persist this object graph is unnecessary compared to the same scenario on the server. 

    When I have a single user with one role and wish to persist both objects from client code I am required to add both objects to the DataServiceContext (.Add(), then AddLink()), before I call .SaveChanges(). The larger the object graph gets, the more difficult it is to teach the context about my changes.

    On the server I merely add the User object to the context, call SaveChanges(), and everything persists.

    Why the difference? Am I doing something incorrectly?

    If this is indeed the correct way to proceed, I'm wondering the feasibility of doing some basic change tracking client side, serializing the object graph to the server myself, and attaching it to the server context to be persisted there in full. Does that sound viable?

    Terry

    Friday, October 21, 2011 12:48 PM

Answers

  • Hey Terry,

    The DataServiceContext is different as compared to the EF ObjectContext and DbContext. Each have their own method of tracking objects to understand the changes that have occurred.

    For the DataServiceContext you explicitly tell it how the objects are related other wise we don't know. When we serialize out the ATOM payload to send to the server we will serialize out links and such but only when the they have been added. That is why you have to call AddLink, SetLink etc. We don't want to just add things into the payload automagically.

    With the EF I believe that if you add it into the navigation property it will determine that a new link will be required to add and they will add it. So essentially they are making that "SetLink" or "AddLink" call automatically instead. That is the difference between the two.

    When the Data Services team was designing the client we wanted an explicit api. Meaning we didn't want to do automatic things like the EF does above. The reason why we did this is because if you give people the primitives for the client then a nicer layer over the top can be made later or done by customers. Also creating the automagic api is quite complicated due tracking the whole state graph and so we opted for something simpler.

    Likely your next question is how would I go and build that next layer, I'm not sure I have the clearest answer there. Here are some ways that I can think of.

    Create your own collection class to use for navigation properties (likely it should derive from observableCollection by the way) and have a way to attach the context that this collection when the object is attached to a context, then whenever Add or remove are called simply call AddLink and RemoveLink. For reference links you would need to modify the code generation so that when set calls are made to the reference property it will make a call to the context to SetLink. Again you would have to have a context associated with the object.

    To deal with how to connect an entityinstance to the context, typically there are methods AddCustomer on the context, you can simply update the generation here to set the context on an internal property then the tracking (AddLink, SetLink, RemoveLink) can kick in.

    Code gening different objects can be a pain. But luckily another team mate of mine has put together some t4 templates to generate Data Services Client Code. Here is a post on this.

    http://blogs.msdn.com/b/phaniraj/archive/2011/03/04/t4-templates-for-wcf-data-services-part-ii-client-code-gen.aspx

    So you could modify the code generation for the GenerateEntityTypes.tt and GenerateDataServiceContext.tt to do what you want.

    Thanks,

    Chris Robinson - OData Test Team.


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, October 21, 2011 3:19 PM
    Moderator

All replies

  • Hey Terry,

    The DataServiceContext is different as compared to the EF ObjectContext and DbContext. Each have their own method of tracking objects to understand the changes that have occurred.

    For the DataServiceContext you explicitly tell it how the objects are related other wise we don't know. When we serialize out the ATOM payload to send to the server we will serialize out links and such but only when the they have been added. That is why you have to call AddLink, SetLink etc. We don't want to just add things into the payload automagically.

    With the EF I believe that if you add it into the navigation property it will determine that a new link will be required to add and they will add it. So essentially they are making that "SetLink" or "AddLink" call automatically instead. That is the difference between the two.

    When the Data Services team was designing the client we wanted an explicit api. Meaning we didn't want to do automatic things like the EF does above. The reason why we did this is because if you give people the primitives for the client then a nicer layer over the top can be made later or done by customers. Also creating the automagic api is quite complicated due tracking the whole state graph and so we opted for something simpler.

    Likely your next question is how would I go and build that next layer, I'm not sure I have the clearest answer there. Here are some ways that I can think of.

    Create your own collection class to use for navigation properties (likely it should derive from observableCollection by the way) and have a way to attach the context that this collection when the object is attached to a context, then whenever Add or remove are called simply call AddLink and RemoveLink. For reference links you would need to modify the code generation so that when set calls are made to the reference property it will make a call to the context to SetLink. Again you would have to have a context associated with the object.

    To deal with how to connect an entityinstance to the context, typically there are methods AddCustomer on the context, you can simply update the generation here to set the context on an internal property then the tracking (AddLink, SetLink, RemoveLink) can kick in.

    Code gening different objects can be a pain. But luckily another team mate of mine has put together some t4 templates to generate Data Services Client Code. Here is a post on this.

    http://blogs.msdn.com/b/phaniraj/archive/2011/03/04/t4-templates-for-wcf-data-services-part-ii-client-code-gen.aspx

    So you could modify the code generation for the GenerateEntityTypes.tt and GenerateDataServiceContext.tt to do what you want.

    Thanks,

    Chris Robinson - OData Test Team.


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, October 21, 2011 3:19 PM
    Moderator
  • This is precisely the information we were looking for Chris, thx. I'm currently reviewing the T4 code from your ninja team mate and its likely something we can use as a starting point, as you recommended.

    Much obliged!

    Terry

    Friday, October 21, 2011 4:24 PM