OData second look

ODataLogo-96This is a follow up on my OData first look post. Since then I’ve moved the service to IIS for better fiddler support. The sample code builds on the previous post and shows how to load related data in one call, the ability to overcome model changes and how to use operations.
Every part has the exceptions and how I solved them.

Expand

With the Expand operation the eager loading of related data is executed. This means that the property (set in the Expand parameter) will be supplied as an object in the response. Default a link would be returned for later retrieval of the object.

Exception

The property is returned in the response, but not available in the result object. With LoadProperty the property is set, but that is not why you call expand.

Solution

MergeOption of the context should be set. The default AppendOnly does not allow “changes” and ignores the expanded data. With NoTracking the LoadProperty will throw an exception, but the expand operation is executed and applied. PreserveChanges or OverwriteChanges support both Expand and LoadProperty, since my data is readonly both options are applicable.

Code

context.MergeOption = MergeOption.OverwriteChanges;
var lastReport = context.CreateQuery<Report>("Reports")
    .Expand("Survey")
    .Where(r => r.SurveyId == "1")
    .OrderByDescending(r => r.Created)
    .First();

Changes to the model

You can create a client for you odata service on three different ways

  1. Reference the entities assembly, update the client when the service changes
  2. Create a copy of the entities, your project is more standalone
  3. Generate a Service Reference, this combines 1 and 2, by generating the types from the entities assembly as a copy in the project

What type of client you use, changes to the model can break the application.

Exception

The property ‘x’ does not exist on type ‘y’. Make sure to only use property names that are defined by the type.

Solution

On the DataServiceContext is a property that instructs it to ignore missing properties. Whatever property cannot be mapped (renamed, removed or added) the value stays null or default.
Make note that changing the type of a property can cause an exception. A DateTime will fit in a String, but not every String will fit in a DateTime.

Code

var context = new DataServiceContext(uri, DataServiceProtocolVersion.V3);
context.IgnoreMissingProperties = true;

Operations

Operations can be used to handle the data. This offers more control to the developer. My sample does the filtering on the name. Maybe for security reasons or for performance.

Exception

I’ve got a 404 Not found exception on my first try.

Solution

The uri constructor replaces everything after the last slash. Make sure you end the baseUri with a ‘/’.

Code

// Dataservice:
public static void InitializeService(DataServiceConfiguration config) {
     // other config left out for clarity
     config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
}

[WebGet]
public Reporting.Entities.Survey FlightSurvey() {
    return CurrentDataSource.Surveys
        .Where(x => x.SurveyName.Equals("flight"))
        .FirstOrDefault();
}

// client
var uri = new Uri ("http://MACHINENAME/Reporting.Service/SurveyServiceOData.svc/");
var context = new DataServiceContext(uri, DataServiceProtocolVersion.V3);
var flight = new Uri (uri, "FlightSurvey");
var survey = context.Execute<Survey>(flight).First();

Operations with parameter

Operations can have parameters. In my sample above the name could be a parameter.

Exception

Query options $select, $expand, $filter, $orderby, $inlinecount, $skip, $skiptoken and $top are not supported by this request method or cannot be applied to the requested resource.

Solution

Do a ToList() on the query before calling First(). This executes the odata query (without $top=1), reads the result in a list and then returns the first item.

Code

// DataService: 
[WebGet]
public Reporting.Entities.Survey GetSurvey(string surveyName) {
    return CurrentDataSource.Surveys
        .Where(x => x.SurveyName.Equals(surveyName))
        .FirstOrDefault();
}

// client
var uri = new Uri ("http://MACHINENAME/Reporting.Service/SurveyServiceOData.svc/");
var context = new DataServiceContext(uri, DataServiceProtocolVersion.V3);
var survey = context.CreateQuery<Survey >("GetSurvey" )
    .AddQueryOption("surveyName", "'flight'")
    .ToList().First();

Conclusion

With lazy loading and operations I can limit the traffic. Looking deeper into WCF data service, my use-case implementation is getting clearer.

References

Calling Service Operations article on MSDN
Framework source code of ClientType showing how IgnoreMissingProperties works. Exception or Ignore, no hooks for custom handling.

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.

1 Response to OData second look

  1. Pingback: OData third look | .NET Development by Eric

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 )

Twitter picture

You are commenting using your Twitter 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.