Max Xapi
Max Xapi

Reputation: 815

C# Odata v4 open type raw values

I have a simple open type that contains dynamic properties:

public class Person {
    [Key]
    public string Uid { get; set; }     
    public IDictionary<string, object> DynamicProperties { get; set; }
}

In my controller, I fill these properties like this:

public IHttpActionResult Get([FromODataUri] string key) {
    Person person;
    // ...
    person.DynamicProperties.Add("foo", "foo_value");
    person.DynamicProperties.Add("bar", "bar_value");
    // ...
    return this.Ok(person);
}

I can figure how to request individually these properties - with an URL like ~/Person('uid')/foo - thanks to their keys and this method:

public IHttpActionResult GetDynamicProperty([FromODataUri] string key, [FromODataUri] string dynamicProperty) {
    Person person;
    // ...
    // (string) for the example
    return this.Ok((string)person.DynamicProperties[dynamicProperty]);
}

Result is like that:

{
    "@odata.context": "http://myServer/$metadata#Person('uid')/foo",
    "value": "foo_value"
}

But I can't figure how to get the raw value of these properties, with an URL like ~/Person('uid')/foo/$value

If I try it, the result is this one:

{
    "error": {
        "code": "",
        "message": "No HTTP resource was found that matches the request URI 'http://myServer/Person('uid')/foo/$value'.",
        "innererror": {
            "message": "No routing convention was found to select an action for the OData path with template '~/entityset/key/dynamicproperty/$value'.",
            "type": "",
            "stacktrace": ""
        }
    }
}

How I should route to have access to these raw values? Or is there a method or something else that could handle it?

-- Edit after Sam Xu's answer

Thanks for your example, it works like a charm. I've followed your samples for points 1 and 2. For the point 3, if it can help someone, this is the adapted version for a classic Web API's Register method:

public static void Register(HttpConfiguration config) {
    // Custom routing for dynamic attributes raw values
    var routings = ODataRoutingConventions.CreateDefault();
    routings.Insert(0, new DynamicRawValueRoutingConvention());
    // Our model
    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<Person>("Person");
    // ...
    // Old way
    //config.MapODataServiceRoute(
    //    routeName: "myDefaultRouteName",
    //    routePrefix: null,
    //    model: builder.GetEdmModel()
    //);
    // New way with Sam's samples
    config.MapODataServiceRoute(
        "myDefaultRouteName",
        null,
        configureAction =>
        configureAction.AddService(ServiceLifetime.Singleton, sp => builder.GetEdmModel()).AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp => routings)
    );
    var formatters = ODataMediaTypeFormatters.Create();
    foreach (var oDataMediaTypeFormatter in formatters) {
        oDataMediaTypeFormatter.MediaTypeMappings.Insert(0, new ODataDynamicValueMediaTypeMapping());
    }
    config.Formatters.InsertRange(0, formatters);
}

Upvotes: 0

Views: 668

Answers (1)

Sam Xu
Sam Xu

Reputation: 3380

@Max Xapi

OData hasn't the built-in logic to support $value for dynamic property.

However, you can have workaround by yourself to accomplish it.

It's simple and only several steps:

  1. create your own routing convention.
  2. create the dynamic property value mapping
  3. insert the routing convention and dynamic property value mapping into configuration.

I created a sample that you can refer to, see the commit here. Please let me know any further problem and hope it can help.

Regards,

-Sam

Upvotes: 2

Related Questions