Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Sample showing how to implement ~/entityset/key/property kind of url support in web API OData. Run and try the url http://localhost:8080/customers(42)/ID or http://localhost:8080/customers(42)/Name for a demo.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.ServiceModel;
using System.Web.Http;
using System.Web.Http.OData;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Routing;
using System.Web.Http.SelfHost;
using Microsoft.Data.Edm;
namespace ODataTests
class Program
static void Main(string[] args)
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://localhost:8080");
config.HostNameComparisonMode = HostNameComparisonMode.Exact;
config.Routes.MapODataRoute("default", "", GetEdmModel());
config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create());
HttpSelfHostServer server = new HttpSelfHostServer(config);
private static IEdmModel GetEdmModel()
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
return builder.GetEdmModel();
public class CustomersController : ODataController
[AcceptVerbs("GET", "POST", "PUT", "POST", "PATCH", "MERGE")]
public HttpResponseMessage HandleUnmappedRequest(ODataPath odataPath)
// Handle property GET.
if (Request.Method == HttpMethod.Get &&
(odataPath.PathTemplate == "~/entityset/key/property" ||
odataPath.PathTemplate == "~/entityset/key/cast/property"))
string propertyName = (odataPath.Segments.Last() as PropertyAccessPathSegment).PropertyName;
string key = (odataPath.Segments[1] as KeyValuePathSegment).Value;
// replace with code to get the entity by key.
var customer = new Customer { ID = 42 };
// use reflection to get the property. replace with code specific to your provider.
PropertyInfo property = customer.GetType().GetProperty(propertyName);
object propertyValue = property.GetValue(customer);
// use the custom CreateResponse overload.
return Request.CreateResponse(HttpStatusCode.OK, property.PropertyType, propertyValue);
throw new HttpResponseException(HttpStatusCode.BadRequest);
public static class HttpRequestMessageExtensions
private static MethodInfo _createResponse = InitCreateResponse();
private static MethodInfo InitCreateResponse()
Expression<Func<HttpRequestMessage, HttpResponseMessage>> expr =
(request) => request.CreateResponse<object>(HttpStatusCode.OK, default(object));
return (expr.Body as MethodCallExpression).Method.GetGenericMethodDefinition();
// Content negotiation doesn't use the runtime type of the object. This helper method is CreateResponse overload that passes
// the specified type to content negotiation.
public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode status, Type type, object value)
return _createResponse.MakeGenericMethod(type).Invoke(null, new[] { request, status, value }) as HttpResponseMessage;
public class Customer
public int ID { get; set; }
public string Name { get; set; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment