WCF Data Service Projections - null handling and expansion

One of the capabilities that I wanted to call out of WCF Data Service projections on the client is the ability to include expanded entities.

So I can write something like the following, which returns the ID, LastName and manager's ID and LastName for each employee. This is just a Northwind database with the 'Employee1' property renamed to 'Manager'.

var q = from e in service.Employees
        select new Employee()
        {
            EmployeeID = e.EmployeeID,
            LastName = e.LastName,
            Manager = new Employee()
            {
                EmployeeID = e.Manager.EmployeeID,
                LastName = e.Manager.LastName,
            }
        };

This query works because we're projecting values into an entity types without doing any transformations, and so the values will generally round-trip correctly.

For the expanded Manager employee, note that we must initialize it from the projected Manager employee as well. Mixing properties from different entities into a single one would mess up the state and thus such as query would be rejected by the local query processor.

However, if you run this query with the Northwind database, you will get a NullReferenceException with the following message:

An entry returned by the navigation property 'Manager' is null and cannot be initialized. You should check for a null value before accessing this property.

What this means is that one of the managers was null, but the projection is trying to get the EmployeeID and LastName values off of it. If this were an in-memory query over lists for example, you would get a NullReferenceException with a more general error message. We decided to keep throw the same exception to keep things consistent, as there are cases where we will defer to in-memory LINQ processing, for example when projecting calculated values into anonymous types.

The code you can write to handle this case is the same you would write for the in-memory case, and is one of the few non-one-to-one-assignments supported - there is explicit support for conditional null checks in queries, and the right things happens at runtime.

var q = from e in service.Employees
        select new Employee()
        {
            EmployeeID = e.EmployeeID,
            LastName = e.LastName,
            Manager = (e.Manager == null) ? null : new Employee()
            {
                EmployeeID = e.Manager.EmployeeID,
                LastName = e.Manager.LastName,
            }
        };

The same mechanism can be applied to entities that are nested further down, of course.

Enjoy!