First look at OData

ODataLogo-96I’ve heard about OData back in 2012 but never used it. Now I might have a use case for OData and want to explore it. Below are the parts of my sample WCF DataService project.
Every part has the exceptions and how I solved them.

Entities

My first project contains the entities of the sample project. A Survey and the Reports on it. Master-Detail with the surveyId stored in the Report.

Exception

On data context type ‘SurveyProvider’, there is a top IQueryable property ‘Surveys’ whose element type is not an entity type. Make sure that the IQueryable property is of entity type or specify the IgnoreProperties attribute on the data context type to ignore this property.

Solution

This exception is from the datacontext in the next part, but the cause is the missing [DataServiceKey(“Id”)] on the entities.

Code

[DataServiceKey("Id")]
public partial class Survey {
    public string Id { get; set; }
    public string SurveyName { get; set; }
    [InverseProperty("SurveyId")]
    public virtual ICollection<Report> Reports { get; set; }
}
[DataServiceKey("Id")]
public partial class Report {
    public string Id { get; set; }
    public DateTime Created { get; set; }
    public string SurveyId { get; set; }
    [ForeignKey("SurveyId")]
    public virtual Survey Survey { get; set; }
}

DataContext

The data project is for the reflection provider that holds the (test)data. All data is stored in lists.

Exception

HTTP 204 no content

Solution

Property is null in LoadProperty. Make sure the DataContext provides a value for the Master/Detail.

Code

public class SurveyProvider {
    private List<Survey> surveys = new List<Survey> {
        new Survey { Id = "1", SurveyName = "Flight" }
    };
    private List<Report> reports = new List<Report> { 
        new Report { Id = "1", SurveyId = "1", Created = new DateTime(2015,1,1,10,0,0) },
        new Report { Id = "2", SurveyId = "1", Created = new DateTime(2015,1,1,11,0,0) },
        new Report { Id = "3", SurveyId = "1", Created = new DateTime(2015,1,1,12,0,0) },
        new Report { Id = "4", SurveyId = "1", Created = new DateTime(2015,1,1,13,0,0) },
        new Report { Id = "5", SurveyId = "1", Created = new DateTime(2015,1,1,14,0,0) },
    };
    public SurveyProvider() {
        surveys.ForEach(s => s.Reports = reports.Where(r => r.SurveyId == s.Id).ToList());
        reports.ForEach(r => r.Survey = surveys.First(s => s.Id == r.SurveyId));
    }
    public IQueryable<Survey> Surveys {
        get { return surveys.AsQueryable(); }
    }
    public IQueryable<Report> Reports {
        get { return reports.AsQueryable(); }
    }
}

DataService

For easy debugging and trouble shooting enable the extra information on exceptions for the DataService. (See the code)

Also look at the metadata with the $metadata query option: http://localhost:53021/SurveyServiceOData.svc/$metadata

Host this in IIS to capture all traffic, because you’ll miss the lazy loading when hosting in IISExpress.

Code

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class SurveyServiceOData : DataService<SurveyProvider> {
    public static void InitializeService(DataServiceConfiguration config) {
        config.UseVerboseErrors = true;
        config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
        config.DataServiceBehavior.MaxProtocolVersion = 
            DataServiceProtocolVersion.V3;
    }
}

Client

Exception

HTTP Error 400. The request hostname is invalid.
or no traffic captured with fiddler.

Solution

Use the localhost.fiddler when hosting in IISExpress.

var uri = new Uri("http://localhost.fiddler:53021/SurveyServiceOData.svc");

Exception

The remote name could not be resolved: ‘localhost.fiddler’.

Solution

Set the proxy to fiddler.

<configuration>
  <!-- other settings in app.config -->
  <system.net>
    <defaultProxy enabled="true" >
      <proxy proxyaddress="http://localhost:8888"/>
    </defaultProxy>
  </system.net>
</configuration>

Exception

The MaxDataServiceVersion ‘2.0’ is too low for the response. The lowest supported version is ‘3.0’.

Solution

Make sure the versions of the DataService and Client match. See also the InitializeService method of the DataService.

var ctx = new DataServiceContext(uri, DataServiceProtocolVersion.V3);

Exception

A property with name ‘Reports’ on type ‘Reporting.Entities.Survey’ has kind ‘Structural’, but it is expected to be of kind ‘Navigation’.

Solution

install-package EntityFramework

Code

static void Main(string[] args) {
    // use MachineName
    var uri = new Uri("http://localhost.fiddler:53021/SurveyServiceOData.svc");
    var context = new DataServiceContext(uri, DataServiceProtocolVersion.V3);
    var survey = context.CreateQuery<Survey>("Surveys").First();
    Console.WriteLine("Found survey {0} with {1} reports",
        survey.SurveyName, survey.Reports == null ? "null" : survey.Reports.Count.ToString());
    var response = context.LoadProperty(survey, "Reports");
    Console.WriteLine("LoadProperty Reports on Survey returned {0}", response.StatusCode);
    Console.WriteLine("Found survey {0} with {1} reports",
        survey.SurveyName, survey.Reports.Count.ToString());
    var pointInTime = new DateTime(2015, 1, 1, 12, 0, 0);
    var report = context.CreateQuery<Report>("Reports")
            .Where(r => r.SurveyId == "1")
            .Where(r => r.Created == pointInTime)
            .First();
    Console.WriteLine("Found report on {0} for Survey {1}",
            report.Created, report.Survey == null ? "null" : report.Survey.SurveyName);
    response = context.LoadProperty(report, "Survey");
    Console.WriteLine("LoadProperty Survey on Report returned {0}", response.StatusCode);
    Console.WriteLine("Found report on {0} for Survey {1}", report.Created, report.Survey.SurveyName);
    Console.ReadLine();
}

Conclusion

The potential of WCF data service is there. Just expose the data and let the clients filter and query what they need.

References

Demo project for download
Odata.org with all the documentation
Custom Data Service Providers on MSDN

About erictummers

Working in a DevOps team is the best thing that happened to me. I like challenges and sharing the solutions with others. On my blog I’ll mostly post about my work, but expect an occasional home project, productivity tip and tooling review.
This entry was posted in Development and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.