Paul Haag
Paul Haag

Reputation: 271

ASP.Net OData with string keys

I am trying to use ASP.Net OData v4 (e.g. ODataController) to allow access where the key is a string. 95% of the examples out there use an integer as a key and the couple of posts I've found that discuss the steps to use a string as the key aren't working for me.

In all cases, I am trying to access my resource with the following URL:

/api/ContactTypes('Agency')

Optimistically, I started with just changing the type of the key from int to key:

public SingleResult<ContactType> Get([FromODataUri] string key)

But I get a 404 response. Changing the URL to an integer, /api/ContactTypes(1) does "work" in that it routes to the correct method and that the key is a string type, but obviously, that doesn't help me. This is the scenario described in this post: How to get ASP.Net Web API and OData to bind a string value as a key? except that that post implies that accessing the URL the way I am should work (and also is for OData v3).

After further searching, I found this article: https://blogs.msdn.microsoft.com/davidhardin/2014/12/17/web-api-odata-v4-lessons-learned/ which basically says that you have to decorate the Get method with an explicit routing:

[ODataRoute("({key})")]
public SingleResult<ContactType> Get([FromODataUri] string key)

If I do that alone, though, I get "The path template '({key})' on the action 'Get' in controller 'ContactTypes' is not a valid OData path template. Empty segment encountered in request URL. Please make sure that a valid request URL is specified."

The comments in this post (https://damienbod.com/2014/06/16/web-api-and-odata-v4-crud-and-actions-part-3/) suggest that I need to decorate the Controller with an ODataRoutePrefix:

[ODataRoutePrefix("ContactTypes")]
public class ContactTypesController : ODataController

That seems counter-intuitive since I do not have anything ASP.Net should be confusing. My controller name is already following convention and I have no Web API controllers that could be confusing it.

Regardless, it does seem to "fix" the issue in that the error goes away, but then I am right back at square one (e.g. only integer values can be passed in the URL).

What am I missing?

Full controller code:

[Authorize]
[ODataRoutePrefix("ContactTypes")]
public class ContactTypesController : ODataController
{
    PolicyContext _Db;

    public ContactTypesController(PolicyContext db)
    {
        if (db == null)
            throw new ArgumentNullException("db");

        this._Db = db;
    }

    public ContactTypesController() : this(new PolicyContext())
    {

    }

    protected override void Dispose(bool disposing)
    {
        _Db.Dispose();

        base.Dispose(disposing);
    }

    [EnableQuery]
    [ODataRoute()]
    public IQueryable<ContactType> Get(ODataQueryOptions options)
    {
        return _Db.ContactType;
    }

    [EnableQuery]
    [ODataRoute("({key})")]
    public SingleResult<ContactType> Get([FromODataUri] string key)
    {
        IQueryable<ContactType> result = _Db.ContactType.Where(p => p.ContactTypeKey == key);

        return SingleResult.Create(result);
    }

Full WebApiConfig:

public static void Register(HttpConfiguration config)
{
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();

        builder.EntitySet<ContactType>("ContactTypes");

        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: "api",
            model: builder.GetEdmModel()
        );
    }

Upvotes: 3

Views: 7683

Answers (1)

Fan Ouyang
Fan Ouyang

Reputation: 2132

1.If in your EdmModel, the string property is key, then no ODataRoute is need, for example:

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
}

ConventionModelBuilder will use property named "Id" as the key, or you should specify it's a key like:

public class Product
{
    [Key]
    public string StringKey { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
}

Then the call like localhost\api\Products('test') should just go to

public SingleResult<Product> GetProduct([FromODataUri]string key)

2.If you already have a int as a key, but you want use string as another key, then you should try this feature: http://odata.github.io/WebApi/#04-17-Alternate-Key , and you can call like: localhost\api\Products(StringKey='test')

Upvotes: 7

Related Questions