In the OData specification, the $format parameter can be passed on the query string of the request to tell the server that you would like the response to be serialized as JSON. Normally, to get JSON-formatted data, you have to specify "application/json" in your "Accept" header. The query string feature is handy in situations when it’s not easy or possible to modify the request headers.

Unfortunately, if you try to pass $format on a WCF Data Services query, you will get a response that looks like:

<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <code /> 
    <message xml:lang="en-US">
        The query parameter '$format' begins with a system-reserved 
        '$' character but is not recognized.
    </message> 
</error>

Unfortunately, WCF Data Services doesn’t support this OData convention. But with a little bit of trickery, you can make it work by modifying the request in an ASP.NET HTTP module.

The trick is to check for the query string parameter before the request gets to WCF Data Services and modify the request headers accordingly. Normally, the Request.Headers collection is read-only. You’ll need to use some simple reflection to make it writable but once you do, it’s just a matter of setting the appropriate Accept header, then rewriting the URL to remove the $format parameter so WCF Data Services doesn’t bitch and moan.

The entire HTTP module is shown below. Like it? Hate it? Let me know in the comments.

<!-- add to web.config -->
<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="ODataFormatModule" type="YourNamespace.ODataFormatModule, YourAssembly" />
    </modules>
</system.webServer>
/// <summary>
/// Intercepts WCF Data Services requests that include a $format parameter on the query
/// string and alters the request headers according to the OData specification.
/// </summary>
public sealed class ODataFormatModule : IHttpModule
{

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="T:ODataFormatModule"/> class.
    /// </summary>
    public ODataFormatModule( )
    {
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the application instance.
    /// </summary>
    public HttpApplication Application
    {
        get;
        private set;
    }

    /// <summary>
    /// Gets the current HTTP context.
    /// </summary>
    public HttpContext Context
    {
        get
        {
            return HttpContext.Current;
        }
    }

    /// <summary>
    /// Gets the current request.
    /// </summary>
    public HttpRequest Request
    {
        get
        {
            return HttpContext.Current.Request;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Initializes a module and prepares it to handle requests.
    /// </summary>
    /// <param name="context">An <see cref="T:HttpApplication"/> that provides access to the methods, 
    /// properties, and events common to all application objects within an ASP.NET application</param>
    public void Init( HttpApplication context )
    {

        Application = context;
        Application.BeginRequest += Application_BeginRequest;

    }

    /// <summary>
    /// Disposes of the resources (other than memory) used by the module that implements
    /// <see cref="T:IHttpModule"/>.
    /// </summary>
    public void Dispose( )
    {
        Application.BeginRequest -= Application_BeginRequest;
        Application = null;
    }

    /// <summary>
    /// Forces the <see cref="T:NameValueCollection"/> to allow modifications by using reflection to
    /// set the IsReadOnly property to false.
    /// </summary>
    /// <param name="collection">The collection to make writable.</param>
    /// <returns>The original collection after the IsReadOnly property has been hacked.</returns>
    private static NameValueCollection MakeWritable( NameValueCollection collection )
    {

        var collectionType = collection.GetType( );
        var isReadOnlyProperty = collectionType.GetProperty(
            "IsReadOnly",
            BindingFlags.Instance |
            BindingFlags.IgnoreCase |
            BindingFlags.NonPublic
        );

        isReadOnlyProperty.SetValue( collection, false, null );

        return collection;

    }

    /// <summary>
    /// Gets a content type for the corresponding format parameter according to the OData specification
    /// http://www.odata.org/developers/protocols/uri-conventions#FormatSystemQueryOption
    /// </summary>
    /// <param name="format">The value of the $format querystring parameter.</param>
    /// <returns>The corresponding Accept request header value.</returns>
    private static string MapToMediaType( string format )
    {

        Contract.Requires( format != null );

        switch ( format.ToLowerInvariant() ) {
            case "atom":
            return "application/atom+xml";
            case "xml":
            return "application/xml";
            case "json":
            return "application/json";
            default:
            return format;
        }   // switch

    }

    #endregion

    #region Event Handlers

    /// <summary>
    /// Handles the BeginRequest event of the Application.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="T:EventArgs"/> instance containing the event data.</param>
    private void Application_BeginRequest( object sender, EventArgs e )
    {

        var format = Request.QueryString["$format"];

        if ( !String.IsNullOrWhiteSpace(format) ) {

            // Ordinarily, Request.Headers is read-only so we need to
            // use some reflection to get around that. Ugly, I know.
            var requestQuery = MakeWritable( Request.QueryString );
            var requestHeaders = MakeWritable( Request.Headers );

            // Set the Accept header the way a well-behaved json client
            // would have done, which WCF Data Services does support
            requestHeaders["Accept"] = MapToMediaType( format );

            // Use URL-rewriting to remove the $format part of the querystring
            // Otherwise, if it gets to WCF Data Services, it barfs
            requestQuery.Remove( "$format" );
            Context.RewritePath( Request.FilePath, Request.PathInfo, requestQuery.ToString( ) );

        }   // if

    }

    #endregion

}   // class

This confused me for a minute. But once I realized what was going on I felt like a dolt.

$Query = $UriBuilder.Query

if ($Query.Length > 1) {
    $Query = $Query.Substring(1) + '&'
}

$UriBuilder.Query = $Query + $KeyValuePair
Access to the path 'C:\Windows\system32\1' is denied.
At C:\Blah\Blah.psm1:366 char:32
+             if ($Query.Length > <<<<  1) {
    + CategoryInfo          : OpenError: (:) [], UnauthorizedAccessException
    + FullyQualifiedErrorId : FileOpenFailure

To celebrate the upcoming finale of LOST, I threw this thing together in Silverlight 4 using Expression Blend 4 and Visual Studio 2010. I also used a trial of Goldwave 5 to work on the sound effects.

I’ve included the source code so you can take a look around. This is an example of a pretty barebones MVVM app without the “M”. Some of the techniques I used were XAML storyboards, a simple state machine, semi-transparent overlays to simulate lighting, MediaElement to play audio files, ViewBox to stretch a portion of the view, TransitioningContentControl, etc. just to name a few.

So go ahead, feel important at work like you’re John Locke saving the world in between staff meetings. Just remember to enter the right numbers (with spaces) and push the “EXECUTE” button every 108 minutes or else…

(Or if you just get bored of waiting, click the clock to advance the timer. You can also right click the application to install it to your start menu.)

I find it very frustrating sometimes to get something to build in Blend. Like many developers, I have a "Me.dll" that contains a lot of commonly used classes, custom controls, etc. As you might expect, this DLL often takes dependencies on other DLL’s that must also then be referenced.

In Blend, this can be a pain in the butt because the Silverlight assemblies are scattered into 3 main locations:

  • %ProgramFiles%\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0
  • %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Libraries\Client
  • %ProgramFiles%\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Bin

It gets to be pretty frustrating pointing blend to these various paths to find System.ServiceModel.dll or something.

You can make this a lot easier without copying all the DLL’s by using symbolic links in Windows Vista/7/2008.

To create the symbolic links, open a PowerShell prompt as an administrator. (Make sure you have PowerShell Community Extensions installed, but who doesn’t?) Then paste or type the following script.

Set-Location C:\Temp\Refs

$SearchArgs = @{
    ErrorAction = 'SilentlyContinue'
    Include = @("System.*.dll", "Microsoft.*.dll")
    Exclude = @("*.resources.dll", "*.design.dll")
    Path = @(
        "$($Env:ProgramFiles)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\*"
        "$($Env:ProgramFiles)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\*"
        "$($Env:ProgramFiles)\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Bin\*"
        "$($Env:ProgramFiles) (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\*"
        "$($Env:ProgramFiles) (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\*"
        "$($Env:ProgramFiles) (x86)\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Bin\*"
    )
}

Get-ChildItem @SearchArgs | %{

    $LinkPath = $_.Name
    $TargetPath = $_.FullName
    
    Write-Host "$LinkPath -> $TargetPath"
    New-Symlink -Path:$LinkPath -Target:$TargetPath | Out-Null

}

Came across this while browsing Google Reader…

via Ted Dziuba: Why Engineers Hop Jobs

People in my generation have a very low tolerance for bullshit, and software engineering, in general, is a very high bullshit career. If you couple that with the standard load of bullshit you would get from a non-technical Harvard MBA type boss — like many CEOs that you find trying to get rich in Silicon Valley by hiring some engineers to “code up this idea real quick” — it’s no wonder that a good engineer will walk off the job after his one year cliff vesting.