Windows Phone 7 HttpWebRequest returns same response from Cache

I hit an problem doing some dev on Windows Phone 7 several weeks back and have since helped a couple people out in the old windows phone 7 forum and LinkedIn Windows Phone 7 user group with the same thing.  This issue, is in fact, not an issue – it is more of a lack of knowledge that the HttpWebRequest and WebClient cache responses for your web request.  Let me tell you that client side caching is a good thing that will save you bandwidth and also speed up the percieved user experience when requesting the same resource.  So probably best to get straight to where problems can be faced. 

If you are performing a HttpWebRequest to a uri like so:

string someUri = "http://someservice/service/GetContent/userid/";
HttpWebRequest request = HttpWebRequest.CreateHttp(someUri);            
request.Accept = "application/json"; 

request.BeginGetResponse(OnGetSomeontentCallback, new GetSomeContentState(request, onResultAction)); 

Now lets pretend your service implementation has some smarts around what the user has seen historicaly and returns a different image each time you request the same uri.  You will find on your second call to the service that the same image is displayed in your app as the first call and then you might scratch your head *scratch*, check your service logic – all looks ok, set a breakpoint in your service and bang – the breakpoint is not being hit in the service on the second call and suddently is becomes clear the HttpWebRequest (and WebClient) for that matter will cache the response for that uri. 

So your faced with the following question – Should I change my service interface or change/remove the client side caching for this service operation?.   Well the answer is application specific and will fall into two categories:

1. Your client application knows what resource should be next

  • In this case you should alter your service interface to accept the Id of the item this way the result will be cached only for each specific item.

2. Your client has no idea what should be next as the server figures it out 

  • In this scenario i have identified 3 options – initialy i though you would be able to change the cache to expire or not cache on the HttpWebRequest instance but this is not in fact possible so you have to look at the uri and/or serverside.   Here are 3 options: 

Option 1: Add a random Query String to the end of your URI (think Guid.NewGuid()) this will avoid caching on the client as the Query String will be different each time

Option 2. Specify no cache in the OutgoingResponse header within your WCF service operation:


public ResultABC GetContent(string abc)
{
...
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");  //This line
...
return ...;
}

Option 3. markup your service operation with the AspNetCacheProfile attribute:

[AspNetCacheProfile("GetContent")]
public ResultABC GetContent(string abc)
{
...
...
return ...;
}

update your web.config


<system.web>
<caching>
     <outputCache enableOutputCache="true" />
     <outputCacheSettings> 
        <outputCacheProfiles > 
            <add name="GetContent" duration="0" noStore="true" location="Client" varyByParam="" enabled="true"/> 
        </outputCacheProfiles> 
    </outputCacheSettings>
</caching>
...
</system.web>

What did I choose:
Well all methods work to avoid the particular scenario given.  But in my case, the client application never knows what is next so I was forced to stop the client side cachingso the one I opted for was Option 3 as externalising this configuration means you can change at any later time without needing to rebuild and deploy your service.  

 If my client did know what the user was requesting then I would have gone for passing the ID in the uri to ensure that it is unique per each resource and that each resource would be cached on its unique uri.  Note here it is important for the ID to be passed on the URI it appears the URI forms the key for the cache i did see one example where the user was passing the ID in the Header collection e.g wc.Header["ID"] = id; and this was exhibiting the same problem.  Moving the ID to the URI solves the problem.

Hope this has been helpful,

Nick

 

I Won a Windows Phone 7 at PDC Replay

Hi there readers,

Last night i was at The SAUG  I was thinking about how far away the dream of owning one of these Windows Phone 7 devices would be especially since i had resigned from my job almost three months ago, sacraficing my salary to embark on a dream Development Adventure  that utilisies some of the techonologies I have been blogging about over the past few months – Azure, Windows Phone 7 and ASP .NET MVC 2 + 3(beta)

This is particularly great news and i am still quite excited so here it is  - I am now the happy new owner of a Windows Phone 7 – its just not delivered yet and in my excitement I forgot to ask which Model it was going to be. I haven’t had a chance to play with the hardware although have heard good things - I am really hoping it will be a HTC or Samsung model – but I guess I will see what comes :) .

A big thanks to Microsoft and their PDC replay event this morning at 9am and a very special thanks to Andrew Coates who asked the question about Windows Phone 7 that managed to win me this device :) .  I would list the Q&A but there is another replay session occuring now so dont want to spoil it of anyone.

Nick :) :) :)

Windows Phone Developer Tools October 2010 Update

The Windows Phone Developer Tools October 2010 Update includes:
  • Windows Phone Capability Detection Tool – Detects the phone capabilities used by your application. When you submit your application to Windows Phone Marketplace , Microsoft performs a code analysis to detect the phone capabilities required by your application and then replaces the list of capabilities in the application manifest with the result of this detection process. This tool performs the same detection process and allows you to test your application using the same list of phone capabilities generated during the certification process. For more information, see How to: Use the Capability Detection Tool.
  • Windows Phone Connect Tool – Allows you to connect your phone to a PC when Zune® software is not running and debug applications that use media APIs. For more information, see How to: Use the Connect Tool.
  •  Updated Bing Maps Silverlight Control – Includes improvements to gesture performance when using Bing™ Maps Silverlight® Control.

Download Here

FREE Event – The Sydney Architecture User Group – Open ID vs OAuth Identity on the web

: Open ID vs OAuth : Identity on the web
Lewis Benge
Thursday 28/10/2010 06:30 PM
Grace Hotel , Kiralee or Pinaroo Function Room 77 York st Sydney,NSW. 2000

 
Lewis is the only English speaking Commerce Server MVP in the world. Lewis has extensive experience in all areas of eCommerce and knows what it takes to enable an organisation to embrace eCommerce. Lewis has extensive experience with clients such as Video Ezy and a host of other high profile clients.  We hope to see you there. Dont forget to visit http://thesaug.org to keep up to date with the user group details and news.
 
- Thanks

ASP .NET MVC 3 Beta Deploy to Azure Cycles

If you have just downloaded ASP .NET MVC 3 Beta and upgraded your ASP .NET MVC 2 website then tried to deploy on Azure and found that your deploy is cycling this will likely solve your problem.

Recall while upgrading for MVC 3 Beta you update your project to reference System.Web.Mvc.dll (v3.0.0.0), System.WebPages.dll and System.Web.Helpers.dll – well the likely cause of the issue is you have not updated those three assembly references to copy local i.e Right Click >> Copy Local = True. If you were to deploy at this stage you would still have the cycle issue.  With a quick review of intellitrace logs you you have to add a reference to WebMatrix.Data.dll, System.Web.WebPages.Razor.dll and Microsoft.Web.Infrastructure.dll all of which when run on local are retrieved from the GAC. Therefore the full list of assemblies to copy local becomes:

  • System.Web.Mvc.dll (v3.0.0.0)
  • System.WebPages.dll
  • System.Web.Helpers.dll
  • WebMatrix.Data.dll
  • System.Web.WebPages.Razor.dll
  • Microsoft.Web.Infrastructure.dll
  • Hope this saves you some time
    Nick

    A first look at the ASP .NET MVC 3 WebGrid

    I just installed ASP .NET MVC 3 Beta. This post provides a brief introduction to the new WebGrid.
    Note:

    • At the time of writing the ASP .NET MVC 3 was in Beta
    • I am not yet using Razor for my view markup
    • Sorry about the formatting in the syntax highlighting on this one
    1. How to add an ActionLink Edit column to the ASP .NET MVC 3 WebGrid.
      • To start lets look at a cut down version of the original ASP .NET MVC 2 View generated (except the delete action) using the built in scaffolding:
      • <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<ABC.Web.Models.ModelX>>" %>
        <table>
        <tr>
        <th></th>
        <th>
           Title
        </th>
        <th></th>
        </tr>
        <% foreach (var item in Model) { %>
           <tr>
           <td>
              <%: Html.ActionLink("Edit", "Edit", new { id=item.Id }) %>
           </td>
           <td>
              <%: item.Title %>
           </td>
           <td>
              <a onclick="deleteRecord('Cart', '<%= item.Id %>')" href="JavaScript:void(0)">Delete</a>
           </td>
           </tr>
           <% } %>
        <p>
        <%: Html.ActionLink("Create New", "Create") %>
        </p>
      • Replacing this with the ASP .NET MVC 3 Beta WebGrid can be performed as follows:
      • <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<ABC.Web.Models.ModelX>>" %>
        <%
        var grid = new System.Web.Helpers.WebGrid(Model, new List<string>(){"Title"});
        %>
        <%= grid.GetHtml(columns: grid.Columns(
              grid.Column(format: (item) => Html.ActionLink("Edit", "Edit", new { id = item.Id })),
              grid.Column("Title"),
              grid.Column(format:(item) => Html.ActionLink("Delete", "Delete", null, new {onclick=string.Format("deleteRecord('Cart', '{0}')", item.Id), @class="Delete", href="JavaScript:void(0)"}))
        )
        ) %>
        <p>
        <%: Html.ActionLink("Create New", "Create") %>
        </p>

        The important point to note here is that to get your Edit ActionLink you need to provide a Column definition with the format parameter specified such that it returns your ActionLink i.e

        grid.Column(format: (item) => Html.ActionLink("Edit", "Edit", new { id = item.Id }))
    2. How to enable/disable the pager and change the page size.
    3. Straight out of the box the WebGrid supports paging which by default is enabled with a page size of 10 but can be configured using the rowsPerPage and canPage attributes as follows:

      <%
      var grid = new System.Web.Helpers.WebGrid(Model,
                   columnNames: new List<string>(){"Title"},
                   rowsPerPage:5,
                   canPage:true);
      %>
    4. How to emit CSS class attributes to your table
    5. The last thing worth mentioning in this introductory post is that the css class can also be specified for the table using the following parameters tableStyle, headerStyle, footerStyle, rowStyle and alternateRowStyle style as follows:

      <%= grid.GetHtml(
         tableStyle: "table",
         headerStyle: "header",
         footerStyle: "footer",
         rowStyle: "row",
         alternatingRowStyle: "alt",
         columns: grid.Columns(
            grid.Column(format: (item) => Html.ActionLink("Edit", "Edit", new { id = item.Id })),
            grid.Column("Title"),
            grid.Column(format:(item) => Html.ActionLink("Delete", "Delete", null, new {onclick=string.Format("deleteRecord(Cart, '{0}')", item.Id), @class="Delete", href="JavaScript:void(0)"}))
      )
      ) %>

    Happy Coding,
    Nick