Related entries and feeds: links and link expansion

DB Blogs

While going through application scenarios for the ADO.NET Data Services Framework (Project Astoria) one of the first things we noticed is that data-centric applications usually want to bring down graphs of related resources in each interaction with the server. For example, if you are retrieving a resource that represents an “Event”, you may want to also bring in the set of related Contact resources that are invited or the “Venue” resource where the event will take place. This write up briefly describes how we model associations between resources as Atom links and proposes a usage pattern of the atom:link element to support retrieving resource graphs in a single response. We’re looking for feedback on the approach and also to get folks thinking about inlined content and whether it should be considered an extension to Atom.

More context on Astoria support for Atom here:

http://blogs.msdn.com/astoriateam/archive/2008/02/13/atompub-support-in-the-ado-net-data-services-framework.aspx

1. Links for modeling associations between resources

Related resources can be seen at the instance level as “links” in Atom terms. Of course, from the data application development perspective, it’s interesting to make this discoverable at the service description (schema) level. In Astoria data services the underlying model is the Entity Data Model (EDM), which describes data in terms of “Entities” (instances of Entity Types) and associations between entities. In the context of the Atom interface, Entities are mapped to entries and Associations to links. So by looking at the service description a developer can discover the links that will be present in an entry of a given type.

We model related entries or feeds using a link with a “rel” attribute of “related”, and with a “type” of either “application/atom+xml;type=feed” or “application/atom+xml;type=entry” depending on the cardinality of the other end of the association.

One tricky aspect is that we need to indicate which association it is. At the model level we have a “navigation property” that identifies the starting “end” of the association (e.g. “Attendees”, “Venue”). We currently put that name in the “title” attribute of the link. That solution is not perfect, as we try not to overload constructs that are for human-readable content. However, the alternative is to use a custom attribute, and we’ve been trying not to introduce custom attributes unless absolutely needed. Another option would be to use different “rel” values to specify the relationship, which feels natural but makes it much less likely that generic processors will be able to do something interesting with it.

Do these trade-offs sound reasonable? Is any of the other options more appropriate?

Continuing with the Events sample, this is what an entry (/Events(456)) with links looks like:

<entry xml:base=”http://localhost:81/EventsSample/”        xmlns:d=”http://schemas.microsoft.com/ado/2007/08/dataservices”       xmlns:m=”http://schemas.microsoft.com/ado/2007/08/dataservices/metadata”       xmlns=”http://www.w3.org/2005/Atom”       m:type=”EventsSample.Event”>

  <id>http://localhost:81/EventsSample/Events(456)</id>

  <title type=”text”></title>

  <updated>2008-02-17T02:52:38Z</updated>

  <author>

    <name />

  </author>

  <link rel=”edit” title=”Event” href=”Events(456)” />

  <link rel=”related” type=”application/atom+xml;type=entry” title=”Venue” href=”Events(456)/Venue” />

  <link rel=”related” type=”application/atom+xml;type=feed” title=”Attendees” href=”Events(456)/Attendees” />

  <content type=”application/xml”>

    <d:EventID m:type=”Int32″>456</d:EventID>

    <d:Name>Big Party</d:Name>

    <d:NoteToAttendees>It’s going to be a great party!</d:NoteToAttendees>

    <d:DateAndTime m:type=”DateTime”>2008-03-05T06:00:00</d:DateAndTime>

  </content>

</entry>

From the data modification perspective, links pointing to other resources in the service can be specified in the payload of POST and PUT operations, to establish links between the resource being manipulated and other existing resources.

2. Expanding links inline

As I summarized at the beginning of this note, we want to enable clients to request whole sub-graphs of data starting at some resource or set of resources. There are two aspects that need to be addressed: how does the client indicate that it wants one or more links expanded and how are the expanded links represented on the response.

How link expansion is requested is outside of the atom-syntax problem space, so I’ll just briefly state what we currently do in case you have an opinion: data services support the query string option “$expand” to request link expansion. So you could say “/Events?$expand=Attendees ” to retrieve all Events and all contacts that are attendees for each of them, or “/Events(456)?$expand=Attendees” to retrieve a single event (with key 456) and its attendees. Expand syntax allows for deep expands such as “Attendees/BestFriend” (expand Attendees, and on the expanded entry(es) expand BestFriend) and wide expands such as “Venue, Attendees/BestFriend” meaning expand two immediate links, and for the Attendees one further expand its BestFriend link.

For representing expanded links we put the expanded content inside the link element itself. According to section 4.2.7 of RFC 4287:

“The “atom:link” element defines a reference from an entry or feed to  a Web resource.  This specification assigns no meaning to the content (if any) of this element.”

So it seems that adding content to the link element is not disallowed and at the same time it does not overlap with any existing semantics given to such construct. Based on that we thought it would be the perfect place for this information, as the link itself already contains the metadata about the link that we needed.

When a client indicates that the target of a link should be expanded, the server responds with the Atom representation of the resources pointed at by links wrapped in an <inline> element. For example, for “/Events(456)?$expand=Attendees,Venue” the response would be:

<entry xml:base=”http://localhost:81/EventsSample/”        xmlns:d=”http://schemas.microsoft.com/ado/2007/08/dataservices”        xmlns:m=”http://schemas.microsoft.com/ado/2007/08/dataservices/metadata ”      xmlns=”http://www.w3.org/2005/Atom” m:type=”EventsSample.Event”>

  <id>http://localhost:81/EventsSample/Events(456)</id>

  <title type=”text”></title>

  <updated>2008-02-17T03:01:18Z</updated>

  <author>

    <name />

  </author>

  <link rel=”edit” title=”Event” href=”Events(456)” />

  <link rel=”related” type=”application/atom+xml;type=entry” title=”Venue” href=”Events(456)/Venue”>

    <m:inline>

      <entry m:type=”EventsSample.Venue”>

        <id>http://localhost:81/EventsSample/Venues(789)</id>

        <title type=”text”></title>

        <updated>2008-02-17T03:01:18Z</updated>

        <author>

          <name />

        </author>

        <link rel=”edit” title=”Venue” href=”Venues(789)” />

        <link rel=”related” type=”application/atom+xml;type=entry” title=”SalesContact” href=”Venues(789)/SalesContact” />

        <content type=”application/xml”>

          <d:VenueID m:type=”Int32″>789</d:VenueID>

          <d:Name>The Cool Place</d:Name>

          <d:Description>Great place for parties!</d:Description>

          <d:Capacity m:type=”Int32″>1500</d:Capacity>

          <d:Type>Nightclub</d:Type>

        </content>

      </entry>

    </m:inline>

  </link>

  <link rel=”related” type=”application/atom+xml;type=feed” title=”Attendees” href=”Events(456)/Attendees”>

    <m:inline>

      <feed>

        <title type=”text”>Attendees</title>

        <id>http://localhost:81/EventsSample/Events(456)/Attendees</id>

        <updated>2008-02-17T03:01:18Z</updated>

        <link rel=”self” title=”Attendees” href=”Events(456)/Attendees” />

        <entry m:type=”EventsSample.Contact”>

          <id>http://localhost:81/EventsSample/Contacts(123)</id>

          <title type=”text”></title>

          <updated>2008-02-17T03:01:18Z</updated>

          <author>

            <name />

          </author>

          <link rel=”edit” title=”Contact” href=”Contacts(123)” />

          <link rel=”related” type=”application/atom+xml;type=entry” title=”BestFriend” href=”Contacts(123)/BestFriend” />

          <content type=”application/xml”>

            <d:ContactID m:type=”Int32″>123</d:ContactID>

            <d:FirstName>John123</d:FirstName>

            <d:LastName>Doe123</d:LastName>

            <d:EmailAddress>jd123@foo.com</d:EmailAddress>

            <d:Phone>123-456-123</d:Phone>

            <d:BirthDate m:type=”Nullable`1[System.DateTime]”>1990-04-01T00:00:00</d:BirthDate>

          </content>

        </entry>

        <entry m:type=”EventsSample.Contact”>

          <id>http://localhost:81/EventsSample/Contacts(124)</id>

          <title type=”text”></title>

          <updated>2008-02-17T03:01:18Z</updated>

          <author>

            <name />

          </author>

          <link rel=”edit” title=”Contact” href=”Contacts(124)” />

          <link rel=”related” type=”application/atom+xml;type=entry” title=”BestFriend” href=”Contacts(124)/BestFriend” />

          <content type=”application/xml”>

            <d:ContactID m:type=”Int32″>124</d:ContactID>

            <d:FirstName>John124</d:FirstName>

            <d:LastName>Doe124</d:LastName>

            <d:EmailAddress>jd124@foo.com</d:EmailAddress>

            <d:Phone>123-456-124</d:Phone>

            <d:BirthDate m:type=”Nullable`1[System.DateTime]”>1990-05-01T00:00:00</d:BirthDate>

          </content>

        </entry>

        <!– more entries for contacts that will be –>

     <!– attendees in this party –>

      </feed>

    </m:inline>

  </link>

  <content type=”application/xml”>

    <d:EventID m:type=”Int32″>456</d:EventID>

    <d:Name>Big Party</d:Name>

    <d:NoteToAttendees>It’s going to be a great party!</d:NoteToAttendees>

    <d:DateAndTime m:type=”DateTime”>2008-03-05T06:00:00</d:DateAndTime>

  </content>

</entry>

I focused on the GET operations above. We think it would be better to stay away from attempting to support full modification operations on expanded graphs. In particular, we do not handle PUT on more than one entry at a time today. We do support POSTing an expanded graph, and we simply create all the nested entries and link them to the parent entry, creating the whole graph in a single operation.

Feedback in general about this approach would be greatly appreciated.

Pablo Castro
Technical Lead
Microsoft Corporation

This post is part of the transparent design exercise in the Astoria Team. To understand how it works and how your feedback will be used please look at this post.

0 comments

Discussion is closed.

Feedback usabilla icon