Search code examples
entity-frameworkasp.net-web-apiodata

WebApi OData query with complex type ignores nested values


Using Entity Framework I created an DbContext. The Dbcontext is created within a WebApi project. One of the tables from the DbContext represents buildings. The location is represented as a Geography type in the database. I need to query the buildings and sort the result by the distance to a given location.

The type used for the query is

[DataContract]
public class BuildingDistance
{
    [Key]
    [DataMember]
    public string Id { get; set; }
    [DataMember]
    public string Street { get; set; }
    [DataMember]
    public double? Distance { get; set; }
    [DataMember]
    public t_building Building { get; set; }
}

This type is registered with modelbuilder

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
// ... Registration of the types generated by EF
builder.EntitySet<t_building>("t_building");
builder.EntitySet<BuildingDistance>("BuildingDistances");

I have the following controller action defined

        public IHttpActionResult GetBuildingDistances(ODataQueryOptions<BuildingDistance> queryOptions)
    {
        // validate the query.
        try
        {
            queryOptions.Validate(_validationSettings);
        }
        catch (ODataException ex)
        {
            return BadRequest(ex.Message);
        }

        dbcontext db = new dataBaseEntities();
        string pointString = string.Format(CultureInfo.InvariantCulture.NumberFormat, "POINT({0} {1})", 11.53128, 48.17883);
        DbGeography myLocation = DbGeography.FromText(pointString, 4326);
        var query =
            db.t_building.Select(
                x =>
                    new BuildingDistance
                    {
                        Id = x.id,
                        Street = x.location_street,
                        Building = x,
                        Distance = x.GeoLocation.Distance(myLocation)
                    });

        var result = queryOptions.ApplyTo(query) as IQueryable<BuildingDistance>;

        return Ok(result);
    }

The query is executed and a result is sent to the client. Problem is that property Building is absent

{  "odata.metadata":"http://localhost:50732/odata/$metadata#BuildingDistances","value":[
{
  "Id":"15FF94FE-3CB8-4CF6-BE89-501A8366ED7C","Strasse":"Maistr.","Distance":4407.0418114105069
}  ]

}

Only the properties ID, Distance and Street are actually populated. The property Building is left out.

If I run this query in LinqPad, the property Building is populated.

Property is not readonly so the case from here is ruled out.

Thanks for any suggestions!

Update: tried the approach recommended by Fan Ouyang and I get the following error

The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.

Update: tried using the navigation property approach of OData using Attribute ForeignKey. No luck, with and without $expand.


Solution

  • You should have

    $expand=Building 
    

    In the query to have navigation property in the payload.

    When there is a $expand or $select in the query, the result type of the applyto method will be wrapper class in WebAPI/OData, so you should remove the as IQueryable<BuildingDistance> and use code like https://github.com/OData/WebApi/blob/master/OData/test/E2ETest/WebStack.QA.Test.OData/DollarLevels/DollarLevelsController.cs#L62-L63

    or you can just use EnableQuery Attribute in the controller method and just return the query:

    [EnableQuery]
    public IHttpActionResult GetBuildingDistances()
    {
        ...
        var query =
            db.t_building.Select(
                x =>
                    new BuildingDistance
                    {
                        Id = x.id,
                        Street = x.location_street,
                        Building = x,
                        Distance = x.GeoLocation.Distance(myLocation)
                    });
        return Ok(query as IQueryable<BuildingDistance);
    }