public
Created

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.

  • Download Gist
ODataProperty.cs
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
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.Clear();
config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create());
 
HttpSelfHostServer server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("started..");
Console.ReadKey();
}
 
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("customers");
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; }
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.