<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>ADO.NET Data Services Team Blog : Design Notes</title><link>http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx</link><description>Tags: Design Notes</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Design Notes: Row Count</title><link>http://blogs.msdn.com/astoriateam/archive/2008/12/15/design-notes-row-count.aspx</link><pubDate>Tue, 16 Dec 2008 00:18:34 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9222501</guid><dc:creator>dpblogs</dc:creator><slash:comments>27</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/9222501.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=9222501</wfw:commentRss><description>&lt;p&gt;One of the scenarios we have heard feedback around is the ability for the a client of a data service to determine the total number of entities in a set without having to retrieve them all. The following video discusses this scenario in more detail and some of our thoughts regarding how to make the experience better.&amp;#160; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;iframe src="http://channel9.msdn.com/posts/Andrew+Conrad/434694/player/" frameborder="0" width="320" scrolling="no" height="325"&gt;&lt;/iframe&gt;  &lt;br /&gt;&lt;a href="http://channel9.msdn.com/posts/Andrew+Conrad/Astoria-Design-Walkthrough-Introducing-count-as-an-URI-query-option/"&gt;Astoria Design Walkthrough: Introducing $count as an URI query option&lt;/a&gt;   &lt;br /&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Additional design details&lt;/strong&gt;:&lt;/p&gt;  &lt;p&gt;- The count value would be calculated after $filter expressions are applied.&amp;#160; For example /Customers?$filter=City eq 'London' would return a count value made up of &lt;strong&gt;only&lt;/strong&gt; the customers living in London and not all the customers.&lt;/p&gt;  &lt;p&gt;- If a query included $top, $skip or $orderby, the count value would be unaffected.&amp;#160; For example /Customers?$top=1&amp;amp;$skip=2&amp;amp;$orderby=Name would return the total number of customers stored by the service.&lt;/p&gt;  &lt;p&gt;- If a query includes a $expand, then the count applies to the outer entity set.&amp;#160; For example /Customers?$expand=Orders would return the number of customers stored by the service&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;What do you think?&amp;#160; Would this be useful in your data services applications?&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Mike Flasko&lt;/p&gt;  &lt;p&gt;ADO.NET Data Services, Program Manager&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9222501" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Video/default.aspx">Design Video</category></item><item><title>Alpha preview of Project Codename &amp;amp;quot;Astoria Offline&amp;amp;quot; coming soon</title><link>http://blogs.msdn.com/astoriateam/archive/2008/11/03/alpha-preview-of-project-codename-astoria-offline-coming-soon.aspx</link><pubDate>Tue, 04 Nov 2008 02:01:58 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9035676</guid><dc:creator>dpblogs</dc:creator><slash:comments>6</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/9035676.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=9035676</wfw:commentRss><description>&lt;p&gt;As we already discussed in a &lt;a href="http://blogs.msdn.com/astoriateam/archive/2008/10/22/astoria-futures-offline-enabled-data-services.aspx"&gt;previous blog post&lt;/a&gt;, one of the problem spaces related to data services we&amp;#8217;d like to take on is synchronization-capable services, enabling offline scenarios. We&amp;#8217;re still in the early stages of the project, where direction can be adjusted and good feedback can be very influential.&lt;/p&gt;  &lt;p&gt;Giving good feedback without actually having something to look at it&amp;#8217;s tricky, so we want to ship bits. Earlier at &lt;a href="http://channel9.msdn.com/pdc2008/TL08/"&gt;the PDC talk that showed this technology end-to-end for the first time&lt;/a&gt; we announced that we&amp;#8217;ll be shipping a very early release of the bits that extend our entity platform, including the Entity Framework and ADO.NET Data Services, with synchronization capabilities through integration with the Microsoft Sync Framework and a nice toolset that makes development of offline-capable applications reasonably straightforward.&lt;/p&gt;  &lt;p&gt;Here is a brief video with Waseem and myself discussing the release. Waseem was probably the most involved developer so far in this effort and has been dealing with a lot of the challenges that we hit as we put all the pieces together and build new ones on top.&lt;/p&gt;  &lt;p&gt;&lt;iframe src="http://channel9.msdn.com/posts/Andrew+Conrad/437013/player/" frameborder="0" width="320" scrolling="no" height="325"&gt;&lt;/iframe&gt;    &lt;br /&gt;&lt;a href="http://channel9.msdn.com/posts/Andrew+Conrad/Astoria-Design-Walkthrough-Alpha-preview-of-Project-Codename-Astoria-Offline-coming-very-soon/"&gt;Astoria Design Walkthrough: Alpha preview of Project Codename &amp;quot;Astoria Offline&amp;quot; coming soon&lt;/a&gt;     &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Just like the first release of &amp;#8220;Astoria&amp;#8221; almost two years ago, the goal if this alpha release is to paint the picture of what we want to build, what scenarios we consider interesting and how we want to approach the tooling around it. It&amp;#8217;s definitely not a goal to ship something that&amp;#8217;s usable in production or even something that can be used to start writing real applications now. Things will change substantially as we hear feedback from all of you.&lt;/p&gt;  &lt;p&gt;You can expect the release to ship before the end of this year, and to include both runtime and tool components for the whole thing. If you care about synchronization and offline applications, giving this release a try and sending us your thoughts, comments and suggestions would help build the right technology in this space.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.msdn.com/pablo"&gt;Pablo Castro&lt;/a&gt;     &lt;br /&gt;Software Architect     &lt;br /&gt;Microsoft Corporation&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9035676" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Video/default.aspx">Design Video</category></item><item><title>Astoria futures: offline-enabled data services</title><link>http://blogs.msdn.com/astoriateam/archive/2008/10/22/astoria-futures-offline-enabled-data-services.aspx</link><pubDate>Thu, 23 Oct 2008 04:32:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9012111</guid><dc:creator>dpblogs</dc:creator><slash:comments>17</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/9012111.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=9012111</wfw:commentRss><description>&lt;p&gt;We mentioned that we were doing some early thinking of &amp;#8220;Astoria Offline&amp;#8221; back in Mix 2008, where we even demo&amp;#8217;ed an early proof of concept. Now we&amp;#8217;ve been working on various design aspects of Data Services for its future versions, and synchronization/offline support is one of them. It&amp;#8217;s still an experimental thing with no official home or release vehicle, so this is the best time to follow the design process if you find the scenario interesting, as this is when it&amp;#8217;s easiest to influence the direction we&amp;#8217;ll go for.&lt;/p&gt;  &lt;p&gt;A short way of describing this can be: &amp;#8220;imagine you can point Visual Studio to a data service and say &amp;#8216;take it offline&amp;#8217;, and things just happen&amp;#8221;.&lt;/p&gt;  &lt;p&gt;Of course, the real world is more complicated than that :-)&lt;/p&gt; &lt;iframe src="http://channel9.msdn.com/posts/Andrew+Conrad/434554/player/" frameborder="0" width="320" scrolling="no" height="325"&gt;&lt;/iframe&gt;  &lt;br /&gt;&lt;a href="http://channel9.msdn.com/posts/Andrew+Conrad/Astoria-Design-Walkthrough-Thinking-of-a-future-with-sync--offline2/"&gt;Astoria Design Walkthrough: Thinking of a future with sync &amp;amp; offline&lt;/a&gt;   &lt;br /&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;In this first note I&amp;#8217;ll just touch on the scenarios we want to hit and go over a few guiding principles. In future posts I&amp;#8217;ll elaborate more on the details.&lt;/p&gt;  &lt;p&gt;We have many scenarios in mind for this infrastructure. The ones we&amp;#8217;re thinking of tackling first:&lt;/p&gt;  &lt;p&gt;&amp;#183; Outlook type of apps: I&amp;#8217;m sure there is a fancier way of saying this, but anyone that has used Microsoft Outlook knows what I mean. The application is basically a 1-tier app that interacts with a local (embedded) database. In the background -and independent from UI activity- 2-way synchronization with a data service (e.g. a Microsoft Exchange server) happens. Often sync&amp;#8217;ing against a database is not quite what you want&amp;#8230;&amp;#8221;Astoria Offline&amp;#8221; will let you sync against your data-service layer, where the usual business logic/validation/etc will run just like in the online path.&lt;/p&gt;  &lt;p&gt;&amp;#183; The description above sort of implies that server and client are built in collaboration, perhaps as part of the same development team. That&amp;#8217;s certainly an scenario and we can do some things easier when that&amp;#8217;s the case. But the other scenario we want to tackle is when client and server in a synchronization relationship are independent from each other (e.g. sync a service that&amp;#8217;s just available for sync on the web). &lt;/p&gt;  &lt;p&gt;&amp;#183; Local replicas of cloud-stored data: as more online services offer structured storage capabilities, and more of them use the Data Services REST interface, it becomes more interesting to be able to synchronize that data locally either for latency reduction, offline operation or other reasons.&lt;/p&gt;  &lt;p&gt;&amp;#183; Data consolidation: if you have multiple data services that expose data from a variety of sources (some databases, some online/&amp;#8221;cloud&amp;#8221; stores, some custom repositories), you may want to synchronize a slice of data of each store to a local database, and then work with the data locally.&lt;/p&gt;  &lt;p&gt;A couple of guiding principles:&lt;/p&gt;  &lt;p&gt;&amp;#183; We will stick to a simple and open interface. What that means is that while we will definitely build a nice end-to-end integrated story for Visual Studio, it will be on top of a well-documented underlying data exchange using just HTTP and known formats. Anybody with an HTTP client and enough knowledge of our sync strategy should be able to synchronize with a data service.&lt;/p&gt;  &lt;p&gt;&amp;#183; Data independence will remain there for sync as it is already for online access. Today when you access a data service the interface is the same regardless of whether the service is backed by a database, a cloud store, some custom application or whatever. With sync, the same applies. If the data service is sync-enabled you can sync with it, no matter what backs it.&lt;/p&gt;  &lt;p&gt;&amp;#183; We are targeting data services for structured stores and business applications. That implies certain level of sophistication in the shape of data, such as assuming cross-item dependencies, store-level and application-level constraints that dictate consistent states of data, the need for making partial progress during synchronization, etc. Such support does come with some extra complexity, but we think it&amp;#8217;s the right target.&lt;/p&gt;  &lt;p&gt;We&amp;#8217;re just taking on this space, so any feedback you may have is good. Are our initial scenarios interesting? Do you need this thing at all? Does the initial direction we&amp;#8217;re looking at sound reasonable?&lt;/p&gt;  &lt;p&gt;btw &amp;#8211; if you are going to be at PDC, we have a &lt;a href="http://channel9.msdn.com/pdc2008/TL08/"&gt;full talk on this&lt;/a&gt; at the event.&lt;/p&gt;  &lt;p&gt;I hope this &amp;#8220;short video&amp;#8221; format that &lt;a href="http://blogs.msdn.com/aconrad/"&gt;Andy&lt;/a&gt; wants to do for our design notes adds a good little twist and makes them more interesting.&lt;/p&gt;  &lt;p&gt;Pablo Castro    &lt;br /&gt;Software Architect     &lt;br /&gt;Microsoft Corporation     &lt;br /&gt;&lt;a href="http://blogs.msdn.com/pablo"&gt;http://blogs.msdn.com/pablo&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9012111" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Project+Codename+_2600_quot_3B00_Astoria_2600_quot_3B00_/default.aspx">Project Codename &amp;quot;Astoria&amp;quot;</category><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Video/default.aspx">Design Video</category></item><item><title>Making Feeds Friendly</title><link>http://blogs.msdn.com/astoriateam/archive/2008/09/28/making-feeds-friendly.aspx</link><pubDate>Mon, 29 Sep 2008 01:17:49 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8968222</guid><dc:creator>dpblogs</dc:creator><slash:comments>6</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/8968222.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=8968222</wfw:commentRss><description>&lt;p&gt;The goal of Astoria in V1 is to easily expose a data source using the REST approach to the web (with support for concurrency, versioning, auth, etc).&amp;#160; In general, the way we think about serialization formats (ie. how we represent entites on the wire) is that they are a tool and an application will select the best tool (ie. serialization format) for the job.&amp;#160; For example, AJAX developers will likely opt to interact with Astoria services using our JSON serialization format while .NET developers may choose AtomPub since our .NET client library uses that format under the covers.&amp;#160;&amp;#160; In V1, Astoria supports using the AtomPub and JSON formats as a general purpose data exchange representation and has fixed requirements regarding how an entity is to be represented using the formats.&amp;#160;&amp;#160; While this works well for a number of app scenarios, we&amp;#8217;ve received feedback regarding use cases (one example being mashups) where an app/tool/etc is written to consume general Atom feeds and can then make assumptions about the data given (ie. what the title is, who was the author, etc).&amp;#160; This area of thinking/exploration (uses of feeds in the wild and how to make Astoria&amp;#8217;s feeds just work in certain scenarios) has been dubbed by our team as &amp;#8220;Friendly Feeds&amp;#8221;. &lt;/p&gt;  &lt;p&gt;One of the specific scenarios we&amp;#8217;ve discussed is a mapping site (ie. virtual earth, etc) which accepts feeds as an input and then displays the feed information on the appropriate spots on the map.&amp;#160; In this scenario the mapping site would pull metadata from the well known Atom elements (title, author, content, etc), but also needs to locate geo information to be able to determine where on the map to place the data.&amp;#160; For this we&amp;#8217;ve seen some feeds start to embed micro formats in feeds such as &lt;a href="http://www.georss.org/"&gt;georss&lt;/a&gt;.&amp;#160; To make this more concrete, imagine being able to drive &lt;a href="http://blogs.msdn.com/keithkin/archive/2007/04/28/virtual-earth-api-georss-layers.aspx"&gt;the example shown here&lt;/a&gt; via an Astoria feed.&amp;#160; &lt;/p&gt;  &lt;p&gt;We are still thinking about this space, but right now the high level goals we see as necessary for such a feature are listed below.&amp;#160; What do you think? &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Goals:&lt;/b&gt; &lt;/p&gt;  &lt;p&gt;&amp;#183; Enable feeds to be easily customized such that a generic feed viewer can render an Astoria feed &lt;/p&gt;  &lt;p&gt;&amp;#183; The feed customization approach could be applied to feed-based formats other than Atom.&amp;#160; JSON serialization will not support this type of &amp;#8220;Friendliness&amp;#8221; &lt;/p&gt;  &lt;p&gt;&amp;#183; Enable entity properties to be serialized as microformats within a feed &lt;/p&gt;  &lt;p&gt;&amp;#183; Serialization customizations &amp;#8220;just work&amp;#8221; with V1 data service client libraries wherever possible.&amp;#160; Changes that require a protocol version number increase should be explicit and off by default &lt;/p&gt;  &lt;p&gt;&amp;#183; The ADO.NET Data Services client library (for .NET Fx and Silverlight) would be able to roundtrip a &amp;#8220;Friendly Feed&amp;#8221; in the same way the client roundtrips data in Astoria V1.&amp;#160;&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&amp;#183; We would also teach our code generation tools to understand such &amp;#8220;Friendly Feeds&amp;#8221; and generate the appropriate clients. &lt;/p&gt;  &lt;p&gt;&amp;#183; It is NOT a goal to make ADO.NET Data Services into a blog server -- there are better domain-specific tools for that&amp;#160; &lt;/p&gt;  &lt;p&gt;Oh, I almost forgot&amp;#8230;&amp;#8230;a nice benefit is that with this feature Astoria Atom feeds could likely be made directly viewable in IE.&amp;#160; Folks who have inspected AtomPub-based feeds in web browsers today &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/12/10/viewing-data-services-responses-using-atom-serialization-in-internet-explorer.aspx"&gt;know what I&amp;#8217;m referring to&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;Below is a short video we recorded that discussed this area as well.&amp;#160; Andy (Astoria Dev Lead) got a new camera and came up with the idea of adding these videos. &lt;strong&gt;Let us know what you think of having short video snippets such as this to accompany our design notes as we blog them&lt;/strong&gt;....    &lt;br /&gt;    &lt;br /&gt;&lt;iframe src="http://channel9.msdn.com/posts/Andrew+Conrad/429881/player/" frameborder="0" width="320" scrolling="no" height="325"&gt;&lt;/iframe&gt;    &lt;br /&gt;&lt;a href="http://channel9.msdn.com/posts/Andrew+Conrad/Astoria-Design-Walkthrough-Friendly-Feeds-Part-1/"&gt;Astoria Design Walkthrough: Friendly Feeds Part 1&lt;/a&gt;    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;In a future post, I'll include some specific examples to show how we will enable this in our server runtime and client libraries. &lt;/p&gt;  &lt;p&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;Cheers,   &lt;br /&gt;Mike Flasko &lt;/p&gt;  &lt;p&gt;ADO.NET Data Services, Program Manager&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8968222" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Video/default.aspx">Design Video</category></item><item><title>Merge vs. Replace Semantics for Update Operations</title><link>http://blogs.msdn.com/astoriateam/archive/2008/05/20/merge-vs-replace-semantics-for-update-operations.aspx</link><pubDate>Tue, 20 May 2008 19:45:49 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8523803</guid><dc:creator>dpblogs</dc:creator><slash:comments>15</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/8523803.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=8523803</wfw:commentRss><description>&lt;p&gt;So far Astoria has used “merge” semantics for update. That is, an “update” operation (an HTTP PUT request) replaces the values of the properties for the target entity that are specified in the input payload; this applies both to properties and links. If a property or link is not present in a PUT operation, it means “leave it with its current value”. To null-out a property or link it has to be present in the PUT body and has to have a null marker (null attribute for properties, null href for links). &lt;p&gt;While supporting merge is important and will remain part of Astoria, there are scenarios where we need “replace” semantics. That is, an operation where the entity properties are entirely replaced by those in the request body, even if the request body is partial. In particular, the AtomPub protocol requires PUT to have replace semantics. &lt;p&gt;&lt;b&gt;Operation semantics&lt;/b&gt; &lt;p&gt;In a “replace” operation, each property in the target entity either takes the value specified in the payload (if any) or its default value. The meaning of “default value” for a “replace” operation is server implementation-specific. The Astoria server will likely use the CLR default values for each value type and null for references.  &lt;p&gt;For operations on primitive values (e.g. PUT /Customers(123)/CompanyName), there is no practical difference between “merge” and “replace”.  &lt;p&gt;A “replace” operation, just like a “merge” operation, cannot specify different values in the key properties. That is, keys remain non-updatable in “replace”. Similarly, a “replace” operation is subject to the same requirements from the ETags perspective as a “merge” operation. &lt;p&gt;About links inside entity payloads: “replace” operations replace the properties in entities themselves, not its links. If you include a link in a “merge” or “replace” operation we’ll wire it up, but if you don’t it’ll maintain the existing links. &lt;p&gt;About directly-addressed links: links are currently atomic values (just the link itself), so there is no difference between replacing it and merging it when operating on link resources (e.g. /$links/…). Astoria servers should support both operations but keep identical semantics. The rest of this discussion won’t touch on links as stand-alone resources anymore.  &lt;p&gt;Note that these operational semantics are the same regardless of the actual format (Atom, JSON, etc.) used to represent the resources being exchanged. &lt;p&gt;Finally, this in general does not apply to service operations, as the meaning of service operations is service-defined. It’s interesting to think about whether in the Astoria server library we introduce some mechanism to allow it to expose a MERGE-enabled URL, but that would still require the user-implementation to make sense of it.  &lt;p&gt;&lt;b&gt;HTTP interface&lt;/b&gt; &lt;p&gt;AtomPub specifically requires HTTP PUT to mean replace. So we adjusted the way Astoria interprets PUT to mean “replace”. &lt;p&gt;In order to request a “merge” operation we have two options: &lt;p&gt;1. Introduce a new HTTP method, “MERGE”, and rely on verb tunneling (POST + a header) for the cases where custom methods are not allowed. There has been talk about a PATCH verb in multiple circles including the AtomPub community, but it seems to be going in a somewhat different direction. &lt;p&gt;2. Introduce a new custom header, “DataServices-Merge” or something, that when set to “1” in a PUT request indicates that the server should merge the body with the server entity instead of replacing it. &lt;p&gt;While we’re not thrilled with the idea of introducing a new HTTP method, overloading PUT with an extra header seems to be very problematic. If anything else, a server that does not support “merge” through headers would see PUT as a regular “replace” request and perform an operation that’s not what the client expected. Also other things break. For example, if a server sees an actual MERGE request and cannot handle it then it can respond with 405 – method not supported. &lt;p&gt;So we’re leaning toward MERGE and tunneling (we already support tunneling for PUT/DELETE in Astoria servers and clients). &lt;p&gt;Responses to “merge” and “replace” requests are identical. &lt;p&gt;&lt;b&gt;.NET client&lt;/b&gt; &lt;p&gt;Using “merge” from the client has several advantages: &lt;p&gt;1. The server does not know what a client considers a “whole” entity. The client may be using entity types that contain a subset of the properties of the server-side version, either due to versioning mismatches or because the client is not interested in all of the properties.  &lt;p&gt;2. The client is pretty much required to use “merge” to make links work. Since an entity might have been brought down to the client without its related entities expanded, one or more links won’t be present, and if we used replace we’d lose information on the server. &lt;p&gt;Based on the above, it would seem that the client should do “merge”, which will effectively result in “replacing the subset the client knows about” because the client always sends all the fields. &lt;p&gt;The problem now is servers that don’t implement “merge” operation.&amp;nbsp; One option is to require “merge” for the client to work, but that leaves too many interesting scenarios out. Since we already had a SaveChangesOptions enum that’s used as an argument to SaveChanges/BeginSaveChanges, we introduced SaveChangeOptions.UpdateAsReplace to indicate that you want the client to use PUT. &lt;p&gt;&lt;b&gt;AJAX client&lt;/b&gt; &lt;p&gt;The AJAX client’s DataService.update method can be extended with a new argument “UpdateAsReplace” that enables use of replace when set to true. By default we would continue to do “merge” (this requires tweaking the library because currently DataService.update generates a PUT request). &lt;p&gt;If the new boolean adds one too many arguments for update(), alternatively we could add a knob to the DataService class, we still made this change.  &lt;p&gt;&lt;b&gt;Astoria runtime-data source interaction&lt;/b&gt; &lt;p&gt;The only change in the interaction between Astoria and the data source should be that for the resource being replaced the system will call IUpdatable.ReplaceResource instead of IUpdatable.GetResource. &lt;p&gt;Pablo Castro&lt;br&gt;Software Architect&lt;br&gt;Microsoft Corporation &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8523803" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>Optimistic Concurrency &amp;amp; Data Services</title><link>http://blogs.msdn.com/astoriateam/archive/2008/04/22/optimistic-concurrency-data-services.aspx</link><pubDate>Wed, 23 Apr 2008 01:09:37 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8417326</guid><dc:creator>dpblogs</dc:creator><slash:comments>14</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/8417326.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=8417326</wfw:commentRss><description>&lt;p&gt;Different applications have different requirements around consistency and how concurrent modifications are handled. I’ll oversimplify and put all these applications in two buckets: either you care about controlling concurrent changes or you don’t. &lt;p&gt;If you’re creating a REST interface to your data and don’t care about concurrency (e.g. no deep consistency rules, or nice units of change that change in whole consistent ways), then you can use the basic HTTP methods to retrieve (GET) and manipulate (POST, PUT, DELETE) resources directly without any more context than the representations of your resources. You get “last one wins” semantics on updates in this case. &lt;p&gt;On the other hand, if you do care about concurrency in your REST interface, there are more aspects to take into consideration. If your resources are atomic (no further structure that’s interesting from the concurrent changes perspective than the resource as a whole), then you can have an out of band mechanism for creating a “version number” for each resource -typically a monotonically increasing number- and use HTTP’s existing mechanism to ensure you overwrite stuff that you know about. In HTTP you can stick an “entity tag” or ETag to your responses that contain an opaque value used to denote the version or state of a resource. Later on, when you want to modify a resource, you can use that value in a “if-match” request header to make sure that your knowledge about the state of the resource you’re modifying is still current. If it’s not the resource in the sever would have an ETag that won’t match the one you provided and you’d get back a 412 “Precondition failed” status code. All that is standard HTTP 1.1 stuff described in RFC 2616. (ETags are also used for caching and conditional gets in addition to the scenario I described here). &lt;p&gt;Now, REST data services that expose structured data have to deal with various challenges beyond the basics, which I’ll go into details below. While I discuss this in the context of the ADO.NET Data Services Framework (Astoria), I’m sure some of these problems apply to a broader set of applications. &lt;p&gt;&lt;b&gt;Creating ETags: concurrency tokens&lt;/b&gt; &lt;p&gt;The data services framework has to deal with the fact that we don’t control the data sources that we expose through the REST interface. Sometimes each entity that we turn into a resource will have a nice clean property that’s a timestamp or similar and maps perfectly to ETag semantics (e.g. whenever we change the value in a significant way the value of this property changes). However, often the schema of the underlying data is not under the control of the service developer so we have to work with what we have. What that means in practice is that you can tell the data services framework which properties of each entity type are “concurrency tokens”. Changing those values means that you chanced the version of the resource.  &lt;p&gt;The way you do that in the framework is by using an [ETag(props…)] attribute in your class or an annotation in your EDM schema. For types that don’t have any concurrency tokens we won’t generate ETags for the responses for those types, and they get “last one wins” update behavior. &lt;p&gt;Once you indicated which property or properties are your concurrency tokens we can produce ETags by using the values of those properties for the particular instance we’re returning. &lt;p&gt;During update the data services runtime works with the data source to determine whether the concurrency token values that were marshaled through ETags and if-match headers still match, and if so perform the update/delete operation. If they don’t match a 412 response is sent to the client. &lt;p&gt;&lt;b&gt;Including ETags in headers and/or payloads&lt;/b&gt; &lt;p&gt;The HTTP spec describes the ETag response header to transport the entity tag for a given resource. That works great for us for cases were we respond with a single entity (e.g. an entry in Atom terms), but it doesn’t when we return a collection of entries from a URL (e.g. an Atom feed). For the latter scenario, we include the ETag as part of the resource representation (in the entry for Atom, in the “__metadata” property for JSON), for example: &lt;p&gt;&amp;lt;entry m:type="BikesModel.Customer" m:etag="'A%20Bike%20Store'"&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;!-- rest of the entry --&amp;gt; &lt;p&gt;&amp;lt;/entry&amp;gt; &lt;p&gt;&lt;b&gt;Validation during side-effecting operations&lt;/b&gt; &lt;p&gt;Concurrency tokens are validated whenever you perform an operation that affects the state of an existing resource. In the data services REST interface that means HTTP PUT and DELETE methods. &lt;p&gt;As I mentioned above, validation happens during update processing by extracting the “original” values from the ETag (which was sent back through the if-match header) and comparing them with the data in the data source. If they are the same, we consider the whole resource the same and proceed with the modification. &lt;p&gt;An interesting question is whether presenting an ETag in a if-match header should be mandatory for resources that have concurrency tokens. Put another way: should the decision of whether it’s ok to potentially overwrite changes based on state knowledge be up to the client or restricted by the server? The HTTP spec defines a special value of “*” for the if-match header that effectively means “any value will match”. The behavior that we are planning for is that if an entity type has concurrency tokens then we’ll always require an “if-match” header in modification operations. The header value can be an actual ETag obtained through a GET request or “*” meaning “I know this type supports concurrency control, but I’ll overwrite it anyway”.  &lt;p&gt;&lt;b&gt;Almost, but not quite, a perfect match&lt;/b&gt; &lt;p&gt;HTTP ETags and conditional operations are almost a perfect match to what we need to handle concurrent activity in RESTful data services. There are, however, a few glitches. This is where we get into the fine-print that’s not necessarily popular knowledge. Mike brought up many of these details I wasn’t aware of. &lt;p&gt;ETags can be “strong entity tags” or “weak entity tags”. Weak ETags are very similar to what happens when we have entities for which only some properties are designated concurrency tokens. From section 13.3.3 of the HTTP spec: &lt;p&gt;“However, there might be cases when a server prefers to change the &lt;p&gt;&amp;nbsp;&amp;nbsp; validator only on semantically significant changes, and not when &lt;p&gt;&amp;nbsp;&amp;nbsp; insignificant aspects of the entity change. A validator that does not &lt;p&gt;&amp;nbsp;&amp;nbsp; always change when the resource changes is a "weak validator." “ &lt;p&gt;The problem is that weak ETags only apply to GET operations, they cannot be used for PUT/DELETE which is what we’re trying to do. &lt;p&gt;For cases where you own the data, the data services framework can expose a compliant interface by using constructs such as timestamps (if using a database as a data source), where any change in the entity will reflect in the ETag. You can also used a relaxed form of ETags where the entity might change but the ETag stay the same. It’s not completely HTTP compliant and may confuse intermediate systems, but it may be your only option in some scenarios. &lt;p&gt;As always, thoughts and feedback is welcome. &lt;p&gt;Pablo Castro&lt;br&gt;Software Architect&lt;br&gt;Microsoft Corporation &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8417326" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>IUpdatable &amp;amp; ADO.NET Data Services Framework</title><link>http://blogs.msdn.com/astoriateam/archive/2008/04/10/iupdatable-ado-net-data-services-framework.aspx</link><pubDate>Fri, 11 Apr 2008 00:40:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8376704</guid><dc:creator>dpblogs</dc:creator><slash:comments>10</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/8376704.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=8376704</wfw:commentRss><description>&lt;p&gt;Astoria service allows reading/querying of data via the already-established IQueryable interface – this helps in abstracting Astoria from the underlying data source. But there is no existing interface for the update operations (CUD – create, update, delete operations). Hence we came up with IUpdatable interface to support CUD operations and support read-write services. &lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;One of the main design goals while designing the IUpdatable interface was to make it resource independent. In other words, the methods that return objects representing resources can return anything – for Astoria, the returned object is a opaque object that represents the resource being asked, and whenever we want to use the resource (reading/updating a value from the resource), we will pass the same opaque back to IUpdatable. The actual implementation of IUpdatable needs to track the mapping between this opaque object to the actual object it represents. The only time we need the actual clr instance of the resource is when we need to serialize the object and we call a specify method on IUpdatable (ResolveResource) for that. &lt;p&gt;Let take a quick look at IUpdatable interface &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;public interface IUpdatable &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Creates the resource of the given type and belonging to the given container &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="containerName"&amp;gt;container name to which the resource belongs&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="fullTypeName"&amp;gt;full type name i.e. Namespace qualified type name of the resource&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;returns&amp;gt;object representing a resource of given type and belonging to the given container&amp;lt;/returns&amp;gt; &lt;p&gt;object CreateResource(string containerName, string fullTypeName); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Gets the resource of the given type that the query points to &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="query"&amp;gt;query pointing to a particular resource&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="fullTypeName"&amp;gt;full type name i.e. Namespace qualified type name of the resource&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;returns&amp;gt;object representing a resource of given type and as referenced by the query&amp;lt;/returns&amp;gt; &lt;p&gt;object GetResource(IQueryable query, string fullTypeName); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Gets the resource of the given type that the query points to. The resource returned contains the default values, &lt;p&gt;/// and not the value as present in the server &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="query"&amp;gt;query pointing to a particular resource&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="fullTypeName"&amp;gt;full type name i.e. Namespace qualified type name of the resource&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;returns&amp;gt;object representing a resource of given type and belonging to the given container and containing default values&amp;lt;/returns&amp;gt; &lt;p&gt;object ReplaceResource(IQueryable query, string fullTypeName); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Sets the value of the given property on the target object &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="targetResource"&amp;gt;target object which defines the property&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyName"&amp;gt;name of the property whose value needs to be updated&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyValue"&amp;gt;value of the property&amp;lt;/param&amp;gt; &lt;p&gt;void SetValue(object targetResource, string propertyName, object propertyValue); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Gets the value of the given property on the target object &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="targetResource"&amp;gt;target object which defines the property&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyName"&amp;gt;name of the property whose value needs to be updated&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;returns&amp;gt;the value of the property for the given target resource&amp;lt;/returns&amp;gt; &lt;p&gt;object GetValue(object targetResource, string propertyName); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Sets the value of the given reference property on the target object &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="targetResource"&amp;gt;target object which defines the property&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyName"&amp;gt;name of the property whose value needs to be updated&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyValue"&amp;gt;value of the property&amp;lt;/param&amp;gt; &lt;p&gt;void SetReference(object targetResource, string propertyName, object propertyValue); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Adds the given value to the collection &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="targetResource"&amp;gt;target object which defines the property&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyName"&amp;gt;name of the property whose value needs to be updated&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="resourceToBeAdded"&amp;gt;value of the property which needs to be added&amp;lt;/param&amp;gt; &lt;p&gt;void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Removes the given value from the collection &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="targetResource"&amp;gt;target object which defines the property&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="propertyName"&amp;gt;name of the property whose value needs to be updated&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;param name="resourceToBeRemoved"&amp;gt;value of the property which needs to be removed&amp;lt;/param&amp;gt; &lt;p&gt;void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Delete the given resource &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="targetResource"&amp;gt;resource that needs to be deleted&amp;lt;/param&amp;gt; &lt;p&gt;void DeleteResource(object targetResource); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Saves all the pending changes made till now &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;void SaveChanges(); &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp; &lt;p&gt;/// &amp;lt;summary&amp;gt; &lt;p&gt;/// Returns the actual instance of the resource represented by the given resource object &lt;p&gt;/// &amp;lt;/summary&amp;gt; &lt;p&gt;/// &amp;lt;param name="resource"&amp;gt;object representing the resource whose instance needs to be fetched&amp;lt;/param&amp;gt; &lt;p&gt;/// &amp;lt;returns&amp;gt;Returns the actual instance of the resource represented by the given resource object&amp;lt;/returns&amp;gt; &lt;p&gt;object ResolveResource(object resource); &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;p&gt;&amp;nbsp; &lt;p&gt;Let’s go through each api one by one. &lt;p&gt;object CreateResource(string containerName, string fullTypeName) :– This is called when one tries to insert a new resource via the POST http method. The first parameter points to the container that the resource belongs to and the second parameter tells the namespace qualified name of the resource type that needs to be created. The second parameter might not be that useful when there is no inheritance, since from the container, one can easily figure out the type, but for inheritance cases, this is helpful. The return type, as said before, need not be the actual clr instance of the resource. It can be anything (a cookie) that only the IUpdatable implementor needs to understands. &lt;p&gt;object GetResource(IQueryable query, string fullTypeName) :- Get the given resource as resolved by the query and the namespace qualified name of the type that the query resolves to. In some cases, the full type name can be null. Look at the examples below to see the cases when the fullTypeName can be null. Again, the return type can be anything that represents the resource. &lt;p&gt;object ReplaceResource(IQueryable query, string fullTypeName) :- Very similar to the GetResource API, but this is used for replace-semantics and GetResource is used for merge-semantics. The implementation needs to return the resource referred by the query, but with default values for all non-key properties. &lt;p&gt;void SetValue(object targetResource, string propertyName, object propertyValue) :- Set the value of the property with the given name on the target resource to the given property value. The target resource is the opaque object returned by either CreateResource or GetResource. This method is called for scalar properties and complex properties only. &lt;p&gt;object GetValue(object targetResource, string propertyName) :- Gets the value of the property with the given name for the target resource. The target resource is the opaque object returned by either CreateResource or GetResource. This method is called for scalar properties or complex properties. If it’s a scalar property, we expect the returned object to be the actual value. &lt;p&gt;void SetReference(object targetResource, string propertyName, object propertyValue) :- This method is called for setting the value of navigation property with the given name on the target resource to the given propertyValue. The target resource and the propertyValue are opaque objects returned by GetResource or CreateResource API. This method is called for navigation properties that refer to a single resource – representing 0 or 0..1 side of a relationship. &lt;p&gt;void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded):- This method is called for adding a resource to the collection navigation property with the given name on the target resource. Again, targetResource and resourceToBeAdded are opaque objects returned by GetResource or CreateResource API. The navigation property presents many side of a relationship. We generally call this operation as binding, which means you are binding the resourceToBeAdded to targetResource. In other words, you are setting up a relationship between the 2 resource objects. &lt;p&gt;void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved):- Very similar to the above method, except it removes the given resource from the collection navigation property on the target object. We generally call this operation as unbinding, which means you are deleting the relationship between the two resource objects in question. &lt;p&gt;void DeleteResource(object targetResource):- This actually deletes the given resource. Again, the targetResource is the opaque object returned by GetResource or CreateResource API. &lt;p&gt;void SaveChanges():- This actually saves all the changes that has been made till now, using the above API’s. The IUpdatable implementation needs to track all changes until this API is called and then save all of them when this API is called. The IUpdatable implementation is expected to save all the changes or nothing &lt;p&gt;object ResolveResource(object resource):- This API is called whenever we want to resolve the opaque object returned by the CreateResource or GetResource API into the actual clr instance. This normally is called after SaveChanges, when we want to serialize out the resource (for POST methods). This method is also called if there are UpdateInterceptors, that needs to be invoked with the actual clr resource instances or the provider supports optimistic concurrency and the resource type has concurrency tokens (defined via etag properties in clr based provider). &lt;p&gt;This blog has already become bigger that I intended it to be. In my next blog, I will try and come up with some examples and the sequence of calls made on the IUpdatable interface for each of them. I have purposefully keep this post simple just so that people can get the basic idea of IUpdatable interface. There are few features like ETag, Update interceptors, etc due to which there might be additional calls on this interface. I will try and cover them in my coming blogs. &lt;p&gt;Pratik Patel &lt;p&gt;Developer, ADO.NET Data Services Framework &lt;p&gt;(Project Astoria) &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8376704" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>Batching Data Service Requests</title><link>http://blogs.msdn.com/astoriateam/archive/2008/04/06/batching-data-service-requests.aspx</link><pubDate>Mon, 07 Apr 2008 08:24:30 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8364450</guid><dc:creator>dpblogs</dc:creator><slash:comments>15</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/8364450.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=8364450</wfw:commentRss><description>&lt;p&gt;We have received a fair amount of feedback regarding a number of use cases where it would be beneficial to enable a client of a data service to “batch” up a group of operations and send them to the data service &lt;i&gt;in a single HTTP request&lt;/i&gt;.&amp;nbsp; This reduces the number of roundtrips to the data service for apps that need to make numerous requests to the data service to perform a given action and allows a set of operations to be logically grouped together.&amp;nbsp; &lt;/p&gt; &lt;p&gt;Below is the design we have landed on.&amp;nbsp; Note: We will have something close to this design in our next CTP/Beta release of Astoria, but its not quite there yet.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The base ADO.NET Data Services Framework semantics&amp;nbsp; provide two mechanisms to query and send updates to a data service.&amp;nbsp; From a high-level they are:  &lt;p&gt;Query:  &lt;p&gt;1) Send an HTTP GET request to a URI representing a resource (or set of resources) and receive in the response a representation of the resource (or set of resources). Example: a GET request to /Customers(1) returns a single customer entity in the response  &lt;p&gt;2) Same as #1, but add the $expand query string operator to the request to request resources related to the resource(s) specified in the request URI be returned in the response as well.&amp;nbsp; Example: a GET request to /Products(1)?expand=Category, Parts returns product #1 as well as the Parts and Category associated the product in the response  &lt;p&gt;Update:  &lt;p&gt;1) Insert / update / delete a single resource per HTTP request by sending POST, PUT or DELETE requests to a data service  &lt;p&gt;2) Insert a new resource and related resources in a single request. Example: a POST to /Customers can insert a Customer and related&amp;nbsp; Orders in a single request by inlining the related orders in the request body  &lt;p&gt;&amp;nbsp; &lt;p&gt;&lt;strong&gt;Why do we need batching?&lt;/strong&gt;  &lt;p&gt;Now assume you have the following situation: &lt;em&gt;&lt;u&gt;Single “Save” button per page in my RIA line of business application:&lt;/u&gt;&lt;/em&gt; Contoso Solutions is building an online Silverlight-based order entry system for its salesforce. Any given sale requires a number of entities within the data service be inserted and/or updated. Some of the entities are associated via navigation properties while others have no relation to the other entities being acted on as part of processing the sale. The user experience contoso wants to enable is to paint the full order processing information on a single screen and include a “save changes” button at the bottom to persist all the updates made to create the order.  &lt;p&gt;In this case, the $expand operation cannot be used to pull down all the data to paint the order entry screen in a single HTTP request.&amp;nbsp; Also, the update operations cannot easily be persisted as an atomic set of operations to the underlying data store.  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&amp;nbsp; &lt;p&gt;&lt;strong&gt;Batching Design&lt;/strong&gt;  &lt;p&gt;To support batching, ADO.NET Data Services has added a new $batch URI which will accept batch requests and return batch responses.&amp;nbsp; Logically a batch request is a group of 0 or more QueryOperations and 0 or more ChangeSet operations.&amp;nbsp; QueryOperations are analogous to a simple "non batch" query request.&amp;nbsp; ChangeSet operations are just a group of unordered, atomic CUD (update,insert&amp;amp;delete) operations (ie. all operations succeed or none do).  &lt;p&gt;Now that we have the logical model (Batch is a collection of ordered QueryOps and ChangeSets), we needed a wire representation.&amp;nbsp; After a bit of exploring various ways to represent batches in ATOM, JSON , etc, Yaron Goland pointed out to us there is already a well defined way to represent multiple HTTP requests in a single request using multipart/mixed MIME messages and the mime type application/http.&amp;nbsp; This turned out to be just what we needed and enables us to easily encapsulate binary and text based content in a request or response.&amp;nbsp; Also, it looks like using multipart/mime for batching &lt;a href="http://www.snellspace.com/wp/?p=788"&gt;has been explored&lt;/a&gt; (with pretty positive feedback) a number of times in the blogosphere, so perhaps we'll all land on something generally applicable.&amp;nbsp;&amp;nbsp;&amp;nbsp; Instead of describing this, lets just look at an example.&amp;nbsp; &lt;p&gt;&lt;strong&gt;Example - Batch Request:&lt;/strong&gt;  &lt;p&gt;The example assumes the batch request is sent to a data service located at: http://foo.com/dataservice.svc  &lt;p&gt;The Batch example contains the following operations (in order):  &lt;ul&gt; &lt;li&gt;A Change Set which contains the following operations in order:  &lt;ul&gt; &lt;li&gt;POST operation  &lt;li&gt;PUT operation&lt;/li&gt;&lt;/ul&gt; &lt;li&gt;A Query Operation  &lt;li&gt;A Query Operation&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;u&gt;Note&lt;/u&gt;&lt;strong&gt;:&lt;/strong&gt;  &lt;ul&gt; &lt;li&gt;Outer HTTP Request elements &amp;amp; batch boundaries are shown in blue  &lt;li&gt;Query Operations are shown in green  &lt;li&gt;Change Sets are shown in red&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&lt;font color="#0000ff"&gt;POST /dataservice.svc/$batch HTTP/1.1&lt;br&gt;Host: foo.com&lt;br&gt;Content-Type: multipart/mixed; boundary=batch(36522ad7-fc75-4b56-8c71-56071383e77b) &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font color="#0000ff"&gt;--batch(36522ad7-fc75-4b56-8c71-56071383e77b)&lt;br&gt;&lt;/font&gt;&lt;font color="#ff0000"&gt;Content-Type: multipart/mixed; boundary=changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)&lt;br&gt;Content-Length: ###&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt; &lt;p&gt;&lt;font color="#ff0000"&gt;--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)&lt;br&gt;Content-Type: application/http&lt;br&gt;Content-Transfer-Encoding:binary &lt;/font&gt; &lt;p&gt;&lt;font color="#ff0000"&gt;POST /dataservice.svc/Categories HTTP/1.1&lt;br&gt;Host: foo.com &lt;br&gt;Content-Type: application/atom+xml;type=entry&lt;br&gt;Content-Length: ### &lt;/font&gt; &lt;p&gt;&lt;font color="#ff0000"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" standalone="yes"?&amp;gt;&lt;br&gt;&amp;lt;entry xmlns:d="&lt;/font&gt;&lt;a href="http://schemas.microsoft.com/ado/...&amp;quot;"&gt;&lt;font color="#ff0000"&gt;http://schemas.microsoft.com/ado/..."&lt;/font&gt;&lt;/a&gt;&lt;br&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:m="&lt;/font&gt;&lt;a href="http://schemas.microsoft.com/ado/.../metadata&amp;quot;"&gt;&lt;font color="#ff0000"&gt;http://schemas.microsoft.com/ado/.../metadata"&lt;/font&gt;&lt;/a&gt;&lt;br&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns="&lt;/font&gt;&lt;a href="http://www.w3.org/2005/Atom&amp;quot;"&gt;&lt;font color="#ff0000"&gt;http://www.w3.org/2005/Atom"&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff0000"&gt;&amp;gt;&lt;br&gt;&amp;nbsp; …&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:CategoryName&amp;gt;Software&amp;lt;/d:CategoryName&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Description d:null="true" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Picture d:null="true" /&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/content&amp;gt;&lt;br&gt;&amp;lt;/entry&amp;gt;&lt;br&gt;--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)&lt;br&gt;Content-Type: application/http&lt;br&gt;Content-Transfer-Encoding:binary &lt;/font&gt; &lt;p&gt;&lt;font color="#ff0000"&gt;PUT /Categories(5) HTTP/1.1&lt;br&gt;Host: foo.com&lt;br&gt;Content-Type: application/atom+xml;type=entry&lt;br&gt;If-Match: xxxxx&lt;br&gt;Content-Length: ### &lt;/font&gt; &lt;p&gt;&lt;font color="#ff0000"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" standalone="yes"?&amp;gt;&lt;br&gt;&amp;lt;entry xmlns:d="&lt;/font&gt;&lt;a href="http://schemas.microsoft.com/ado/...&amp;quot;"&gt;&lt;font color="#ff0000"&gt;http://schemas.microsoft.com/ado/..."&lt;/font&gt;&lt;/a&gt;&lt;br&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:m="&lt;/font&gt;&lt;a href="http://schemas.microsoft.com/ado/.../metadata&amp;quot;"&gt;&lt;font color="#ff0000"&gt;http://schemas.microsoft.com/ado/.../metadata"&lt;/font&gt;&lt;/a&gt;&lt;br&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns="&lt;/font&gt;&lt;a href="http://www.w3.org/2005/Atom&amp;quot;"&gt;&lt;font color="#ff0000"&gt;http://www.w3.org/2005/Atom"&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff0000"&gt;&amp;gt;&lt;br&gt;&amp;nbsp; …&lt;br&gt;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:CategoryID&amp;gt;5&amp;lt;/d:CategoryID&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:CategoryName&amp;gt;UpdateCategoryName&amp;lt;/d:CategoryName&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/content&amp;gt;&lt;br&gt;&amp;lt;/entry&amp;gt;&lt;br&gt;--changeset(77162fcd-b8da-41ac-a9f8-9357efbbd621)--&lt;br&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;--batch(36522ad7-fc75-4b56-8c71-56071383e77b)&lt;br&gt;&lt;/font&gt;&lt;font color="#008040"&gt;Content-Type: application/http&lt;br&gt;Content-Transfer-Encoding:binary &lt;/font&gt; &lt;p&gt;&lt;font color="#008040"&gt;GET /Categories(5) HTTP/1.1&lt;br&gt;Host: foo.com &lt;/font&gt; &lt;p&gt;&lt;font color="#0000ff"&gt;--batch(36522ad7-fc75-4b56-8c71-56071383e77b)&lt;br&gt;&lt;/font&gt;&lt;font color="#008040"&gt;Content-Type: application/http&lt;br&gt;Content-Transfer-Encoding:binary &lt;/font&gt; &lt;p&gt;&lt;font color="#008040"&gt;Operation: GET /Categories(6)&lt;br&gt;Host: foo.com &lt;/font&gt; &lt;p&gt;&lt;font color="#0000ff"&gt;--batch(36522ad7-fc75-4b56-8c71-56071383e77b)--&lt;/font&gt;  &lt;p&gt;&amp;nbsp; &lt;p&gt;&lt;strong&gt;Batch Response&lt;/strong&gt;  &lt;p&gt;Now that we have seen what a request looks like, the response is pretty much the mirror image of the request (also uses multipart/mime), with a mime part containing the associated HTTP response for each operation in the batch request.&amp;nbsp; The exception to this rule is for responses to ChangeSets.&amp;nbsp; Since ChangeSets are atomic if an operation in the set fails, the response for the ChangeSet is a single HTTP response instead of a nested multipart/mixed collection of responses.  &lt;p&gt;&amp;nbsp; &lt;p&gt;This is already getting a bit long, so I'll cut this off here.&amp;nbsp; In a future post we'll walk through an end to end request + response and talk about how to cross reference operations within a batch request.&amp;nbsp; What do you think so far?&amp;nbsp; Are we overlooking/missing something?  &lt;p&gt;&amp;nbsp; &lt;p&gt;Mike Flasko  &lt;p&gt;ADO.NET Data Services, Program Manager&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8364450" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>Looking for feedback: query caching in data services</title><link>http://blogs.msdn.com/astoriateam/archive/2008/03/31/looking-for-feedback-query-caching-in-data-services.aspx</link><pubDate>Mon, 31 Mar 2008 23:58:25 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8346488</guid><dc:creator>dpblogs</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/8346488.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=8346488</wfw:commentRss><description>&lt;p&gt;(sorry, tricky problem -&amp;gt; long write-up)  &lt;p&gt;One of the few things pending in the server library of the ADO.NET Data Services Framework is query caching to help with performance. Here is a brief explanation of why we needed and a couple of design options. Feedback is welcome.  &lt;p&gt;&lt;b&gt;Query processing in Astoria&lt;/b&gt;  &lt;p&gt;To give a little bit of context, let me first briefly go through the process that takes place when Astoria receives a URL and works its magic to turns it in query results ready for serialization in the HTTP response.  &lt;p&gt;Data sources are hooked into Astoria services through LINQ expression trees. That means that the role of Astoria during query processing is to take a URL and translate it into an expression tree that then we can ask the data source to execute and give us results. The general flow is more or less like this:  &lt;p&gt;URL -&amp;gt; Expression Tree -&amp;gt; [Data Source Execution and Materialization] -&amp;gt; Objects -&amp;gt; Serialization (Atom/JSON)  &lt;p&gt;The thing in brackets is data source-specific. For example, if using the ADO.NET Entity Framework it would look like this:  &lt;p&gt;URL -&amp;gt; Expression Tree -&amp;gt; Canonical Query Tree -&amp;gt; View expansion/Query simplification -&amp;gt; Canonical Query Tree -&amp;gt; SQL -&amp;gt; Rows (DataReader) -&amp;gt; Entities (DataReader) -&amp;gt; Objects -&amp;gt; Serialization (Atom/JSON)  &lt;p&gt;This is not a cheap thing to do in every request, hence this discussion about query caching.  &lt;p&gt;&lt;b&gt;Why query caching&lt;/b&gt;  &lt;p&gt;The processing pipeline that I showed above can be expensive. In particular, all that query translation between different tree types as well as all that analysis for view expansion and query simplification is an expensive, CPU-bound activity that we want to avoid as much as we can.  &lt;p&gt;To help with this we are planning to introduce query caching, similar to what database systems do (e.g. the SQL Server “proc cache”). The idea of query caching is that for commonly requested URLs we’d bypass most of the processing required to setup the query for execution, and we’d go as directly as possible to the execution phase.  &lt;p&gt;Since Astoria is a generic framework that works on many data sources, we have to enable this in a way that allows different data sources plug in their query caching capabilities if they have such thing.  &lt;p&gt;&lt;b&gt;Data source-independent query compilation&lt;/b&gt;  &lt;p&gt;In order to implement caching, we need a way of doing our own work for translation, then tell the data source to do its own work on the expression trees, and then save that work to be used every time we have to respond to the “same” request (the definition of “same” is well…difficult, more about this later); that is, we need a way of compiling queries.  &lt;p&gt;You can imagine an interface with two methods for this:  &lt;p&gt;interface IQueryCompilationProvider  &lt;p&gt;{  &lt;p&gt;&amp;nbsp; object CompileQuery(Expression&amp;lt;Func&amp;lt;T1, T2, …, Tn, TResult&amp;gt;&amp;gt; query);  &lt;p&gt;&amp;nbsp; IEnumerable ExecuteCompiledQuery(object compiledQuery);  &lt;p&gt;}  &lt;p&gt;The idea is that each data source can give whatever meaning it wants to “compiling” a query, and they can return us an opaque token (object) that we’d pass back along with parameter values in order to execute the query. Ideally you’d do as much work as possible. For example, there is a CompiledQuery class in &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.linq.compiledquery.aspx"&gt;LINQ to SQL&lt;/a&gt; and &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.objects.compiledquery.aspx"&gt;LINQ to Entities&lt;/a&gt; that can do the translation all the way to a SQL statement only once and then re-use it in all subsequent executions (re-binding parameter values).  &lt;p&gt;&lt;b&gt;Query caching design, part 1: for simple cases, a simple design does it&lt;/b&gt;  &lt;p&gt;In the simpler cases where the data services does not have customizations the URL -&amp;gt; Expression Tree translation is 1:1. The only consideration in this case would be to parameterize the URLs so that we don’t fragment the query cache with URLs that are the same query with different constants (e.g. /Customers(1) vs /Customers(2), or /Customers?$filter=City eq ‘Seattle’ and /Customers?$filter=City eq ‘Las Vegas’).  &lt;p&gt;In this case we can keep a simple map of parameterized URL to compiled query. If we don’t find a given URL, we go through the full translation process to produce an expression tree, and then –assuming the data source supports compilation- call CompileQuery to obtain a compiled query opaque token. On subsequent executions we look up the URL, find the compiled query token, bind the constants that we turned into parameters to parameter values and execute the query directly.  &lt;p&gt;The rest is standard caching stuff…keep a map, have a limit and an eviction policy, make it “spike resistant”, etc.  &lt;p&gt;Of course, life is rarely that simple…  &lt;p&gt;&lt;b&gt;Query interceptors&lt;/b&gt;  &lt;p&gt;So far we’ve assumed that there is a 1:1 correspondence between (parameterized) URLs and expression trees. Astoria has a nice feature called “query interceptors” that causes that correspondence to break.  &lt;p&gt;A query interceptor allows the service developer to introduce a custom filter predicate for each entity set that’s exposed through the service interface. For example, if you had a Customers table and a CustomerAccess table that indicates which user-ids can see which customers, you could implement entity-level security for customers by adding this interceptor:  &lt;p&gt;[QueryInterceptor("Customers")]  &lt;p&gt;Expression&amp;lt;Func&amp;lt;Customer, bool&amp;gt;&amp;gt; QueryCustomers()  &lt;p&gt;{  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // retrieve the user from the environment (e.g. the currently //logged-in user)  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserDescriptor u = GetUser();&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (u.IsAdmin)  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return c =&amp;gt; true; // can see all customers  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return c =&amp;gt; c.CustomerAccess.Any(ca =&amp;gt; ca.UserID ==  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; u.UserID);  &lt;p&gt;}  &lt;p&gt;Interceptors are invoked whenever a URL involves the entity-set the interceptor is bound to, regardless of how. The system injects a Where operator with the predicate returned by the interceptor. This includes top level entity-set access (e.g. /Customers), access of subsets through link traversal (e.g. /SalesPeople(123)/AssignedCustomers), and inline expansion (e.g. /SalesPeople(123)?$expand=AssignedCustomers).  &lt;p&gt;Note that now URLs and expression trees are no longer 1:1. There are two kinds of differences that might show up:  &lt;p&gt;a) Same query but different parameters. For example, if user 1 and user 2, both not administrators, fetch the URL /Customers, then both requests will produce identical query trees, with the only difference that the value of “u.UserID” will be different. The difference with the parameterization of the URLs is that in this case we’re not the ones parsing the input.  &lt;p&gt;b) Different query. In the example above, if one of the users accessing the URL /Customers is administrator and the other is not, the filter predicates used in each of them will be different.  &lt;p&gt;Now the caching thing got complicated.  &lt;p&gt;Side-note: I’ve excluded update interceptors in this discussion because they don’t participate in query composition. They are a key part of enforcing access control the way I described above though.  &lt;p&gt;&lt;b&gt;Query caching design, part 2&lt;/b&gt;  &lt;p&gt;We really wanted to make query caching work without any API surface other than maybe a configuration knob for the size of the cache. That’s not looking great at this point, but we do have a few options on the table.  &lt;p&gt;The essence of the problem is the fact that query interceptors need to be able to capture data from the execution environment at the time a given request is being processed. The main example of this is grabbing the user credentials for a given request (e.g. Thread.CurrentThread.CurrentPrincipal, or the user-id extracted from an encrypted cookie/custom HTTP header), but there can be other scenarios as well.  &lt;p&gt;There are a couple of approaches that could do the trick, but then come with their trade-offs:  &lt;p&gt;Option I: a bit of extra magic for a nicer API.  &lt;p&gt;We could let users write interceptors just like I showed above. Those interceptors mix references to environment data and the description of the filter predicate in a single construct, a LINQ expression tree that has references to external variables in the reference closure. What the developer writes looks like this (copied from above):  &lt;p&gt;[QueryInterceptor("Customers")]  &lt;p&gt;Expression&amp;lt;Func&amp;lt;Customer, bool&amp;gt;&amp;gt; QueryCustomers()  &lt;p&gt;{  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // retrieve the user from the environment (e.g. the currently logged-in user)  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserDescriptor u = GetUser();&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (u.IsAdmin)  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return c =&amp;gt; true; // can see all customers  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return c =&amp;gt; c.CustomerAccess.Any(ca =&amp;gt; ca.UserID ==  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; u.UserID);  &lt;p&gt;}  &lt;p&gt;To cache queries we would have to:  &lt;p&gt;· On every request invoke all interceptors that are needed  &lt;p&gt;· Extract all uncorrelated subexpressions from each interceptor filter predicate and turn them into constants (do “funcletization” in LINQ jargon), then replace the constants with generic parameters. Now we have a little tree for each interceptor  &lt;p&gt;· Now use a parameterized URL + all the interceptor trees the caching key.  &lt;p&gt;Option II: explicitness at the cost of API complexity  &lt;p&gt;The other approach is to explicitly separate the definition of the filter predicate from the per-execution state that comes from the environment. The developer would write two pieces, statically-defined filter predicate and a method that sets-up per-request state, as follows:  &lt;p&gt;[QueryInterceptor("Customers")]  &lt;p&gt;static Expression&amp;lt;Func&amp;lt;Customer, CustomersDbContext, Dictionary&amp;lt;string, object&amp;gt;, bool&amp;gt;&amp;gt; QueryCustomers()  &lt;p&gt;{  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (c, ctx, state) =&amp;gt; (bool)state["IsAdmin"] ||  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c.CustomerAccess.Any(ca =&amp;gt; ca.UserID ==  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (string)state["UserID"]);  &lt;p&gt;}  &lt;p&gt;void override OnStartRequest(RequestDescriptor descriptor, Dictionary&amp;lt;string, object&amp;gt; state)  &lt;p&gt;{  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // retrieve the user from the environment (e.g. the currently logged-in user)  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserDescriptor u = GetUser();&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; state[“IsAdmin”] = u.IsAdmin;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; state[“UserID”] = u.UserID;  &lt;p&gt;}  &lt;p&gt;As you can see, the definition of the filter predicate gets a bit trickier (more parameters in the lambda expression in particular).  &lt;p&gt;Of course, since at this point the interceptor is called only once and can’t use any request-bound context, we may as well just remove the idea of a method all together, and move the filter setup to the service initialization, where we already have APIs for configuring service policies. So the code would become:  &lt;p&gt;static void InitializeService(IDataServiceConfiguration config)  &lt;p&gt;{  &lt;p&gt;&amp;nbsp; // other policy initialization  &lt;p&gt;&amp;nbsp; // ...  &lt;p&gt;&amp;nbsp; // filters  &lt;p&gt;&amp;nbsp; config.SetEntitySetFilterPredicate&amp;lt;Customer&amp;gt;("Customers",  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (c, ctx, state) =&amp;gt; (bool)state["IsAdmin"] ||  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c.CustomerAccess.Any(ca =&amp;gt; ca.UserID ==  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (string)state["UserID"]);  &lt;p&gt;}  &lt;p&gt;void override OnStartRequest(RequestDescriptor descriptor, Dictionary&amp;lt;string, object&amp;gt; state)  &lt;p&gt;{  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // retrieve the user from the environment (e.g. the currently logged-in user)  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserDescriptor u = GetUser();&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; state["IsAdmin"] = u.IsAdmin;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; state["UserID"] = u.UserID;  &lt;p&gt;}  &lt;p&gt;The idea is that OnStartRequest (or whatever is a nice name for that) would explicitly capture the state any and all interceptors would use. Then interceptors become static constructs that are setup during initialization. An important detail is that now the predicate expression cannot refer to variables in the environment any more. Instead, for anything that’s request specific it needs to access the “state” object, which is then setup with data on a per-request basis.  &lt;p&gt;This yields a very efficient system, because now we can do minimal work on repeated requests that result in the same query, even in the presence of interceptors. On the other hand, the code that developers have to write is pretty tricky…  &lt;p&gt;Is it better to take a performance hit and leave it more usable? Is there a middle ground that we didn’t consider?  &lt;p&gt;If you made it reading this far, you’re probably one of few J. In any case, feedback is very welcome.  &lt;p&gt;&amp;nbsp; &lt;p&gt;Pablo Castro  &lt;p&gt;Software Architect  &lt;p&gt;Microsoft Corporation  &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8346488" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>Related entries and feeds: links and link expansion</title><link>http://blogs.msdn.com/astoriateam/archive/2008/02/18/related-entries-and-feeds-links-and-link-expansion.aspx</link><pubDate>Tue, 19 Feb 2008 09:04:12 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7787398</guid><dc:creator>dpblogs</dc:creator><slash:comments>11</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/7787398.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=7787398</wfw:commentRss><description>&lt;p&gt;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. &lt;p&gt;More context on Astoria support for Atom here: &lt;p&gt;&lt;a href="http://blogs.msdn.com/astoriateam/archive/2008/02/13/atompub-support-in-the-ado-net-data-services-framework.aspx"&gt;http://blogs.msdn.com/astoriateam/archive/2008/02/13/atompub-support-in-the-ado-net-data-services-framework.aspx&lt;/a&gt; &lt;p&gt;1. Links for modeling associations between resources &lt;p&gt;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.  &lt;p&gt;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.  &lt;p&gt;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.  &lt;p&gt;Do these trade-offs sound reasonable? Is any of the other options more appropriate? &lt;p&gt;Continuing with the Events sample, this is what an entry (/Events(456)) with links looks like: &lt;p&gt;&amp;lt;entry xml:base="http://localhost:81/EventsSample/"&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns="http://www.w3.org/2005/Atom"&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; m:type="EventsSample.Event"&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;id&amp;gt;http://localhost:81/EventsSample/Events(456)&amp;lt;/id&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;title type="text"&amp;gt;&amp;lt;/title&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;updated&amp;gt;2008-02-17T02:52:38Z&amp;lt;/updated&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;name /&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;/author&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;link rel="edit" title="Event" href="Events(456)" /&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=entry" title="Venue" href="Events(456)/Venue" /&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=feed" title="Attendees" href="Events(456)/Attendees" /&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:EventID m:type="Int32"&amp;gt;456&amp;lt;/d:EventID&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Name&amp;gt;Big Party&amp;lt;/d:Name&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:NoteToAttendees&amp;gt;It's going to be a great party!&amp;lt;/d:NoteToAttendees&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:DateAndTime m:type="DateTime"&amp;gt;2008-03-05T06:00:00&amp;lt;/d:DateAndTime&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;/content&amp;gt; &lt;p&gt;&amp;lt;/entry&amp;gt; &lt;p&gt;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. &lt;p&gt;2. Expanding links inline &lt;p&gt;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. &lt;p&gt;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. &lt;p&gt;For representing expanded links we put the expanded content inside the link element itself. According to section 4.2.7 of RFC 4287: &lt;p&gt;"The "atom:link" element defines a reference from an entry or feed to&amp;nbsp; a Web resource.&amp;nbsp; This specification assigns no meaning to the content (if any) of this element." &lt;p&gt;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. &lt;p&gt;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 &amp;lt;inline&amp;gt; element. For example, for "/Events(456)?$expand=Attendees,Venue" the response would be: &lt;p&gt;&amp;lt;entry xml:base="http://localhost:81/EventsSample/"&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata "&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns="http://www.w3.org/2005/Atom" m:type="EventsSample.Event"&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;id&amp;gt;http://localhost:81/EventsSample/Events(456)&amp;lt;/id&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;title type="text"&amp;gt;&amp;lt;/title&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;updated&amp;gt;2008-02-17T03:01:18Z&amp;lt;/updated&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;name /&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;/author&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;link rel="edit" title="Event" href="Events(456)" /&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=entry" title="Venue" href="Events(456)/Venue"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;m:inline&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;entry m:type="EventsSample.Venue"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;id&amp;gt;http://localhost:81/EventsSample/Venues(789)&amp;lt;/id&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title type="text"&amp;gt;&amp;lt;/title&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;updated&amp;gt;2008-02-17T03:01:18Z&amp;lt;/updated&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;name /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="edit" title="Venue" href="Venues(789)" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=entry" title="SalesContact" href="Venues(789)/SalesContact" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:VenueID m:type="Int32"&amp;gt;789&amp;lt;/d:VenueID&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Name&amp;gt;The Cool Place&amp;lt;/d:Name&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Description&amp;gt;Great place for parties!&amp;lt;/d:Description&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Capacity m:type="Int32"&amp;gt;1500&amp;lt;/d:Capacity&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Type&amp;gt;Nightclub&amp;lt;/d:Type&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/content&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/entry&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/m:inline&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;/link&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=feed" title="Attendees" href="Events(456)/Attendees"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;m:inline&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;feed&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title type="text"&amp;gt;Attendees&amp;lt;/title&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;id&amp;gt;http://localhost:81/EventsSample/Events(456)/Attendees&amp;lt;/id&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;updated&amp;gt;2008-02-17T03:01:18Z&amp;lt;/updated&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="self" title="Attendees" href="Events(456)/Attendees" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;entry m:type="EventsSample.Contact"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;id&amp;gt;http://localhost:81/EventsSample/Contacts(123)&amp;lt;/id&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title type="text"&amp;gt;&amp;lt;/title&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;updated&amp;gt;2008-02-17T03:01:18Z&amp;lt;/updated&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;name /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="edit" title="Contact" href="Contacts(123)" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=entry" title="BestFriend" href="Contacts(123)/BestFriend" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:ContactID m:type="Int32"&amp;gt;123&amp;lt;/d:ContactID&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:FirstName&amp;gt;John123&amp;lt;/d:FirstName&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:LastName&amp;gt;Doe123&amp;lt;/d:LastName&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:EmailAddress&amp;gt;jd123@foo.com&amp;lt;/d:EmailAddress&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Phone&amp;gt;123-456-123&amp;lt;/d:Phone&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:BirthDate m:type="Nullable`1[System.DateTime]"&amp;gt;1990-04-01T00:00:00&amp;lt;/d:BirthDate&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/content&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/entry&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;entry m:type="EventsSample.Contact"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;id&amp;gt;http://localhost:81/EventsSample/Contacts(124)&amp;lt;/id&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title type="text"&amp;gt;&amp;lt;/title&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;updated&amp;gt;2008-02-17T03:01:18Z&amp;lt;/updated&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;name /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/author&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="edit" title="Contact" href="Contacts(124)" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;link rel="related" type="application/atom+xml;type=entry" title="BestFriend" href="Contacts(124)/BestFriend" /&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:ContactID m:type="Int32"&amp;gt;124&amp;lt;/d:ContactID&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:FirstName&amp;gt;John124&amp;lt;/d:FirstName&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:LastName&amp;gt;Doe124&amp;lt;/d:LastName&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:EmailAddress&amp;gt;jd124@foo.com&amp;lt;/d:EmailAddress&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Phone&amp;gt;123-456-124&amp;lt;/d:Phone&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:BirthDate m:type="Nullable`1[System.DateTime]"&amp;gt;1990-05-01T00:00:00&amp;lt;/d:BirthDate&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/content&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/entry&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- more entries for contacts that will be --&amp;gt;  &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!-- attendees in this party --&amp;gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/feed&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/m:inline&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;/link&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;content type="application/xml"&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:EventID m:type="Int32"&amp;gt;456&amp;lt;/d:EventID&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:Name&amp;gt;Big Party&amp;lt;/d:Name&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:NoteToAttendees&amp;gt;It's going to be a great party!&amp;lt;/d:NoteToAttendees&amp;gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;d:DateAndTime m:type="DateTime"&amp;gt;2008-03-05T06:00:00&amp;lt;/d:DateAndTime&amp;gt; &lt;p&gt;&amp;nbsp; &amp;lt;/content&amp;gt; &lt;p&gt;&amp;lt;/entry&amp;gt; &lt;p&gt;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. &lt;p&gt;Feedback in general about this approach would be greatly appreciated. &lt;p&gt;Pablo Castro&lt;br&gt;Technical Lead&lt;br&gt;Microsoft Corporation &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7787398" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>AtomPub support in the ADO.NET Data Services Framework</title><link>http://blogs.msdn.com/astoriateam/archive/2008/02/13/atompub-support-in-the-ado-net-data-services-framework.aspx</link><pubDate>Thu, 14 Feb 2008 10:07:48 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7687637</guid><dc:creator>dpblogs</dc:creator><slash:comments>15</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/7687637.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=7687637</wfw:commentRss><description>&lt;p&gt;&amp;nbsp; &lt;p&gt;We have been looking for the last few months at adding first-class support for AtomPub to Project Astoria (we briefly touched on it before &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/09/09/design-feedback-requested.aspx"&gt;here&lt;/a&gt;). We are at a point where we have some parts of the AtomPub story and their initial implementation running (and we'll share fresh experimental bits soon), some parts on the design board and other parts that we haven’t explored yet. I wanted to write a few words to explain why we think AtomPub support is important and enumerate the challenges we face. Guidance and general feedback on the reasoning, the approach and on the details is very much appreciated. &lt;p&gt;&lt;b&gt;Why are we looking at AtomPub?&lt;/b&gt; &lt;p&gt;Astoria data services can work with different payload formats and to some level different user-level details of the protocol on top of HTTP. For example, we support a JSON payload format that should make the life of folks writing AJAX applications a bit easier. While we have a couple of these kind of ad-hoc formats, we wanted to support a pre-established format and protocol as our primary interface. &lt;p&gt;If you look at the underlying data model for Astoria, it boils down to two constructs: resources (addressable using URLs) and links between those resources. The resources are grouped into containers that are also addressable. The mapping to Atom entries, links and feeds is so straightforward that is hard to ignore. Of course, the devil is in the details and we'll get to that later on. &lt;p&gt;The interaction model in Astoria is just plain HTTP, using the usual methods for creating, updating, deleting and retrieving resources. Furthermore, we use other HTTP constructs such as "ETags" for concurrency checks,&amp;nbsp; "location" to know where a POSTed resource lives, and so on. All of these also map naturally to AtomPub. &lt;p&gt;From our (Microsoft) perspective, you could imagine a world where our own consumer and infrastructure services in Windows Live could speak AtomPub with the same idioms as Astoria services, and thus could both have a standards-based interface and also use the same development tools and runtime components that work with any Astoria-based server. This would mean less clients/development tools for us to create and more opportunity for our partners in the libraries and tools ecosystem out there. &lt;p&gt;&lt;b&gt;How are we approaching this?&lt;/b&gt; &lt;p&gt;We are simply mapping whatever we can to regular AtomPub elements. Sometimes that is trivial, sometimes we need to use extensions and sometimes we leave AtomPub alone and build an application-level feature on top. Here is an initial list of aspects we are dealing with in one way or the other. We’ll also post elaborations of each one of these to the appropriate Atom syntax|protocol mailing lists. &lt;p&gt;a) Mapping the data model: how do we map Astoria’s underlying data model, the Entity Data Model, to Atom constructs. This is quite straightforward but it deserves a look for completeness. &lt;p&gt;b) We use just the regular format/protocol whenever we can, we would be interested in validating our use with folks out there &lt;p&gt;c) Using AtomPub constructs and extensibility mechanisms to enable Astoria features: &lt;p&gt;· Inline expansion of links (“GET a given entry and all the entries related through this named link”, how we represent a request and the answer to such a request in Atom?).  &lt;p&gt;· Properties for entries that are media link entries and thus cannot carry any more structured data in the &amp;lt;content&amp;gt; element &lt;p&gt;· HTTP methods acting on bindings between resources (links) in addition to resources themselves &lt;p&gt;· Optimistic concurrency over HTTP, use of ETags and in general guaranteeing consistency when required &lt;p&gt;· Request batching (e.g. how does a client send a set of PUT/POST/DELETE operations to the server in a single go?) &lt;p&gt;d) Astoria design patterns that are not AtomPub format/protocol concepts or extensions: &lt;p&gt;· Astoria gives semantics to URLs and has a specific syntax to construct them &lt;p&gt;· How metadata that describes the structure of a service end points is exposed. This goes from being to find out entry points (e.g. collections in service documents) to having a way of discovering the structure of entries that contain structured data &lt;p&gt;e) How do we deal with aspects that AtomPub does not handle by design or just because it has not been needed so far? &lt;p&gt;· What to do with fields that may not have a backing value in the input source (e.g. updated, author). &lt;p&gt;· Replace versus merge semantics during updates &lt;p&gt;f) High-level client libraries. How high-level can we make clients so they can consume AtomPub-based Astoria services but still feel that they are working against regular objects and have general integration with the development environment? &lt;p&gt;There are probably more, but I think this is a good starting list.  &lt;p&gt;&lt;b&gt;Where do we go from here?&lt;/b&gt; &lt;p&gt;The folks in the AtomPub community understand this the best, so we’ll take our questions to the atom-syntax and atom-protocols lists to hear opinions there. We’ll probably track posts and comments in the Astoria blog as well so people that follow it can keep track of what’s going on in this space. &lt;p&gt;&amp;nbsp; &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;. &lt;p&gt;&amp;nbsp; &lt;p&gt;Pablo Castro &lt;p&gt;Technical Lead &lt;p&gt;Microsoft Corporation&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7687637" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>Design Notes: URI Containment in Astoria</title><link>http://blogs.msdn.com/astoriateam/archive/2007/12/21/design-notes-uri-containment-in-astoria.aspx</link><pubDate>Sat, 22 Dec 2007 01:00:22 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6830518</guid><dc:creator>dpblogs</dc:creator><slash:comments>6</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/6830518.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=6830518</wfw:commentRss><description>&lt;p&gt;&lt;b&gt;Problem statement&lt;/b&gt;  &lt;p&gt;Today in Astoria if you have two entity types that are functionally in a containment relationship, you still see a top-level entity set for the instances of the contained type, and further the URLs for instances of the contained types that go through the parent type contain redundant information.  &lt;p&gt;For example, assuming a schema where Order Lines (type “OrderLine”, entityset “OrderLines”) live within Orders(type “Order”, entityset “Orders”), two issues surface as shown below (points a &amp;amp; b). Additionally, assume that Orders have an “id” member that is the key, and that OrderLines has id and orderId (container id) members that form a composite key.  &lt;p&gt;a) entities are always addressable through top-level entitysets, e.g. /OrderLines(1,2)  &lt;p&gt;b) deep addressing through container has redundant information, e.g. /Orders(1)/OrderLines(1,2)&amp;nbsp; (the container id, “1”, is repeated)  &lt;p&gt;We have received feedback that this does not work for some scenarios in which people want to use Astoria.&amp;nbsp; Continuing the example above where OrderLines are strictly contained within Orders, it does not feel right to have top-level access to it, even a composite key. On the other hand, containment where keys of the contained type repeat within different parents is something that the EDM currently does not model.  &lt;p&gt;We need a solution that forces access to contained instances through its parents, does not require redundant specification of keys, and does not deviate from our data model.  &lt;p&gt;&lt;b&gt;Proposal&lt;/b&gt;  &lt;p&gt;We define two new features to handle the general scenario of composition:  &lt;p&gt;The first feature eliminates the need for redundant keys specifically for children that have a composite key where part of the key is the complete parent key. The model metadata needs to indicate that this is a parent-child relationship. For the case of the EDM metadata (entity framework), we’ll annotate the CSDL with attributes [1] and for the case of CLR object models we’ll use an attribute as in the following example:  &lt;p&gt;class MyData  &lt;p&gt;{  &lt;p&gt;public IQueryable&amp;lt;Order&amp;gt; Orders;  &lt;p&gt;[CanonicalAccessPath(Parent = “Orders”,  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ParentNavigationProperty = “OrderLines”,  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeyMapping = { id, orderId},  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ]  &lt;p&gt;public IQueryable&amp;lt;OrderLine&amp;gt; OrderLines;  &lt;p&gt;}  &lt;p&gt;Once an entity-set has been annotated like this, the keys of the parent are no longer required, so what was /Orders(1)/OrderLines(1,2) becomes /Orders(1)/OrderLines(2), where the key that goes in /OrderLines can repeat across different containers because it’s implicitly scoped to the other parts of the key that flow from the parent. URI construction rules to support this are:  &lt;p&gt;· Child relationships can be chained to build multi-level containment arrangements.  &lt;p&gt;· URIs with compound keys require the key to be specified using name/value pairs consisting of the property name of the key followed by its value  &lt;blockquote&gt; &lt;p&gt;Example: /Orders(1)/Es(orderId=1,id=2)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;· For non-compound keys (ex. /Customers(1) ) the key/value format is allowed, but is not the canonical form  &lt;blockquote&gt; &lt;p&gt;Example: /Customer(1) is legal and canonical, /Customer(Key=1) is also legal, but not canonical&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;· The canonical form of containment URIs is the representation which does NOT include the parent key  &lt;blockquote&gt; &lt;p&gt;Example: given /Orders(1)/OrderLines(orderId=1,id=2) &amp;amp; /Orders(1)/OrderLines(2) from above, the canonical URI is: /Orders(1)/OrderLines(2)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;· While dropping the parent key results in the canonical URI, the “full form” (ie. /Orders(1)/OrderLines(orderId=1,id=2)) is allowed  &lt;p&gt;From the Astoria runtime perspective, this means 2 things:  &lt;p&gt;-the URL translator will know about this annotations in the sets, and whenever we’re translating a URL and hit an entity set with an access path marker we’ll simply flow the key values from the parent. This means that at the query expression tree level there will be no noticeable difference, which scopes the change to URL translation and metadata handling only.  &lt;p&gt;-the metadata generation will include an annotation to indicate that a given entity set contains child resources.  &lt;p&gt;The second feature is the ability to hide a top-level set and re-direct the canonical URL to an alternate path that goes through a parent. This is annotated with the CanonicalAccessPath attributes (or in CSDL via custom annotations[1]). Obviously you can have only one of these. Example (different from previous in green):  &lt;p&gt;class MyData  &lt;p&gt;{  &lt;p&gt;public IQueryable&amp;lt;Order&amp;gt; Orders;  &lt;p&gt;[CanonicalAccessPath(Parent = “Orders”,  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ParentNavigationProperty = “OrderLines”,  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; KeyMapping = { id, orderId},  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TopLevelAccess&lt;b&gt; = false&lt;/b&gt;)]  &lt;p&gt;public IQueryable&amp;lt;OrderLine&amp;gt; OrderLines;  &lt;p&gt;}  &lt;p&gt;The effect of applying this attribute is two-fold:  &lt;p&gt;· the top-level entityset is no longer accessible (specified via TopLevelAccess=false), so something like /OrderLines(orderId=1,id=2) would result in a 404  &lt;p&gt;· the presence of the CanonicalAccessPath attribute means the canonical URLs in the serializer are generated using the parent container, so the canonical form of /OrderLines(orderId=1,id=2) becomes /Orders(1)/OrderLines(2)  &lt;p&gt;NOTE: An additional attribute named AccessPath (ie. no “Canonical” prefix) is defined which enables the specification of additional routes to a resource just as is done with the CanonicalAccessPath attribute; however, paths defined with the AccessPath attribute do NOT represent the canonical path to the resource.  &lt;p&gt;Note that since the keys of the parent are always entirely contained within the child keys we never need additional queries to build the full nested URL.  &lt;p&gt;A side-effect of this feature is that clients that are metadata-driven need to understand the special annotation to know that they need to generate URLs for an entity marked this way in a special way  &lt;p&gt;Notes:  &lt;p&gt;[1] Two annotations will be defined in CSDL which map to the information conveyed by the CanonicalAccessPath and AccessPath attributes noted in the document.&amp;nbsp; The attributes will be:  &lt;p&gt;· On an EntitySet element: TopLevelAccess=true|false  &lt;p&gt;· One a NavigationProperty element: AccessPath=Canonical|NonCanonical  &lt;p&gt;&amp;nbsp; &lt;p&gt;We look forward to hearing what you think of this approach... &lt;p&gt;&amp;nbsp; &lt;p&gt;Happy Holidays, &lt;p&gt;-Mike Flasko  &lt;p&gt;Program Manager, ADO.NET Data Services ("Project Astoria")  &lt;p&gt;&amp;nbsp; &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6830518" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>ADO.NET Data Services Dec2007 CTP  - Validation and Access Control</title><link>http://blogs.msdn.com/astoriateam/archive/2007/12/10/ado-net-data-services-dec2007-ctp-validation-and-access-control.aspx</link><pubDate>Tue, 11 Dec 2007 06:25:54 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6730331</guid><dc:creator>dpblogs</dc:creator><slash:comments>8</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/6730331.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=6730331</wfw:commentRss><description>&lt;p&gt;&lt;i&gt;Note: This post refers to the CTP available &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=A9C6BC06-B894-4B11-8300-35BD2F8FC908&amp;amp;displaylang=en"&gt;here&lt;/a&gt;. &lt;/i&gt;  &lt;p&gt;When building frameworks to expose and consume data on the web, access policy and data validation are always one of the first topics discussed during the design process, at conferences, etc.&amp;nbsp; Given this, we thought the topic would be a good one to kick off a series of blog posts about the feature set of the recently announced Dec 07 CTP of the product.&amp;nbsp; If you haven’t seen ADO.NET Data Services / “Project Astoria” before, I suggest checking out &lt;a href="http://astoria.mslivelabs.com"&gt;http://astoria.mslivelabs.com&lt;/a&gt; for an overview and “getting started” style walkthroughs of the product.&amp;nbsp; &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Authentication&lt;/b&gt;  &lt;p&gt;Before getting into access control and validation, I need to put in a quick note about authentication to set the stage.&amp;nbsp; Data services do not directly implement an authentication scheme. Instead, they rely on the authentication infrastructure of the technology (ex. WCF+ASP.NET, WCF, etc) hosting the data service. If you have an ASP.NET site that uses authentication (either one of the built-in authentication schemes or a custom one that sets the HTTP context principal appropriately), Astoria will simply leverage that mechanism to establish the current identity (principal) for a given request.&amp;nbsp; Ok, now that we know how data services determines &lt;i&gt;who&lt;/i&gt; is making a request, its’ time to look at how to implement access control and perform validation based on this information.  &lt;p&gt;&lt;b&gt;&lt;/b&gt; &lt;p&gt;&lt;b&gt;Service-wide Access Control&lt;/b&gt;  &lt;p&gt;By default all entities, Service Operations and metadata describing any resources in a data service cannot be retrieved.&amp;nbsp; Said another way, by default a data service is completely locked down with no read or write access.&amp;nbsp; This is one of the significant differences from previous (prototype) releases of Astoria.&amp;nbsp; In prior releases, a data service was fully open by default to enable easily creating and evaluating our ideas for what data services on the web might look like.&amp;nbsp; Now that the project has moved out from incubation and into an official product release, we changed such that services are locked down out of the box to align with typical security requirements of production web infrastructures.  &lt;p&gt;One of the first steps a data service developer needs to take is to open up access to the appropriate resources in the data service.&amp;nbsp; One way to do this is to set service wide, access control policy by adding a data service initialization method.&amp;nbsp; The code below shows what a typical data service created over the northwind sample database would look like if only the Customers, Orders and Products sets were exposed.&amp;nbsp; &lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;public class MyDataService : WebDataService&amp;lt;Northwind&amp;gt;&lt;/font&gt; &lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;{&lt;/font&gt;  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt; public static void InitializeService&amp;nbsp;&amp;nbsp; &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp; (&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;IWebDataServiceConfiguration configuration)&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New" size="2"&gt; {&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp; // URI ‘/Customers’ entities is enabled for all&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp; //read and write &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;operations&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; configuration.SetResourceContainerAccessRule&lt;/font&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("Customers", &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;ResourceContainerRights.All);&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;nbsp; URI ‘/Orders’ is disabled, but ‘/Orders(1)’ &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // is enabled for read &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;only &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; configuration.SetResourceContainerAccessRule&lt;/font&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("Orders", R&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;esourceContainerRights.ReadSingle);&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&lt;/font&gt;&amp;nbsp; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;nbsp; Can insert and update, but not delete &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Products&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; configuration.SetResourceContainerAccessRule&lt;/font&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("Products", &lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceContainerRights.WriteInsert |&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceContainerRights.WriteUpdate);&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp; }&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New" size="2"&gt;}&lt;/font&gt;  &lt;p&gt;The access policies in the snippet above are applied to all requests to the data service and are related to entity sets, not particular URIs.&amp;nbsp; For example, the policy described by the call: ‘SetResourceContainerAccessRule("Customers",ResourceContainerRights.All)’ would apply to a request sent to&amp;nbsp; ‘/Customers(‘ALFKI’)’ as well as&amp;nbsp; ’/Orders(1)/Customers’.&amp;nbsp; Basically, the rule is no matter how you address an entity or entity set, the rules in the init method apply equally.&amp;nbsp; All other entities not explicitly enabled in the initialize service method will not be exposed by the data service.&amp;nbsp; For example, the northwind db also includes Employee entities.&amp;nbsp; Since the initialization method does not say anything about Employees, any request to retrieve an employee entity will result in an HTTP 404 (Not Found) response.&amp;nbsp; In addition accessing the endpoint to retrieve the service’s metadata (ex. &lt;a href="http://host/service.svc/$metadata"&gt;http://host/service.svc/$metadata&lt;/a&gt;) will return a document which only describes those resources where were explicitly made visible.  &lt;p&gt;&lt;b&gt;Per Request Access Control &amp;amp; Validation&lt;/b&gt;  &lt;p&gt;Many data services will need to need to run validation logic when entities enter the data service (for inserts, updates or deletes) and/or restrict access to entities on a per request basis.&amp;nbsp; For these scenarios &lt;i&gt;Interceptors&lt;/i&gt; are used which enable a data service developer to plug in custom validation or access policy logic into the request/response processing pipeline of a data service.&amp;nbsp; We decided to take the approach of providing infrastructure to build out custom per request access policy instead of defining an access policy model specific to ADO.NET Data Services because we recognize that each application, business, etc have different requirements in this space and that we could add value by providing comprehensive infrastructure elements for one to build custom access policy.&amp;nbsp; &lt;p&gt;For example, assume you want to implement a policy which enabled customers to only retrieve their orders and not orders placed by other customers.&amp;nbsp;&amp;nbsp; The example below shows a &lt;i&gt;query interceptor&lt;/i&gt; that implements this access policy.&amp;nbsp; The important aspect to note about query interceptors is that they accept as a parameter an instance of IQueryable&amp;lt;T&amp;gt; which represents the query the system will push down to the underlying data store to retrieve the requested entity.&amp;nbsp; As shown in the example, this approach enables access policy to be defined using query composition eliminating any added round trips to the data store to retrieve access control information.&amp;nbsp; The example assumes the data service is hosted within a WCF+ASP.NET web application with authentication enabled as the ASP.Net HTTPContext object is used to retrieve the principle of the current request.&amp;nbsp; &lt;p&gt;using System;  &lt;p&gt;using System.Web;  &lt;p&gt;using System.Collections.Generic;  &lt;p&gt;using System.ServiceModel.Web;  &lt;p&gt;using System.Linq;  &lt;p&gt;using Microsoft.Data.Web;  &lt;p&gt;using NorthwindModel;  &lt;p&gt;namespace TestApplication  &lt;p&gt;{  &lt;p&gt; public class nw :&amp;nbsp;&amp;nbsp; WebDataService&amp;lt;NorthwindModel.NorthwindEntities&amp;gt;  &lt;p&gt; {  &lt;blockquote&gt; &lt;p&gt;public static void InitializeService( &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IWebDataServiceConfiguration configuration)  &lt;p&gt;&amp;nbsp;&amp;nbsp; {  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; configuration.SetResourceContainerAccessRule("Customers",  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceContainerRights.All);  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; configuration.SetResourceContainerAccessRule("Orders",  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceContainerRights.ReadSingle);  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; configuration.SetResourceContainerAccessRule("Products",  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceContainerRights.WriteInsert |  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ResourceContainerRights.WriteUpdate);  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;blockquote&gt; &lt;p&gt;[QueryInterceptor("Orders")] &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public IQueryable&amp;lt;Orders&amp;gt; OnQueryOrders(IQueryable&amp;lt;Orders&amp;gt;  &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; orderQuery) &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return from o in orderQuery  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where o.Customers.ContactName ==  &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpContext.Current.User.Identity.Name &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select o;  &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;p&gt;&amp;nbsp; &lt;p&gt; }  &lt;p&gt;}  &lt;p&gt;To hook into update operations (insert, update &amp;amp; delete), &lt;i&gt;change interceptors&lt;/i&gt; are used.&amp;nbsp; The example below validates all the Product entities that are created are not discontinued.&amp;nbsp;&amp;nbsp; This would also be the place to implement per request access policy to restrict inserts, updates and/or deletes per request.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;p&gt;[ChangeInterceptor("Products")]  &lt;p&gt;public void OnChangeCategories(Products p, ResourceActions action){  &lt;blockquote&gt; &lt;p&gt;if(action == ReceiveEntityOperation.Insert) { &lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&amp;nbsp; if(p.Discontinued) { &lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new WebDataServiceException(400, &lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Cannot add discontinued products"); &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }  &lt;p&gt; }  &lt;p&gt;}  &lt;p&gt;To summarize, services created using the ADO.NET Data Services framework are locked down by default and resources must be explicitly exposed.&amp;nbsp; Once exposed, the service developer has a number of hooks at their disposal to implement per request access control and business rule validation.&amp;nbsp;&amp;nbsp; &lt;p&gt;What do you think of this approach?&amp;nbsp; Is there something we should change/alter/add/etc to better meet your requirements?&amp;nbsp;&amp;nbsp; We look forward to hearing your feedback and continuing our &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;open design process&lt;/a&gt; on this blog.  &lt;p&gt;&amp;nbsp; &lt;p&gt;Mike Flasko  &lt;p&gt;Program Manager  &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6730331" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>Astoria data sources and system layering</title><link>http://blogs.msdn.com/astoriateam/archive/2007/09/27/astoria-data-sources-and-system-layering.aspx</link><pubDate>Fri, 28 Sep 2007 04:02:12 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5176117</guid><dc:creator>dpblogs</dc:creator><slash:comments>12</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/5176117.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=5176117</wfw:commentRss><description>&lt;p&gt;The question is: what data sources should Astoria support as inputs? Should we allow ADO.NET Entity Framework only or open up more? If we open up more, how does the system need to be layered to enable the use of those alternate data sources?&lt;/p&gt;  &lt;p&gt;Let me step back for a second first and establish why certain data sources are interesting.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Why does Astoria build on top of the EDM and the Entity Framework?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Putting an HTTP head on top of some set of data is easy enough that it&amp;#x2019;s tempting to do it against any data source out there. The problem is that then if the way data is modeled and surfaced has not been thought through, inconsistencies and awkward aspects start to show up in the HTTP interface.&lt;/p&gt;  &lt;p&gt;The Entity Data Model (EDM) was a very nice candidate. EDM explicitly models all data as &lt;i&gt;entities&lt;/i&gt;, which can be nicely mapped to &lt;i&gt;resources&lt;/i&gt;. So the definition of a resource is crisp and it comes from the underlying data model. Furthermore, the EDM defines associations between those entities, which can be surfaced quite naturally as hyperlinks. Finally, semantics around query and update for EDM data are clearly defined and can be mapped easily to HTTP verbs.&lt;/p&gt;  &lt;p&gt;Entity Framework also provides an important contribution: mapping. Very rarely you want to surface your database data as-is to the web, either to an application or as a service for external applications. You&amp;#x2019;ll want to adjust names, reshape data, merge tables, etc. The Entity Framework mapping layer can help with data, and with the VS integration we&amp;#x2019;ll include in the Entity Framework for EDM design and mapping, you&amp;#x2019;ll be able to do this in a nice visual experience.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Clearly, the world of data sources does not end with the Entity Framework&amp;#x2026;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Strictly from the data model perspective we clearly need to pick a single one for consistency, and EDM has the right characteristics for our needs. Now, from the perspective of the actual data sources that we can handle, it would not be reasonable to assume that every data source out there will be plugged into the Entity Framework. There are many scenarios where you want to surface data coming from other sources and you still want to use the Astoria URI and payload formats, along with the client libraries and tools that will be available for it.&lt;/p&gt;  &lt;p&gt;So we want to support Entity Framework *&lt;b&gt;and&lt;/b&gt;* other data sources. Now the trick is how to do this without basically writing Astoria twice :-), and maintain a sound, consistent data- and interaction-model.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;What do we need from a data source?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Astoria has a few specific needs when interacting with a data source:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Surface data as reasonable units we can expose as resources. For example, we could say that each CLR object is a resource&lt;/li&gt;    &lt;li&gt;Addressability: each resource (say CLR object) needs to be addressable. For us to create an address we need to be able to figure out what members are the &amp;#x201C;keys&amp;#x201D; on that resource&lt;/li&gt;    &lt;li&gt;Query composition: Astoria URIs are a simple form of queries. We need to be able to formulate and execute queries against the data source without knowing the specifics of the target data source. We also need to be able to &amp;#x201C;compose&amp;#x201D; queries; e.g. to take a given query and say add sort order to it&lt;/li&gt;    &lt;li&gt;Update: we need to be able to pull a bunch of resources (say, again, CLR objects), make some modifications, and then push the changes back into the data source.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;b&gt;Proposed approach&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Trying to avoid inventing new stuff, we could try to tackle this problem using the technology that was introduced as part of LINQ. The IQueryable interface provides a mechanism by which you can build a query by applying operators to input IQueryable objects, obtaining a new IQueryable object that includes the applied operator (there is plenty of blog entries and such on IQueryable on the web, so I won&amp;#x2019;t elaborate on it here; &lt;a href="http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx"&gt;Matt Warren&amp;#x2019;s series on IQueryable&lt;/a&gt; provide a great detailed reference for this.).&lt;/p&gt;  &lt;p&gt;In this approach we can say that your data service takes as input any class that has a set of public properties of type IQueryable&amp;lt;T&amp;gt;. For example:&lt;/p&gt;  &lt;p&gt;public class MyDataService : WebDataService&amp;lt;MyDataSource&amp;gt;&lt;/p&gt;  &lt;p&gt;{&lt;/p&gt;  &lt;p&gt;}&lt;/p&gt;  &lt;p&gt;Where MyDataSource could be something like:&lt;/p&gt;  &lt;p&gt;public class MyDataSource&lt;/p&gt;  &lt;p&gt;{&lt;/p&gt;  &lt;p&gt;&amp;#xA0; public IQueryable&amp;lt;SomeType&amp;gt; SomeThings {&amp;#x2026;}&lt;/p&gt;  &lt;p&gt;&amp;#xA0; public IQueryable&amp;lt;OtherType&amp;gt; OtherThings {&amp;#x2026;}&lt;/p&gt;  &lt;p&gt;}&lt;/p&gt;  &lt;p&gt;public class SomeType {&lt;/p&gt;  &lt;p&gt;&amp;#xA0; public int ID { get; set; }&lt;/p&gt;  &lt;p&gt;&amp;#xA0; public string SomeData { get; set; }&lt;/p&gt;  &lt;p&gt;&amp;#xA0; public ICollection&amp;lt;OtherThings&amp;gt; RelatedThings { get; }&lt;/p&gt;  &lt;p&gt;}&lt;/p&gt;  &lt;p&gt;public class OtherType { ... }&lt;/p&gt;  &lt;p&gt;That would result in a service with two top-level entity containers, /SomeThings and /OtherThings.&lt;/p&gt;  &lt;p&gt;Translation to LINQ expression trees is for the most part fairly straightforward. Following the example, let&amp;#x2019;s say that you now request the URI /SomeThings!1, we would translate it to roughly:&lt;/p&gt;  &lt;p&gt;myDataSourceInstance.SomeThings.Where(i =&amp;gt; i.ID == 1)&lt;/p&gt;  &lt;p&gt;(at least conceptually, we would build the expression trees directly and not go through the language form, of course).&lt;/p&gt;  &lt;p&gt;A more elaborate example would be association traversal, for example /SomeThings!1/RelatedThings; the translation for that would be:&lt;/p&gt;  &lt;p&gt;myDataSourceInstance.SomeThings.Where(i =&amp;gt; i.ID == 1).SelectMany(i =&amp;gt; i.RelatedThings)&lt;/p&gt;  &lt;p&gt;So we are basically saying that the Astoria server runtime has 2 halves. The top-half is the Astoria runtime itself; this part is &amp;#x201C;fixed&amp;#x201D;, and it implements URI translation,&amp;#xA0; the XML/JSON/etc wire formats, the interaction protocol, etc. It&amp;#x2019;s basically what it makes an Astoria service look like an Astoria service. The bottom half is the data-access layer and it&amp;#x2019;s pluggable. Communication between layers happens in terms of the IQueryable interface plus a set of conventions to map CLR graphs into the URI/payload patterns of Astoria.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Other options?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;An alternate approach would be to come up with a new interface. Astoria needs only a few operations to be supported by data sources, and the IQueryable interface is certainly overkill for the job. However, the fact that IQueryable already exists and there will be various implementations for it has a lot of value itself&amp;#x2026;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Net result&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;The net result that we want, through IQueryable or some other alternative, is that we can surface various data sources through Astoria, so that clients can interact with data services across the way using a single, uniform mechanism. The exposed HTTP interface is still EDM-ish even when the source is not the Entity Framework, and the metadata is still expressed in EDM terms for consistency. That helps maintain the simple, straightforward semantics of the Astoria HTTP interface.&lt;/p&gt;  &lt;p&gt;Now you can bring any data source you want and expose it through Astoria, provided that you have or can write an IQueryable implementation for it. Not only we&amp;#x2019;ll automatically hook it up in the Astoria pipeline, but also we&amp;#x2019;d push-down all the filters, sorting, and other operators, so the data source can efficiently implement them. &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Querying over the Entity Framework &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;It turns out that we build first-class LINQ support into the Entity Framework, we call the thing LINQ to Entities. That means that while we have a different code-path for metadata, the query composition and execution code paths, as well as all of the serialization and other details, is common code across Entity Framework and any other LINQ implementation plugged into Astoria.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;What&amp;#x2019;s missing&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Metadata: one thing I did not discuss here is how we turn CLR object graphs obtained by executing IQueryable objects into something that works well with the Astoria HTTP interface. There is a set of conventions that we&amp;#x2019;ll use, and a few attributes to override conventions when they don&amp;#x2019;t work for you. In a future post we&amp;#x2019;ll discuss those in detail.&lt;/p&gt;  &lt;p&gt;Update: while this model enables us to support querying over arbitrary .NET classes that expose containers of instances as IQueryable objects, it does not say how to update stuff. Discussing the update model will take a whole post (or several, most likely), but the short story is that we&amp;#x2019;ll define an interface, something like IUpdatable or whatever names works, that has the basic operations we need to perform in order to handle updates. The interface would have primitive operations for adding a new resource, remove an existing resource, applying modifications to resources and also handle linking/unlinking of resources.&lt;/p&gt;  &lt;p&gt;Pablo Castro   &lt;br /&gt;Technical Lead    &lt;br /&gt;Microsoft Corporation    &lt;br /&gt;&lt;a href="http://blogs.msdn.com/pablo"&gt;http://blogs.msdn.com/pablo&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#xA0;&lt;/p&gt;  &lt;p&gt;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 &lt;a href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;this post&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5176117" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item><item><title>URI Format - Part 1 - Addressing resources using URI path segments</title><link>http://blogs.msdn.com/astoriateam/archive/2007/09/21/uri-format-part-1-addressing-resources-using-uri-path-segments.aspx</link><pubDate>Fri, 21 Sep 2007 19:25:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5035268</guid><dc:creator>dpblogs</dc:creator><slash:comments>13</slash:comments><comments>http://blogs.msdn.com/astoriateam/comments/5035268.aspx</comments><wfw:commentRss>http://blogs.msdn.com/astoriateam/commentrss.aspx?PostID=5035268</wfw:commentRss><description>&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Deciding on something that becomes a public interface of a developer-oriented technology is a tricky task. Not only does the resulting design need to be correct and complete, but also there are various aspects that are more around aesthetics and personal preference. The URI format used by Astoria will need to survive both sets of challenges…&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;The Astoria REST “protocol” is made up of a URI addressing scheme, HTTP-based interaction model and payload formats (Web3S/XML,JSON , ATOM/APP).&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;In the interest of staying focused on the URI format, this write-up will only touch on the URI format used by Astoria and leave discussion of the interaction model to a future post.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;See &lt;/FONT&gt;&lt;A href="http://blogs.msdn.com/astoriateam/archive/2007/09/09/design-feedback-requested.aspx"&gt;&lt;FONT face=Calibri size=3&gt;this post&lt;/FONT&gt;&lt;/A&gt;&lt;FONT face=Calibri size=3&gt; for a discussion around payload formats used by Astoria.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;In general, Astoria takes a conceptual model expressed in terms of entities in an EDM schema and surfaces data that follows that model over an HTTP interface, representing entities as resources and associations between entities as links. The URI interface needs to provide a rich yet simple way of addressing those resources.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;The URI format in Astoria has a few specific goals:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 38.25pt; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"&gt;&lt;SPAN style="mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT face=Calibri size=3&gt;a)&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;B&gt;Provide a mechanism to point to every resource or member of a resource in the system.&lt;/B&gt; That is, every piece of data is addressable, and the URI used to address it needs to be derivable from the service metadata which describes the conceptual model of the system&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 38.25pt; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"&gt;&lt;SPAN style="mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT face=Calibri size=3&gt;b)&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;B&gt;Allow for simple queries to be formulated.&lt;/B&gt; That is, instead of pointing to a particular resource, allow URIs that express filtered sets of resources satisfying certain criteria&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 38.25pt; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"&gt;&lt;SPAN style="mso-fareast-font-family: Calibri; mso-bidi-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT face=Calibri size=3&gt;c)&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;B&gt;Support manipulating the presentation of results.&lt;/B&gt; This includes things such as sorting resources, paging over them and expanding related resources.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;This “part I” write up focuses on item a) above; pointing to resources and their members. We will discuss b) and c) in future blog posts&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;NOTE: the following descriptions use EDM terminology and constructs.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Regardless of the underlying data access layer (Entity Framework, Custom LINQ provider, etc) an Astoria service is exposing, the service is described using an EDM schema, so this description applies equally to any data source.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;In addition, typical REST verbiage (as is done above) refers to items pointed to by URIs as resources.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;In the remainder of this write up the term ‘entity’ should be interpreted as a synonym for ‘resource’.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Starting from the root&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;At the root of the service we are thinking of carrying the behavior of the CTP forward and putting all of the &lt;I&gt;resource sets&lt;/I&gt;, which are simply the list of entity sets we find in the EDM schema. These are addressed by name, separated by a forward-slash (“/”) from the service root URI. (e.g. …/northwind.svc/Customers, where “Customers” is a resource container).&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;A detail: In an EDM schema an entity set is contained within a single entity container and there may be multiple entity containers in the schema. If that’s the case, to access non-default containers, the names need to be container-qualified (e.g. “/NorthwindContainer.Customers”). The default one *&lt;B&gt;cannot&lt;/B&gt;* be qualified, to avoid introducing a redundant way of getting to it.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Pointing to a particular entity&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Every entity in an EDM schema has a key which consists of one or more of the properties in the entity. An entity key is unique within the containing entity set, so to identify an entity with a URI we need to include at least the entity-set and the key values. &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;--Location of keys&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;The key value could go before or after the question mark. That is, the URI could be built by adding a query parameter after the URI question mark as in:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/northwind.svc/Customers?key=23&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Or we could consider the entity-set-plus-value construct part of the URI namespace of the service and write it as&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/northwind.svc/Customers(23)&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;We prefer the second approach with the entity-sets and keys form a URI namespace and there is no query parameter required.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;One of the reasons for leaning towards the second approach is that it makes it explicit which entity set the key is associated with, especially when the URI path becomes quite long. &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;--a bit of syntax&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Now, assuming we go with that approach, there is now the question of the syntax. The May 2007 CTP used values in square-brackets (e.g. “…/Customers[ALFKI]”). We got “generous” feedback saying that square-brackets were a bad choice. &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;The approach we are currently thinking to take is to attach the key directly after the entity set name and using the ‘!’ character as the separator (e.g. …/Customers!23 ).&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;That said, as per our last “design” posting on formats, we are looking to support the Web3S format.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Web3S has a more flexible data model in that it allows heterogeneous sets while an Astoria server supports homogenous sets.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;To enable interoperability between any servers implementing Web3S and an Astoria server a URI scheme flexible enough to address heterogeneous sets is required.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;Therefore, we are thinking to expand on our current approach and allow a “full” form of URI and a “compressed” form, where the full form supports heterogeneous sets and the compressed form can be used as a shorthand notation when the set being addressed is on an Astoria server and is thus homogenous.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;For example:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;“Full” form: …/Customers/Customer(123) would identify the instance 123 of type Customer within the Customers set.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;“Compressed” form: …/Customers!123 would identify the same resource as above in Astoria because the ‘Customer’ type is implied since Astoria Servers support homogenous sets. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;--composite keys&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;One option would be to encode name/value pairs, but that would result in verbose URIs and extra syntax to be invented. We are leaning toward a simple approach: we only use the values, separated by semicolon. The values are listed in the same order as they appear in the metadata document which describes the service.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Metadata will be the topic of a future post, however, for now it’s enough to say in the typical case the description of a service will be available by making a GET request to …/$metadata.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;The following is an example of a URI which contains a composite key:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers!’ALFKI’,2&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Some folks love this, some hate it. The main concern from folks who hate it is readability: you cannot interpret the URI without the schema. Is that an issue? The alternate option of using name-value pairs is more explicit in this sense. We could have:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers!CustomerID='ALFKI',Key=2&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;The single-key case would still not require the name, and given that most cases will be single-key cases, compactness won’t suffer too much. An aspect of this that’s both good and bad is the fact that by using names you can specify the values in different order. That’s “handy”, but it means that these URIs are not useful for comparison as strings when trying to determine identity.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;-- literal forms&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Using just literal values in a composite key doesn’t really work, because now you cannot tell whether that’s a 2-element key or a 1 element key that happens to have a comma in it. So we need to use proper literal forms for the values. We will need that when we want to express query expressions such as filter predicates anyway, so we may as well be consistent and use a single literal form everywhere.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Literals:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri size=3&gt;Strings: a string surrounded by single-quotes, (e.g. 'ALFKI')&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri size=3&gt;Numbers: just the number, using US style (dot separates decimal digits)&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri size=3&gt;Dates: quoted as strings. Inside the string, use format described in RFC3339&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri size=3&gt;Guids: use the form &lt;/FONT&gt;&lt;SPAN style="FONT-SIZE: 8.5pt; FONT-FAMILY: 'Verdana','sans-serif'"&gt;“dddddddd-dddd-dddd-dddd-dddddddddddd”&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoListParagraph style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri size=3&gt;Binary: “0x” followed by two hex digits per byte (e.g. 0x1AB4)&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;So the examples above would actually be, for single- and composite-key respectively:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers('ALFKI')&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers('ALFKI',2)&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Probably is a good idea to &lt;B&gt;not&lt;/B&gt; allow spaces, as it would help making sure that URIs that mean the same thing are easily comparable.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Addressing members&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;To address a member of an entity, simply append the member to a URI that points to the entity, separated by a forward-slash. For example, if Customers have a CompanyName property:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers!'ALFKI'/CompanyName&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Note that addressing a member like that would return the member appropriately wrapped to conform to the negotiated MIME type. For example, if using XML you’d get the value wrapped in an XML element and annotated with the required namespaces and such.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;If you want just the value with no wrappers, you can use the /$value “magic member”. For example:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers('ALFKI')/CompanyName/$value&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;For a string, this would just return the string (text/plain) by default (same applies to all types but binary, which would return application/octet-stream). The developer can customize this by annotating the schema and indicating which MIME type a given value should be treated as. That would allow for example a text field to store HTML or a binary field to store an image, and HTTP responses would include the proper MIME type for them.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Association traversal&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;A special form of member access is when the member being accessed is actually a navigation property (a link in non EDM terms). Such a property can be considered a hard-link that resolves into the related entity (for associations that have a cardinality of 1 on the other end) or a set of entities (if the other end is “many”).&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;The syntax is the same as in regular members, independent of the cardinality of the other end. So, if a customer has sales orders, the URI to access the orders would be:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers!'ALFKI'/Orders&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Keep drilling down&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;When the result of a given URI is a single object, the members of those objects can be accessed by adding the member name to the URI. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;For example, if a customer has a “Contact” navigation property that points to a single Person object, which has a Name property, the name can be retrieved directly by using this URI:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers!'ALFKI'/Contact/Name&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;For the case where traversing an association yields a set, you need to further scope the set by providing a key to point to a single element of the set in order to traverse further using the URI path.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;For example if a customer has a set of orders and each order has an order date property, one valid URI to access an order date would be:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;…/Customers!'ALFKI'/Orders!123/OrderDate&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;A note on escaping&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Quite a bit of escaping beyond basic URI encoding is necessary for the whole scheme to work. Things like “=” and “?” need to be carefully handled to not confuse URI translators and agents. Details go beyond this write up, but specific thoughts are welcome. Although the trickiest one deserves to be brought up: should we escape “/” inside a quoted string? The same question, asked more deeply, is “should we assume that consumers of Astoria URIs understand their syntax?”.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;In general, we are leaning towards requiring characters of special meaning in a URI path (ex. ‘/’) to be escaped even when such a character is within a quoted string.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If the character is not escaped we will treat it as per its predefined meaning in path segments in RFC 3986.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;We believe this would provide a consistent method for developers to craft and interpret URIs.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;FONT face=Calibri&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;B&gt;&lt;FONT size=3&gt;Wrapping Up...&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT size=3&gt;The ideas presented above represent our current thinking in the space.&amp;nbsp; As always, feedback and comments are most welcome.&amp;nbsp; We look forward to hearing your thoughts.&amp;nbsp;&amp;nbsp; In follow up posts we will discuss the query string section of the URI and dig into addressing service operations.&lt;/FONT&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Mike Flasko&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Program Manager&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;A href="http://blogs.msdn.com/mflasko"&gt;&lt;FONT face=Calibri size=3&gt;http://blogs.msdn.com/mflasko&lt;/FONT&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Pablo Castro&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT face=Calibri size=3&gt;Tech Lead &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;A href="http://blogs.msdn.com/pablo"&gt;&lt;FONT face=Calibri size=3&gt;http://blogs.msdn.com/pablo&lt;/FONT&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp; &lt;/P&gt;
&lt;P&gt;&lt;SPAN lang=EN style="FONT-SIZE: 11pt; FONT-FAMILY: 'Calibri','sans-serif'; mso-ansi-language: EN"&gt;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 &lt;A href="http://blogs.msdn.com/astoriateam/archive/2007/07/20/transparency-in-the-design-process.aspx"&gt;&lt;FONT color=#0000ff&gt;this post&lt;/FONT&gt;&lt;/A&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt"&gt;&lt;o:p&gt;&lt;FONT face=Calibri size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5035268" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Project+Codename+_2600_quot_3B00_Astoria_2600_quot_3B00_/default.aspx">Project Codename &amp;quot;Astoria&amp;quot;</category><category domain="http://blogs.msdn.com/astoriateam/archive/tags/Design+Notes/default.aspx">Design Notes</category></item></channel></rss>