<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>public Blogger SPQ;</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/peter_qian/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/peter_qian/feed.xml</link>
    <description>Peter Qian&amp;#039;s blog - Data Developers Framework and Tools</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:25:24 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Microsoft Power Query in Power BI suite</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/microsoft-power-query-in-power-bi-suite</link>
      <pubDate>Mon, 08 Jul 2013 17:51:32 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2013/07/08/microsoft-power-query-in-power-bi-suite/</guid>
      <description><![CDATA[I'm really excited today to be able to write about what I was working on for the past year. It is...]]></description>
      <content:encoded><![CDATA[<p>I'm really excited today to be able to write about what I was working on for the past year. It is now officially announced as part of the "Power BI" suite - the Microsoft Power Query for Excel (<a href="https://office.microsoft.com/en-us/excel/download-data-explorer-for-excel-FX104018616.aspx">Download</a>).&nbsp;</p>
<p style="padding-left: 30px;"><span>Microsoft Power Query is an Excel add-in that enhances the self-service Business Intelligence experience in Excel by simplifying data discovery and access. Power Query enables users to easily discover, combine, and refine data for better analysis in Excel. Power Query includes a public search feature that is currently intended for use in the United States only.</span></p>
<p>This project was previously known as "Data Explorer". The new name reflects the direction that we are taking, plus, it happens to match my initials - so from now own I get to be PQ(PQ).</p>
<p>&nbsp;</p>
<p>Regards,</p>
<p>PQ(PQ)</p>]]></content:encoded>
    </item>
    <item>
      <title>Your WebAPP’s Worst Performance Bottleneck Maybe in the CSS Stylesheet</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/your-webapps-worst-performance-bottleneck-maybe-in-the-css-stylesheet</link>
      <pubDate>Mon, 07 May 2012 02:29:00 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2012/05/07/your-webapps-worst-performance-bottleneck-maybe-in-the-css-stylesheet/</guid>
      <description><![CDATA[Recently our team is making a big push for general UI performance improvements. Our UI layer is...]]></description>
      <content:encoded><![CDATA[<p>Recently our team is making a big push for general UI performance improvements. Our UI layer is mostly a HTML5 web app that sits on top of ASP.net MVC host. The javascript code base is rather old and poorly factored, so most of our initial efforts were focused onto profiling and cleaning the javascript. We had some success, but our UI is still sluggish in some areas. Soon we began investigating other browser activities like DOM queries and layouts. Using the Chrome developer tool&rsquo;s awesome &ldquo;timeline&rdquo; feature, we discovered that on the sluggish pages, &ldquo;recalculating styles&rdquo; is taking up to 2-3 seconds to complete!</p>
<p>This obviously got our attention, so we ran the CSS selector profiler (again, Chrome comes with it preloaded in the developer tools). It turns out that there are some regular expression based selectors that are taking 95% of the calculation time. For example:</p>
<pre class="scroll"><code class="css">[data-sprite$="0"]:before { ... }<br /> <br />[data-sprite$="13"]:before { ... }<br /><br />[class*="o-content"].role-link { ... }</code></pre>
<p>These selectors walk every single element in the DOM tree and do regular expression matching on its attributes. These searches cannot be indexed and therefore can be extremely slow. Because browsers need to restyle on certain events, it made our UI felt as if the javascript handlers are taking forever to run, while in fact the handlers weren&rsquo;t even called yet &ndash; the browser is busy restyling and not firing the events!</p>
<p>In our case the fix was simple: these selectors were part of an obsolete feature and were returning no matches. Simply deleting these CSS selectors boosts our UI responsiveness nearly 10 times in certain cases. Essentially we were paying a huge cost for a bunch of dead CSS selectors that nobody was using at all.</p>
<p>At the end of the day, a lesson learnt for all of us. So next time when you profile your Web App, make sure you look at the entire picture before jumping into javascript profiling, and also don&rsquo;t forget: dead CSS is truly deadly!</p>]]></content:encoded>
    </item>
    <item>
      <title>Serializing the WCF Data Service Client State</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/serializing-the-wcf-data-service-client-state</link>
      <pubDate>Mon, 21 Mar 2011 14:32:00 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2011/03/21/serializing-the-wcf-data-service-client-state/</guid>
      <description><![CDATA[With the release of Windows Phone 7, developers using WCF Data Service Client often find themselves...]]></description>
      <content:encoded><![CDATA[<p>With the release of Windows Phone 7, developers using WCF Data Service Client often find themselves needing to preserve the state their data context is in, and be able to continue from a previously saved state. This is called &ldquo;Tombstoning&rdquo; an app &ndash; the process which the phone framework notifies the app for shutting down, and later on coming back online. As a result we have been looking at designing a set of APIs to make saving and restore the client state an easy process. The already released <a href="https://www.microsoft.com/downloads/en/details.aspx?FamilyID=b251b247-70ca-4887-bab6-dccdec192f8d&amp;displaylang=en">CTP</a> contains a &ldquo;preview&rdquo; to what we think the API should look like. Since then we&rsquo;ve had a number of iterations on the design while incorporating feedbacks from the community. Here I&rsquo;ll talk about some of the issues we are trying to address, and some general guidelines on making your serialization experience better.</p>
<h4>Data Service State</h4>
<p>When I talk about the &ldquo;State of the client&rdquo;, what I mean is one DataServiceContext instance containing a graph of entities, and a set of root level collections (may or may not be DataServiceCollections) that serve as &ldquo;entry points&rdquo; into the graph. Consider your typical master-detail page bound to a query of Customers expanding each customer&rsquo;s Orders, in this scenario there is only one root level collection containing the customers. When you want to capture the state this client is in, you must consider both the context and the collection, although they will both eventually lead to the same set of entities. Hence the API we added in the CTP release requires the caller to pass in both. In this iteration we kept the same API (with a bit of renaming/refactoring around the return type), and I think it works best given the scenario we are going after. More details on that below. The new API will capture the sematic of &ldquo;serialization&rdquo;, and will return a string representing the serialized state. You can save this string to isolated storage or the application state, depending on how large your context is (larger states should be saved to isolated storage). These APIs are:</p>
<pre class="scroll"><code class="csharp">String DataServiceState.Serialized(DataServiceContext context, Dictionary&lt;String, Object&gt; rootCollections);
DataServiceState DataServiceState.Deserialize(String serializedState);</code></pre>
<h4>Graph, Navigation, Links, Relationships</h4>
<p>Serialization of entity graphs, or whatever you might call it, is the most important scenario develops are facing today that the CTP did not address properly. The essence of the problem is universal, not just limited to the data service world. It&rsquo;s how we represent references to the same object instance from multiple edges (including itself). If you&rsquo;ve tried our CTP library and used the DataServiceState class then you&rsquo;ve probably ran into this problem &ndash; and probably without knowing that you have. The symptom is non-trivial, you will most likely get an exception during deserialization complaining about &ldquo;An entity with the same identity has already been tracked&rdquo;, or worse, you could start making changes to your entities through the binding collections but not able to save these changes (SaveChanges does nothing). These are all caused by the fact that reference to the same entity instance has been deserialized into multiple copies. </p>
<p>Fortunately, the DataContractSerializer (used by NetCF to &ldquo;tombstone&rdquo; the application state) already has an API to deal with references. The way to do it on the NetCF is to mark ALL entities in the graph with DataContractAttribute, and set &ldquo;IsReference&rdquo; to true in the attribute declaration. In fact you can do this today with the CTP library and it will handle graphs nicely, although it will be a pain to do this by hand &ndash; note that if you mark a class with DataContract, then you must also mark its members with DataMember, since data contract uses an Opt-In approach. Because of this, we are also updating our code-gen assembly to automatically emit these attributes for you.</p>
<p>This does mean that you now absolutely have to bundle up everything related to your graph as a package and serialize that in one call. If you break away from this restriction then the graph will be broken, and the multi-instance problem is back. For example, if one of your class implements IXmlSerializable, and happens to have links back to the graph (a navigation property, for instance), then when DataContractSerializer encounters this object, it calls the WriteXml method, at which point the graph has been broken and you cannot continue to preserve reference for entities that are already written out. This is one restriction that one must live with &ndash; if you want to use IXmlSerializable on one of the entities, then you must use it on ALL entities, and do reference resolution on your own.</p>
<h4>Projections and Server Driven Paging</h4>
<p>Projection comes into play when you want to save a DataServiceQueryContinuation instance, the issue here is the additional &ldquo;client-side&rdquo; information that are captured by the query continuation are not easily serializable. These information are required to deserialize payloads returned from executing the continuation. For example, if you have a projection that look like:</p>
<pre class="scroll"><code class="csharp">from c in context.Customers
select new CustomerDisplay
{
    Name = c.FirstName + &ldquo; &ldquo; + c.LastName,
    Phone = c.Telephone == null ? &ldquo;-&ldquo; : c.Telephone 
}<br /></code></pre>
<p>This is an example of what we called a non-entity projection, which means an entity type is been projected into a complex type. Here information sent to the server is captured by the URI:</p>
<p>service.svc/Customers?$select=&rdquo;FirstName, LastName, Telephone&rdquo;</p>
<p>As you can see the uri itself is missing the necessary information to construct an instance of CustomerDisplay class. This missing information is referred to as &ldquo;Projection Plan&rdquo;, or &ldquo;Materialization Plan&rdquo;, which takes the form of an expression tree. It was built from the original query expression tree, and transformed into a set of Materialization calls. Expressions trees are in general non-serializable, so we must discard this during serialization. Because of this, serialization for DataServiceQueryContinuation of queries containing projections that introduce additional client-side information is not supported.</p>
<p>There is the other category of projections that we refer to them as &ldquo;entity projections&rdquo;, which means they only contain narrowing down a type, rather than transformation. For example:</p>
<pre class="scroll"><code class="csharp">from c in context.Customers
select new NarrowCustomer
{
    ID = c.ID,
    Name = c.Name,
    Orders = new DataServiceCollection&lt;NarrowOrder&gt;(
        from o in c.Orders
        select new NarrowOrder
        {
            ID = o.ID
            Customer = o.Customer
        })
}
</code></pre>
<p>The above query can be materialized by just executing the resulting URI directly, since the narrowing semantics is captured by the $select query options. This is a supported scenario. However, this is one more complication. If you examine the output URI for this query, you&rsquo;ll discover that the back reference to o.Customer is missing from the URI (it didn&rsquo;t output $select=&rdquo;Orders/Customer&rdquo;). This is an unfortunate side-effect for this round. We&rsquo;ll definitely be looking out how to make the experience better.</p>
<p>As a side note. I advice that you adopt a principal where everything you do on the context should be able to carry across the wire and deliver to the server side, either in the URI or the payload. A transforming projection cannot be expressed as an URI and carried out on the server side as of today, therefore in this case I advice that you bring whatever necessary information down to the client through entity projections first, then transform the result using Linq to Objects instead. </p>
<h4>Multithreading</h4>
<p>One should note that whenever you call Serialize, we take a snapshot of the context at that instance of time. This means if you are in the middle of an Asynchronous operation, the context will likely be in a transient state, and you might later have problems recovering from this situation. The general advice here is that you should save at certain &ldquo;check points&rdquo; where the state is known to you. For example, the WP7 development guideline advices that you save state whenever you navigate away from a page. This could be one solution out of many that are possible. Another possible check point is after all async operation completes. This also means that you definitely do not want to wait until the deactivating event and save there, since the event is triggered by the user and could happen at anytime.</p>
<h4>Guidance on Tombstoning the DataServiceContext</h4>
<p>The following are key take-away points to help you better support tombstoning in your data service app:</p>
<p>1. Mark all entity types as [DataContract(IsReference=true)], mark the member properties as [DataMember]</p>
<p>2. Do not use transforming projections, instead, do the transformation on entity projection results in memory.</p>
<p>3. Call DataServiceState.Serialize (DataServiceState.Save in the CTP) when you know the context is in a steady state &ndash; i.e., not in a middle of asynchronous operation. </p>
<p>4. In deactivation event, save the serialized string to IsolatedStorage if your context is large (100+ entities). Save to application state if your context is small (faster).</p>]]></content:encoded>
    </item>
    <item>
      <title>Using ASP.net Output Caching with WCF Data Services</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/using-asp-net-output-caching-with-wcf-data-services</link>
      <pubDate>Tue, 16 Nov 2010 19:05:00 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2010/11/16/using-asp-net-output-caching-with-wcf-data-services/</guid>
      <description><![CDATA[We all know hitting the database is an expensive operation, adding the cost of serialization on top...]]></description>
      <content:encoded><![CDATA[<p>We all know hitting the database is an expensive operation, adding the cost of serialization on top of that means that caching the output makes even more sense. The fact that WCF Data Services is built on top of the ASP.net platform means you can utilize all of its power to help you build a better service. This post examines the ASP.net output caching and how one would use it on WCF Data Service.</p>
<h2>Cache Variation and Static Service Caching</h2>
<p>ASP.net applications has the ability to cache the generated output on the server side. When a next matching request comes in, the output will then be delivered straight from the cache, rather than invoking the handler (calling data service). Note that this behavior applies to GET requests only. Note the word &ldquo;matching&rdquo; requests. How we match an request to a cached item is essential to delivering correct data to the customers. For a static (non-updating, we&rsquo;ll take a look at caching for writable services too) WCF data service endpoint, the output will change depending on the URI path, all of the query parameters, and headers as well (accept, charset, etc.). ASP.net output caching has this notion of &ldquo;VaryBy&hellip;&rdquo;, which essentially means how we match incoming requests to items in the cache table (of course, non-matching items are added to the table). MSDN has <a target="_blank" href="https://msdn.microsoft.com/en-us/library/06bh14hk(v=VS.71).aspx">an article</a> that discusses asp.net caching in detail, so I won&rsquo;t repeat what the parameters are here.</p>
<p>Let&rsquo;s setup our example using a standard Northwind service over Entity Framework, everything is very standard at this point:</p>
<pre class="scroll"><code class="csharp">namespace DataServiceCache 
{ 
    [ServiceBehavior(IncludeExceptionDetailInFaults=true)] 
    public class NorthwindService : DataService&lt;NorthwindEntities&gt; 
    { 
        public static void InitializeService(DataServiceConfiguration config) 
        { 
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3; 
            config.SetEntitySetAccessRule("*", EntitySetRights.All); 
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.All); 
            config.UseVerboseErrors = true; 
        } 
    } 
}</code></pre>
<p>Next, we override the OnStartProcessingRequest method to set the cache policy:</p>
<pre class="scroll"><code class="csharp">protected override void OnStartProcessingRequest(ProcessRequestArgs args) 
{ 
    base.OnStartProcessingRequest(args);

    HttpContext context = HttpContext.Current;    // set cache policy to this page 
    HttpCachePolicy cachePolicy = HttpContext.Current.Response.Cache;

    // server&amp;private: server and client side cache only 
    cachePolicy.SetCacheability(HttpCacheability.ServerAndPrivate);

    // default cache expire: never 
    cachePolicy.SetExpires(DateTime.MaxValue);

    // cached output depends on: accept, charset, encoding, and all parameters (like $filter, etc) 
    cachePolicy.VaryByHeaders["Accept"] = true; 
    cachePolicy.VaryByHeaders["Accept-Charset"] = true; 
    cachePolicy.VaryByHeaders["Accept-Encoding"] = true; 
    cachePolicy.VaryByParams["*"] = true;

    cachePolicy.SetValidUntilExpires(true); 
}</code></pre>
<p>We assume that this service is static here, (never changes shape), so we set the expire date to never expire (although ASP.net should auto adjust this back to expire in 1 year). Fire up the service now to test the cache &ndash; one indicator of whether the feed is cached is by examining the &ldquo;Updated&rdquo; timestamp. For debugging and testing purposes, let&rsquo;s add a call count to OnStartProcessingRequest to make sure data service is never called for subsequent requests:</p>
<pre class="scroll"><code class="csharp">private const string processedRequestCount = "ProcessedRequestCount";

protected override void OnStartProcessingRequest(ProcessRequestArgs args) 
{ 
    if (HttpContext.Current.Application.Get(processedRequestCount) == null) 
    { 
        HttpContext.Current.Application.Set(processedRequestCount, 1); 
    } 
    else 
    { 
        int count = (int)HttpContext.Current.Application.Get(processedRequestCount); 
        HttpContext.Current.Application.Set(processedRequestCount, count + 1); 
    }

    base.OnStartProcessingRequest(args); 
    &hellip; 
}</code></pre>
<p>We can retrieve this count via service operation. Note how we can control the service operation variation individually to &ldquo;no cache&rdquo;, this will cause the requests to /GetProcessedCount to always go through data service.</p>
<pre class="scroll"><code class="csharp">[WebGet] 
public int GetProcessedCount() 
{ 
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);

    int count = Convert.ToInt32(HttpContext.Current.Application.Get(processedRequestCount));

    HttpContext.Current.Application.Set(processedRequestCount, 0); 
    return count; 
}</code></pre>
<p>Next, let&rsquo;s quickly write a client &ndash;side app to test this:</p>
<pre class="scroll"><code class="csharp">var context = new NorthwindEntities(new Uri("https://localhost:85/NorthwindService.svc"));

int callCount = context.Execute<int>(new Uri("https://localhost:85/NorthwindService.svc/GetProcessedCount")).Single();

Console.WriteLine("Initial call count {0}", callCount);

IQueryable[] queries = new IQueryable[] { 
    context.CreateQuery<customer>("Customers").Skip(10).Take(10), 
    context.CreateQuery<customer>("Customers") , 
    context.CreateQuery<order>("Orders").Skip(10).Take(10) , 
    context.CreateQuery<order>("Orders") , 
    context.CreateQuery<customer>("Customers").Expand("Orders"), 
};

foreach (var q in queries) ((DataServiceQuery)q).Execute(); 
foreach (var q in queries) ((DataServiceQuery)q).Execute(); 
foreach (var q in queries) ((DataServiceQuery)q).Execute();

callCount = context.Execute<int>(new Uri("https://localhost:85/NorthwindService.svc/GetProcessedCount")).Single(); 
Console.WriteLine("Call count after batch querying {0}", callCount);</int></customer></order></order></customer></customer></int></code></pre>
<p>The output is:</p>
<p style="padding-left: 30px;">Initial call count 1   <br />Call count after batch querying 6</p>
<p>As you can see, invoking the query twice will not hit the data service twice. The second request is served directly from the cache. Also, ASP.net is smart enough to cache a different page for each of the entity set, and for each variation of the parameter. </p>
<h2>Cache Dependency and Writable Service Caching</h2>
<p>So far we&rsquo;ve looked up caching for static services. Not many services in the world are truly static, which makes caching kind of pointless if you cannot expire an cached item dynamically. The ASP.net output cache uses the idea of &ldquo;Dependencies&rdquo;, namely, an output cache item can take dependency on a data cache item &ndash; something you have full control over. When the latter one expires, the output cache will automatically expire too. This gives us a way to signal when we should clear the cache, and enables service writers to fine tune their expiration policy for better performances.</p>
<p>The basic idea is this, we can separate responses into cache-able groups, insert a dummy item into the data cache for each of the group, and then setup dependencies accordingly. Whenever we see a verb that&rsquo;s not a GET, that means change has happened to the data source. We can then decide which group to refresh by expiring the corresponding item in the data cache. Let&rsquo;s take the simplest approach for our example here &ndash; we expire ALL items related to the service whenever an update happens.</p>
<pre class="scroll"><code class="cplusplus">// group key 
private const string cacheDependencyItemKey = "DataServiceCacheItem";

protected override void OnStartProcessingRequest(ProcessRequestArgs args) 
{ 
    if (HttpContext.Current.Application.Get(processedRequestCount) == null) 
    { 
        HttpContext.Current.Application.Set(processedRequestCount, 1); 
    } 
    else 
    { 
        int count = (int)HttpContext.Current.Application.Get(processedRequestCount); 
        HttpContext.Current.Application.Set(processedRequestCount, count + 1); 
    }

    base.OnStartProcessingRequest(args);

    HttpContext context = HttpContext.Current;

    if (context.Request.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase) || 
        context.Request.HttpMethod.Equals("MERGE", StringComparison.OrdinalIgnoreCase) || 
        context.Request.HttpMethod.Equals("PUT", StringComparison.OrdinalIgnoreCase) || 
        context.Request.HttpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) 
    { 
        // if we are making changes to this service, expire all caches 
        context.Cache.Remove(cacheDependencyItemKey); 
    } 
    else 
    { 
        Debug.Assert(context.Request.HttpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase)); 
                
        // set cache policy to this page 
        HttpCachePolicy cachePolicy = HttpContext.Current.Response.Cache;

        // server&amp;private: server and client side cache only 
        cachePolicy.SetCacheability(HttpCacheability.ServerAndPrivate);

        // default cache expire: never 
        cachePolicy.SetExpires(DateTime.MaxValue);

        // cached output depends on: accept, charset, encoding, and all parameters (like $filter, etc) 
        cachePolicy.VaryByHeaders["Accept"] = true; 
        cachePolicy.VaryByHeaders["Accept-Charset"] = true; 
        cachePolicy.VaryByHeaders["Accept-Encoding"] = true; 
        cachePolicy.VaryByParams["*"] = true;

        cachePolicy.SetValidUntilExpires(true);

        // output cache has dependency on the data service cache item 
        context.Response.AddCacheItemDependency(cacheDependencyItemKey); 
    }

    if (context.Cache.Get(cacheDependencyItemKey) == null) 
    { 
        // what the cache item value is doesn't really matter 
        context.Cache.Insert(cacheDependencyItemKey, "Item"); 
    } 
}</code></pre>
<p>We can update the client-side application to test this:</p>
<pre class="scroll"><code class="cplusplus">var context = new NorthwindEntities(new Uri("https://localhost:85/NorthwindService.svc"));

var cust = context.CreateQuery<customer>("Customers").Take(1).FirstOrDefault(); 
context.UpdateObject(cust); 
context.SaveChanges();

int callCount = context.Execute<int>(new Uri("https://localhost:85/NorthwindService.svc/GetProcessedCount")).Single();

Console.WriteLine("Initial call count {0}", callCount);

IQueryable[] queries = new IQueryable[] { 
    context.CreateQuery<customer>("Customers").Skip(10).Take(10), 
    context.CreateQuery<customer>("Customers") , 
    context.CreateQuery<order>("Orders").Skip(10).Take(10) , 
    context.CreateQuery<order>("Orders") , 
    context.CreateQuery<customer>("Customers").Expand("Orders"), 
};

foreach (var q in queries) ((DataServiceQuery)q).Execute();

callCount = context.Execute<int>(new Uri("https://localhost:85/NorthwindService.svc/GetProcessedCount")).Single();

Console.WriteLine("Call count after query {0}", callCount);

foreach (var q in queries) ((DataServiceQuery)q).Execute(); 
foreach (var q in queries) ((DataServiceQuery)q).Execute(); 
foreach (var q in queries) ((DataServiceQuery)q).Execute();

callCount = context.Execute<int>(new Uri("https://localhost:85/NorthwindService.svc/GetProcessedCount")).Single(); 
Console.WriteLine("Call count after batch querying {0}", callCount);

context.UpdateObject(cust); 
context.SaveChanges();

foreach (var q in queries) ((DataServiceQuery)q).Execute(); 
foreach (var q in queries) ((DataServiceQuery)q).Execute(); 
foreach (var q in queries) ((DataServiceQuery)q).Execute();

callCount = context.Execute<int>(new Uri("https://localhost:85/NorthwindService.svc/GetProcessedCount")).Single(); 
Console.WriteLine("Call count after update and querying {0}", callCount);</int></int></int></customer></order></order></customer></customer></int></customer></code></pre>
<p>And the output is:</p>
<p style="padding-left: 30px;">Initial call count 3 &lt;&mdash;1 update, 1 get, 1 service op   <br />Call count after query 6    <br />Call count after batch querying 1     <br />Call count after update and querying 7</p>
<p>As you can see, requests will always go through data services after an update has occurred. In this example we grouped all items into one big group, that&rsquo;s probably not the optimal solution for most people. An entity-set based grouping should be sufficient, and you can implement this with Query and Change interceptors.</p>
<h2>Total Cache Control: The OutputCacheProvider</h2>
<p>Still not satisfied with the level of control you have? You can choose to implement the abstract class OutputCacheProvider, and hook it up via a simple config file change. Note that if you go this route, then you cannot add output page dependencies to data cache items. You&rsquo;ll have to implement the dependency logic inside your provider. I won&rsquo;t go into details here but the <a target="_blank" href="https://msdn.microsoft.com/en-us/library/system.web.caching.outputcacheprovider.aspx">MSDN documentation</a> and <a target="_blank" href="https://www.4guysfromrolla.com/articles/061610-1.aspx">this article</a> should help if you decided to go this route.</p>]]></content:encoded>
    </item>
    <item>
      <title>Using WCF Data Service With Restricted Characters as Keys</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/using-wcf-data-service-with-restricted-characters-as-keys</link>
      <pubDate>Mon, 24 May 2010 16:19:00 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2010/05/24/using-wcf-data-service-with-restricted-characters-as-keys/</guid>
      <description><![CDATA[If you are hosting your WCF Data Service on top of IIS+ASP.Net/WCF, then you may discover that there...]]></description>
      <content:encoded><![CDATA[<p>If you are hosting your WCF Data Service on top of IIS+ASP.Net/WCF, then you may discover that there are certain characters that will cause the server to throw when they are contained in entity keys. The result is either a 400 Bad Request or 404 Not Found. In VS 2010 RC, you can potentially configure the server to support these characters, but first, let&rsquo;s see what characters are considered &ldquo;special&rdquo;:</p>
<p style="padding-left: 30px; ">%,&amp;,*,:,&lt;,&gt;,+,#, /, ?,\</p>
<blockquote>
</blockquote>
<p>If any of the above characters are used inside a string key for an entity, then querying for the entity will resulting in an error, whether you escape the Uri or not.</p>
<p>The reason for these failures is actually many-fold. The main concern here is, of course, security. Allowing these characters can potentially lead to URI injection attacks and other security holes that leave your service vulnerable. Hence, <strong>before going forward and start allowing restricted characters, you should evaluate the security risks involved - </strong><strong><span style="text-decoration: underline;">these characters are restricted for good reasons</span></strong><strong>.</strong></p>
<p>The very first thing you can do is turning of ASP.net request filtering. You can do that by adding the following section in web.config:</p>
<pre class="scroll"><code class="html">&lt;httpRuntime requestPathInvalidCharacters="" requestValidationMode="2.0"/&gt; 
&lt;pages validateRequest="false"/&gt;</code></pre>
<p>This will allow the first six characters (%,&amp;,*,:,&lt;,&gt;) to be used in the path of request URI. If you just want to allow one of the six, you can add the rest to &ldquo;requestPathInvalidCharacters&rdquo;.</p>
<p>The next character is &lsquo;+&rsquo;, which when used inside a key will cause the server to throw 404 bad request. The reason for this is the IIS security filter rejecting double-escaped URL. You can allow this by adding the following into the web.config file:</p>
<pre class="scroll"><code class="html">&lt;system.webServer&gt; 
  &lt;security&gt; 
    &lt;requestFiltering allowDoubleEscaping="true" /&gt; 
  &lt;/security&gt; 
&lt;/system.webServer&gt;</code></pre>
<p>Now the next character is a bit tricky. &lsquo;#&rsquo; inside a string literal will cause WCF to truncate the Uri from that character and on (actually this may be a problem inside System.Uri), leaving a broken link to the server. You may think escaping the sequence will get you out of the problem &ndash; unfortunately the underlying host (either IIS or Asp.net) unescapes the Uri. Without a custom host, data service is normally two layers above ASP.net, with WCF host in between. Thus, WCF will see the unescaped URI passed out from ASP.net, and when it passes this URI out again to the service layer, it&rsquo;s already been truncated. Luckily, you can still retrieve the original request uri through ASP.net&rsquo;s HttpContext.Current.Request.Url. So to get around this problem, we are going to have to manufacture our own properly escaped URIs. To pass the homemade Uri to our server, we can use the IncomingMessageProperties discussed <a href="https://blogs.msdn.com/peter_qian/archive/2010/03/24/overwriting-the-service-root-uri-in-wcf-data-service.aspx">here</a>. Basically, we are bypassing WCF host to retrieve the request URI.</p>
<p>The following code is a simple proof of concept parser, and it&rsquo;s a very hacky one too. <strong><span style="text-decoration: underline;">It will not work in all scenarios and it&rsquo;s not meant to be used in production services</span>.</strong> More importantly, it may totally screw up your server and cause severe damage to your data. So if you must go ahead with this workaround, then you should spend some time and write a more comprehensive parser. I should also note that this is a problem with the WCF host sitting below the data service layer<strong>. If you are using a custom host then you probably won&rsquo;t see this problem, skip this workaround and go directly to the Uri configurations.</strong></p>
<p><strong></strong></p>
<pre class="scroll"><code class="csharp">public WcfDataService1() 
{ 
    string uri = HttpContext.Current.Request.Url.OriginalString; 
    StringBuilder replaceUri = new StringBuilder();<br />
    bool inquote = false; 
    for (int i = 0; i &lt; uri.Length; ++i) 
    { 
        switch (uri[i]) 
        { 
            case '\'': 
                replaceUri.Append(uri[i]); 
                inquote = !inquote; 
                break; 
            case '#': 
            case '\\': 
            case '/': 
            case '?': 
                if (inquote) 
                { 
                    replaceUri.AppendFormat("%{0:X}", (int)uri[i]); 
                } 
                else 
                { 
                    replaceUri.Append(uri[i]); 
                } <br />
                break; 
            default: 
                replaceUri.Append(uri[i]); 
                break; 
        } 
    }<br />
    OperationContext.Current.IncomingMessageProperties["MicrosoftDataServicesRequestUri"] = new Uri(replaceUri.ToString()); 
}</code></pre>
<p>You&rsquo;d noticed that this will also take care of the rest of the characters (\,/ and ?). Although they are caused by a different but similar problem &ndash; the underlying host unescapes URI and screws up the parser. However, to get the final three characters working, this is not enough. You also have to tell the Uri parser to allow slashes and question marks. This can be done by adding the following to the very top of the web.config file, just under the &lt;configuration&gt; session:</p>
<p>&nbsp;</p>
<pre class="scroll"><code class="html">&lt;configSections&gt; 
    &lt;section name="uri" type="System.Configuration.UriSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/&gt; 
&lt;/configSections&gt; 
&lt;uri&gt; 
    &lt;schemeSettings&gt; 
        &lt;add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes"/&gt; 
        &lt;add name="https" genericUriParserOptions="DontUnescapePathDotsAndSlashes"/&gt; 
    &lt;/schemeSettings&gt; 
&lt;/uri&gt;</code></pre>
<p>&nbsp;</p>
<p>And there you have it! You can now have any characters inside those string IDs and the server won&rsquo;t choke on it. You&rsquo;d also be happy to know that your service is now extremely vulnerable to URI attacks. So, after evaluating the security risks and if you decide to allow only a subset of these characters, you only have to enable the settings targeted specifically for them.</p>]]></content:encoded>
    </item>
    <item>
      <title>Overwriting the service root URI in Wcf Data Service</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/overwriting-the-service-root-uri-in-wcf-data-service</link>
      <pubDate>Tue, 23 Mar 2010 23:21:54 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2010/03/23/overwriting-the-service-root-uri-in-wcf-data-service/</guid>
      <description><![CDATA[Suppose you are hosting a WCF Data Service on a particular URL, but for some reason you’d like the...]]></description>
      <content:encoded><![CDATA[<p>Suppose you are hosting a WCF Data Service on a particular URL, but for some reason you’d like the consumers of your service to hit another URI – perhaps an IIS re-write, or a cookieless session. You can do this via setting one of the custom properties in the OperationContext:</p>  <p>OperationContext.Current.IncomingMessageProperties[&quot;<strong>MicrosoftDataServicesRootUri</strong>&quot;] = serviceUri;</p>  <p>You need to do this per instance of the data service, so it makes a lot of sense to do it in the service class’ constructor. Normally, WCF data service will try to resolve the absolute service URI by asking the underlying host. However, when there is a “MicrosoftDataServicesRootUri” key present, then the property will take precedence and become the service root URI. By default, the service root uri is used to generate identities and links in the content (such as edit links).</p>  <p>Doing just this, however, is not quite enough. Some of the links are generated by looking at the request uri, rather than the service root uri. For example, server driven paging’s next page link is constructed this way. Therefore, you also need to override request uri (and you need to do it per request, obviously). You do this in the “MicrosoftDataServicesRequestUri” property:</p>  <p>OperationContext.Current.IncomingMessageProperties[&quot;<strong>MicrosoftDataServicesRequestUri</strong>&quot;] = requestUri;</p>  <p>As a real world example, let’s take a look at the OData demo service located at <a href="https://services.odata.org/(S(readwrite))/OData/OData.svc/">https://services.odata.org/(S(readwrite))/OData/OData.svc/</a>. This service utilizes ASP.net cookieless session to allow multiple users reading/writing to the same structural data. If you follow this link, you’ll notice that the “readwrite” token inside the session ID will get replaced by an actual session ID, and from that point on all of the service content will be referred with this session key inside. For example:</p>  <blockquote>   <p><font color="#0000ff" face="Consolas">&lt;id&gt;<strong>https://services.odata.org/(S(v43ws2r0y00yx42zc220o2ci))/</strong>OData/OData.svc/Products(0)&lt;/id&gt;</font></p>    <p><font color="#0000ff" face="Consolas">&lt;title type=&quot;text&quot;&gt;Bread&lt;/title&gt;</font></p>    <p><font color="#0000ff" face="Consolas">&lt;summary type=&quot;text&quot;&gt;Whole grain bread&lt;/summary&gt;</font></p> </blockquote>  <p>By default, ASP.net will strip the session part of the URI out, leaving the service to think the host root is actually unsessioned. Therefore we need to utilize the above technique and “reconstruct” the sessioned uri (based on session IDs). We can construct the service root once per session, but the request URI must be reconstructed per request. Therefore, we ended up with the following code:</p>  <blockquote>   <p><font style="background-color: #ffff00" face="Consolas"></font><font color="#0000ff" face="Consolas">if (WebOperationContext.Current.IncomingRequest.UriTemplateMatch != null)        <br />{         <br />&#160;&#160;&#160; Uri serviceUri = HttpContext.Current.Session[&quot;ServiceUri&quot;] as Uri;         <br />&#160;&#160;&#160; Uri requestUri = null; </font></p>    <p><font color="#0000ff" face="Consolas">&#160;&#160;&#160; UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch; </font></p>    <p><font color="#0000ff" face="Consolas">&#160;&#160;&#160; if (serviceUri == null)        <br />&#160;&#160;&#160; {         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // create a service Uri that represent the current session         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; UriBuilder serviceUriBuilder = new UriBuilder(match.BaseUri);         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; serviceUriBuilder.Path = &quot;(S(&quot; + HttpContext.Current.Session.SessionID + &quot;))&quot; + serviceUriBuilder.Path; </font></p>    <p><font color="#0000ff" face="Consolas">&#160;&#160;&#160;&#160;&#160;&#160;&#160; serviceUri = serviceUriBuilder.Uri;        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; HttpContext.Current.Session[&quot;ServiceUri&quot;] = serviceUri;         <br />&#160;&#160;&#160; } </font></p>    <p><font color="#0000ff" face="Consolas">&#160;&#160;&#160; if (serviceUri != null)        <br />&#160;&#160;&#160; {         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; OperationContext.Current.IncomingMessageProperties[&quot;MicrosoftDataServicesRootUri&quot;] = serviceUri; </font></p>    <p><font color="#0000ff" face="Consolas">&#160;&#160;&#160;&#160;&#160;&#160;&#160; UriBuilder requestUriBuilder = new UriBuilder(match.RequestUri);        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; requestUriBuilder.Path = &quot;(S(&quot; + HttpContext.Current.Session.SessionID + &quot;))&quot; + requestUriBuilder.Path;         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // because we have overwritten the Root URI, we need to make sure the request URI shares the same host         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // (sometimes we have request URI resolving to a different host, if there are firewall re-directs         <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; requestUriBuilder.Host = serviceUri.Host; </font></p>    <p><font color="#0000ff" face="Consolas">&#160;&#160;&#160;&#160;&#160;&#160;&#160; requestUri = requestUriBuilder.Uri;        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; OperationContext.Current.IncomingMessageProperties[&quot;MicrosoftDataServicesRequestUri&quot;] = requestUri;         <br />&#160;&#160;&#160; }         <br />}</font><font style="background-color: #ffff00" face="Consolas"></font></p> </blockquote>  <p>NOTE: the above source code is from the OData service sample code library, located at <a title="https://www.odata.org/developers/odata-sdk" href="https://www.odata.org/developers/odata-sdk">https://www.odata.org/developers/odata-sdk</a> and under the MPL license.</p>]]></content:encoded>
    </item>
    <item>
      <title>SDET Openings in Our Team</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/sdet-openings-in-our-team</link>
      <pubDate>Tue, 26 Jan 2010 13:57:01 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2010/01/26/sdet-openings-in-our-team/</guid>
      <description><![CDATA[For those of you who love the technology, and want to put your amazing skills to good use – well,...]]></description>
      <content:encoded><![CDATA[<p>For those of you who love the technology, and want to put your amazing skills to good use – well, now it’s your chance! The Data and Modeling Group at Microsoft has two open Software Development Engineer in Test positions, you can find the description <a href="https://careers.microsoft.com/JobDetails.aspx?ss=&amp;pg=0&amp;so=&amp;rw=1&amp;jid=11878&amp;jlang=EN">here</a> and <a href="https://careers.microsoft.com/JobDetails.aspx?ss=&amp;pg=0&amp;so=&amp;rw=1&amp;jid=11877&amp;jlang=EN">here</a>.</p>  <p>Don’t let this exciting opportunity just slip away!</p>]]></content:encoded>
    </item>
    <item>
      <title>WCF Data Service V2 and the updated JSON format</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/wcf-data-service-v2-and-the-updated-json-format</link>
      <pubDate>Wed, 20 Jan 2010 20:47:21 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2010/01/20/wcf-data-service-v2-and-the-updated-json-format/</guid>
      <description><![CDATA[In V1 of WCF Data Service (aka ADO.net Data Service), the JSON serialization format for entity sets...]]></description>
      <content:encoded><![CDATA[<p>In V1 of WCF Data Service (aka ADO.net Data Service), the JSON serialization format for entity sets look like:</p>  <p><font color="#0000ff" face="Consolas">{ &quot;d&quot; : [ {entity1}, {entity2} ] }</font></p>  <p>Where “d” is the “data” wrapper, used as a security measure, and it is defined as the array of elements in the entity set. For example, if you hit a V1 service today, you will see the following response:</p>  <p><font color="#0000ff" face="Consolas">{      <br />&#160; &quot;d&quot; : [ {      <br />&#160;&#160;&#160; &quot;__metadata&quot;: {      <br />&#160;&#160;&#160;&#160;&#160; &quot;uri&quot;: &quot;$identity&quot;,       <br />&#160;&#160;&#160;&#160;&#160; &quot;etag&quot;: &quot;$etag&quot;,       <br />&#160;&#160;&#160;&#160;&#160; &quot;type&quot;: &quot;$type&quot;      <br />&#160;&#160;&#160; },       <br />&#160;&#160;&#160; &quot;NavigationProperty&quot;: {      <br />&#160;&#160;&#160;&#160;&#160; &quot;__deferred&quot;: {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;uri&quot;: &quot;$link&quot;      <br />&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; },      <br />&#160;&#160;&#160; &quot;Property&quot; : Value      <br />&#160; },      <br />&#160; ...      <br />&#160; ]      <br />}</font></p>  <p>This format has to be changed in V2 because we now have some additional attributes that are entity-set specific. Specifically, row count and server driven paging skip tokens. We originally thought that we can stuff then inside the data array as the first element and the last element of the array, as this would prevent a change of format. If you have followed our development closely, you would have seen this behavior in the V1.5 CTP1 release. We soon realized that having a mixed type array that sometimes gives the user additional information offers very poor usability. So we finally decided to “upgrade” our JSON format to 2.0, which will let us put the entity set level information where they logically belong to – well, on the entity set.</p>  <p>The new format changes the “d” wrapper from the actual entity array to a new object with multiple properties. The data now lives under “results”, the entity set level information will each become a property in that same object:</p>  <p><font color="#0000ff" face="Consolas">{     <br />&#160; &quot;d&quot; : {      <br />&#160;&#160;&#160; &quot;results&quot;: [ {entity1}, {entity2}, ... ],      <br />&#160;&#160;&#160; &quot;__count&quot;: &quot;$count&quot;      <br />&#160;&#160;&#160; &quot;__next&quot; : &quot;$next-link&quot;      <br />&#160; }      <br />}</font></p>  <p>The serialization format for the individual entities stays the same. </p>  <p>To maintain backwards compatibility, the server will only output in the new format if a V2 specified feature had been requested. That is, when the server has to use a DSV=2.0 response (see my post on <a href="https://blogs.msdn.com/peter_qian/archive/2009/09/28/versioning-protocol-of-astoria-ii.aspx">versioning</a>). For the “normal” V1 requests, the old format will be used.</p>  <p>This does cause some pain when you try to support multiple versions of requests. You now must read the data service version header from the response, and decide which JSON parser you use to retrieve the data. </p>]]></content:encoded>
    </item>
    <item>
      <title>Versioning Protocol of Astoria II</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/versioning-protocol-of-astoria-ii</link>
      <pubDate>Mon, 28 Sep 2009 13:17:00 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2009/09/28/versioning-protocol-of-astoria-ii/</guid>
      <description><![CDATA[In my previous post on the protocol versioning scheme for Astoria, I've explained the two HTTP...]]></description>
      <content:encoded><![CDATA[<p>In my previous post on the protocol versioning scheme for Astoria, I've explained the two HTTP headers DataServiceVersion and MaxDataServiceVersion. These headers are used by the server and client library to determine how and what to communicate with each other. This mechanism is defined by the Astoria Protocol and our library's implementation does not require any user interactions. There is no public APIs to give users direct control of mechanism either. In fact, in the most common scenario where the server and the client versions match, one can simply ignore the existence of version control. However, if you ever need to setup a data service where the server and the consumer are built from two different versions of Astoria, you'll likely want to know what is expected to work and what is not. </p>  <p>With the release of CTP2, we have introduced a couple of new features on the server. In the table below I'll list these features and their behavior when it comes to cross-version calls. In the table, minimum requesting DataServiceVersion means the client must send a DSV header that's greater than or equal to this value, and the response DSV means the server will response with this version (which also means the MDSV header sent by the client must be less than or equal to this value). </p>  <table border="1" cellspacing="0" cellpadding="0"><tbody>     <tr>       <td valign="top" width="22%">         <p>Feature</p>       </td>        <td valign="top" width="33%">         <p>Call Uri</p>       </td>        <td valign="top" width="22%">         <p>Minimum Requesting DataServiceVersion</p>       </td>        <td valign="top" width="22%">         <p>Response DataServiceVersion</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">         <p>Friendly Feeds</p>       </td>        <td valign="top" width="33%">         <p>Any Entity With EPM mapping and KeepInContent set to <b>false</b></p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>        <td valign="top" width="22%">         <p>2.0(ATOM) / 1.0(JSON)</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">&#160;</td>        <td valign="top" width="33%">         <p>Any Entity With EPM mapping and KeepInContent set to <b>true</b></p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">         <p>Server Driven Paging</p>       </td>        <td valign="top" width="33%">         <p>Any EntitySets that are paged and the number of requested entities are <b>greater</b> than the page size</p>       </td>        <td valign="top" width="22%">         <p>1.0 (First page) / 2.0 (Subsequent pages with $skipToken)</p>       </td>        <td valign="top" width="22%">         <p>2.0</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">&#160;</td>        <td valign="top" width="33%">         <p>Any EntitySets that are paged and the number of requested entities are <b>less</b> than the page size</p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">         <p>Row Count</p>       </td>        <td valign="top" width="33%">         <p>/EntitySet?$inlinecount=allpages</p>       </td>        <td valign="top" width="22%">         <p>2.0</p>       </td>        <td valign="top" width="22%">         <p>2.0</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">&#160;</td>        <td valign="top" width="33%">         <p>/EntitySet/$count</p>       </td>        <td valign="top" width="22%">         <p>2.0</p>       </td>        <td valign="top" width="22%">         <p>2.0</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">         <p>Blob</p>       </td>        <td valign="top" width="33%">         <p>Accessing media links</p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>     </tr>      <tr>       <td valign="top" width="22%">         <p>Projections</p>       </td>        <td valign="top" width="33%">         <p>/EntitySet?$select=propertyList</p>       </td>        <td valign="top" width="22%">         <p>2.0</p>       </td>        <td valign="top" width="22%">         <p>1.0</p>       </td>     </tr>   </tbody></table>  <p>The rule of thumb here is<u> we require the requesting DSV to be 2.0 for all new URI constructs ($inlinecount, $count, $skipToken, $select), and the response DSV will be 2.0 if the content is incompatible with V1 response.</u> For example, for Server Driven Paging, when the server truncates the result set, we will bump the response DSV to 2.0, because V1.0 client will ignore the continuation token and thus unable to receive the full results. </p>  <p><strong><u>The Max Protocol Version configuration</u></strong> </p>  <p>For folks out there wanting to use V2 service and want their service to be V1 protocol compliant, you can &quot;turn off&quot; the features that <b>may</b> require a DSV of 2.0 by setting the MaxProtocolVersion configuration*. This setting is available only during service initialization: </p>  <blockquote>   <p><font color="#0000ff" size="2" face="Consolas">public static void InitializeService(DataServiceConfiguration config)       <br /></font><font color="#0000ff" size="2" face="Consolas">{       <br /></font><font face="Consolas"><font color="#0000ff"><font size="2">&#160;&#160;&#160; config.SetEntitySetAccessRule(&quot;*&quot;, EntitySetRights.All);           <br /></font><font size="2">&#160;&#160;&#160; <b>config.DataServiceBehavior.MaxProtocolVersion = </b></font></font></font><font face="Consolas"><font color="#0000ff"><font size="2"><b>DataServiceProtocolVersion.V1;</b>            <br /></font><font size="2">}</font></font></font>&#160;&#160; </p> </blockquote>  <p>You can think of this as the service-side enforced &quot;MaxDataServiceVersion&quot; header. When this is set, any request that requires a DSV greater than 1.0 will fail even if the proper headers are present. The error message you get in this case is &quot;The response requires that version 2 of the protocol be used, but the MaxProtocolVersion of the data service is set to DataServiceProtocolVersion.V1&quot;, and the status code of the reply is BadRequest(400).</p>  <p><b><span style="text-decoration: underline" class="Apple-style-span">*NOTE</span></b><span style="text-decoration: underline" class="Apple-style-span">: We have decided to ship V2 with </span><b><span style="text-decoration: underline" class="Apple-style-span">MaxProtocolVersion default to V1</span></b><span style="text-decoration: underline" class="Apple-style-span">, for safety and compatibility reasons. You MUST explicitly set this to V2 in order to use any of the V2 features.</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Working with Local Entities in Astoria Client</title>
      <link>https://docs.microsoft.com/archive/blogs/peter_qian/working-with-local-entities-in-astoria-client</link>
      <pubDate>Thu, 23 Apr 2009 17:23:34 GMT</pubDate>
      <dc:creator><![CDATA[Peter Qian - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/peter_qian/2009/04/23/working-with-local-entities-in-astoria-client/</guid>
      <description><![CDATA[Although the Astoria client is really designed to query data existing in some remote service...]]></description>
      <content:encoded><![CDATA[<p>Although the Astoria client is really designed to query data existing in some remote service location, it does keep a local copy of all entities it has seen. If the MergeOption is not set to NoTracking, you can find all of the entities currently been tracked in the DataServiceContext.Entities property. The property is defined as:</p>  <p><font face="Lucida Console" color="#0000ff" size="2">ReadOnlyCollection&lt;EntityDescriptor&gt; Entities</font></p>  <p>where EntityDescriptor is a class that gives you access to the entity instance, the ETag and the state of the entity (changed, deleted, etc). The easiest way to query local copies of entities is to use LINQ to Objects. For example, if you are looking for a Customer who’s CustomerID is 0, and you want to see if it exists locally first, then you can write:</p>  <p><font face="Lucida Console" color="#0000ff" size="2">var q = from ed in Context.Entities      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; where ed.Entity is Customer&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &amp;&amp; ((Customer)ed.Entity).ID == 0       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; select (Customer)ed.Entity;       <br />      <br />Customer cust = q.FirstOrDefault();</font>     <br /></p>  <p>In the above example, when cust is not null, you can work with the copy and still have the context track the changes you make to the entity. If cust is null, you can then query the remote service and the next time you run this again, a local copy will exist.</p>  <p>The key to this is, of course, is to have a singleton DataServiceContext for all of your data access needs. In a sense, you are keeping a local &quot;data store” and when possible, use it’s data instead of the remote one. Ultimately, you would want to have it retrieve remote data on demand.</p>  <p>A simple data store may look like this:</p>  <p><font face="Lucida Console" color="#0000ff" size="2">public class DataStore      <br />{       <br />&#160;&#160;&#160; static DataServiceContext ctx = new DataServiceContext(new Uri(“ServiceUri”, UriKind.Absolute)); </font></p>  <p><font face="Lucida Console" color="#0000ff" size="2">&#160;&#160;&#160; public static DataServiceContext Context      <br />&#160;&#160;&#160; {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; get { return ctx; }       <br />&#160;&#160;&#160; } </font></p>  <p><font face="Lucida Console" color="#0000ff" size="2">&#160;&#160;&#160; public static IQueryable&lt;T&gt; QueryLocal&lt;T&gt;()      <br />&#160;&#160;&#160; {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return (from ed in ctx.Entities.AsQueryable()       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; where ed.Entity is T       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; select (T)ed.Entity);       <br />&#160;&#160;&#160; } </font></p>  <p><font face="Lucida Console" color="#0000ff" size="2">&#160;&#160;&#160; public static IQueryable&lt;T&gt; QueryLocal&lt;T&gt;(Expression&lt;Func&lt;T,bool&gt;&gt; predicate)      <br />&#160;&#160;&#160; {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return (from ed in ctx.Entities.AsQueryable()       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; where ed.Entity is T       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; select (T)ed.Entity).Where(predicate);       <br />&#160;&#160;&#160; }       <br />}</font></p>  <p>With this setup, you can query the remote store with DataStore.Context.CreateQuery&lt;T&gt;(), and you can query the local store with DataStore.QueryLocal&lt;T&gt;(). The above Customer example simply become:</p>  <p><font face="Lucida Console" color="#0000ff" size="2">Customer cust = DataStore.QueryLocal&lt;Customer&gt;(c=&gt;c.ID==0).FirstOrDefault();</font></p>  <p>Taking it one step further, you can imagine having a unified “QueryStore&lt;T&gt;()” function that queries local data first, and if it didn’t find anything, it would go ahead and ask the service for data. This approach can significantly reduce the amount of data you need to move across the wire for your service.</p>  <p>Of course, just like everything else in life, this approach also has trade-offs. The biggest one comes from the fact that the local copies are not “partitioned” or “indexed” in anyway. This means even if all you are looking for is one customer, you may have to loop through thousands of other irrelevant entities. In situation like this, you must make a decision to either minimize the bandwidth or maximize the performance – it could be faster if you just query the remote service instead. In short, whether having a local data store suites your needs depends on the structure of your data. Applied currently, it can significantly optimize your data service experience.</p>]]></content:encoded>
    </item>
  </channel>
</rss>