Search code examples
c#asp.net-web-apiodata

oData getter for Dynamicproerties


I have a class with DynamicProperties (open type) which I am exposing via oData. As per my understanding, if the user queries one of the dynamicproperties the request goes to getDynamicProperty method (or is there any other better way to do it ?).

  1. How do I get the property user is trying to access ?. At present I am parsing the uri using oDatauriParser. Is it right approach ?. Is there any other better way to do it?

  2. When I return I am not able to return the value of the property as it is stored as type object in dictionary. At present I am returning it as a string. But I want it to return its actual type or any other means which will preserve its actual type, how do i do it ?

    public class BooksController : ODataController
    {           
       public IHttpActionResult getDynamicProperty([FromODataUri]string key)  
       {
    
         try
         {
            ODataUriParser uriParser = new ODataUriParser(WebApiConfig.GetEdmModel(), new Uri(Request.RequestUri.PathAndQuery, UriKind.Relative));
            OpenPropertySegment propertySegment = uriParser.ParsePath().LastSegment as OpenPropertySegment;
            if(propertySegment == null || string.IsNullOrEmpty(propertySegment.PropertyName))
            {
                return NotFound();
            }
            string property = propertySegment.PropertyName;
            Book book = getBook(key);
            return Ok(book.DynamicProperties[property].ToString());
         }
         catch(Exception)
         {
            return NotFound();
         }
       }
    }
    
    public class Book
    {
       public string ISBN { get; set; }
       public string Title { get; set; }
       public Press Press { get; set; }
       public Dictionary<string, object> DynamicProperties { get; set; }
    }
    

Solution

  • For #1, I think you shouldn't do that. There are two ways you can use to query dynamic property since Web API OData v5.6

    1. Convention routing

    It should work to define a method named "GetDynamicProperty" in your BooksController. For example:

    public IHttpActionResult GetDynamicProperty([FromODataUri] string key, [FromODataUri] string dynamicProperty)
    {
     ...
    }
    

    Where, parameter name should named dynamicProperty.

    1. Attribute routing

    It should work to define a random method in any controller with an ODataRoute attribute on it. For example:

    [HttpGet]
    [ODataRoute("Books({key})/Press/{pName:dynamicproperty}")]
    public IHttpActionResult XxxxDynamicPropertyXxxx([FromODataUri] string key, [FromODataUri] string pName)
    {
    ...
    }
    

    The template for dynamic property should be "{anyName:dynamicproperty}"

    For #2, please add the following method in your controller,

    private IHttpActionResult Ok(object content, Type type)
    {
        var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
        return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
    }
    

    and call it in your HTTP response method. for example:

    [HttpGet]
    [ODataRoute("Books({key})/Press/{pName:dynamicproperty}")]
    public IHttpActionResult XxxxDynamicPropertyXxxx([FromODataUri] string key, [FromODataUri] string pName)
    {
        Book book = _books.FirstOrDefault(e => e.ISBN == key);
        if (book == null)
        {
            return NotFound();
        }
    
        object value;
        if (!book.Press.DynamicProperties.TryGetValue(pName, out value))
        {
           return NotFound();
        }
    
         return Ok(value, value.GetType());
    }
    

    Test:

    if I use the sample "Books" in http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/use-open-types-in-odata-v4

    I query

    http://localhost/odata/Books('978-0-7356-7942-9')/Authors   
    

    I can get the result as:

    {
      "@odata.context":"http://localhost/odata/$metadata#Collection(Edm.String)","va
    lue":[
        "Leonard G. Lobel","Eric D. Boyd"
      ]
    }
    

    while, I query:

    http://localhost/odata/Books('978-0-7356-7942-9')/Press/Address
    

    I can get the result as:

    {
      "@odata.context":"http://localhost/odata/$metadata#Books('978-0-7356-7942-9')/
    Press/Address","City":"Redmond","Street":"One Microsoft Way"
    }
    

    Hope it can help you. Thanks.