user517406
user517406

Reputation: 13763

OData get object by field other than Id

I have just started working with Web Api OData and I find myself wanting to do something that I was able to do a standard ApiController, that is get an object from a field other than the Id field. I know it is standard to do this for example when getting an object based on it's Id :

[Queryable]
    public SingleResult<Group> GetGroup([FromODataUri] int key)
    {
        return SingleResult.Create(db.Groups.Where(group => group.GroupId == key));
    }

But if I want to get group by groupName field for example, what would I do? I have tried this as something similar worked with an ApiController :

Controller :

public Group GetGroup([FromODataUri] string groupName)
    {
        var group = _repository.GetGroupByGroupName(groupName);
        if (group == null)
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
        }
        return group;
    }

Repository :

public Group GetGroupByGroupName(string groupName)
        {
            Group group = (from u in _db.Groups
                        where u.GroupName == groupName
                        select u).FirstOrDefault();

            return group;
        }

My WebApiConfig looks like this :

public static void Register(HttpConfiguration config)
        {
            config.EnableCors();

            var json = config.Formatters.JsonFormatter;
            json.SerializerSettings.PreserveReferencesHandling =
                Newtonsoft.Json.PreserveReferencesHandling.Objects;

            config.Formatters.Remove(config.Formatters.XmlFormatter);

            // OData
            var modelBuilder = new ODataConventionModelBuilder();
            modelBuilder.EntitySet<City>("Cities");
            modelBuilder.EntitySet<Stage>("Stages");
            modelBuilder.EntitySet<Team>("Teams");
            modelBuilder.EntitySet<Fixture>("Fixtures");
            modelBuilder.EntitySet<Roster>("Rosters");
            modelBuilder.EntitySet<Standing>("Standings");
            modelBuilder.EntitySet<Group>("Groups");

            var model = modelBuilder.GetEdmModel();
            config.Routes.MapODataRoute("ODataRoute", "odata", model);

        }

I want to be able to get a group object based on a groupname using odata/Groups/GroupName, but what I currently have does not work. How would I do this, or am I approaching this from totally the wrong direction?

Upvotes: 3

Views: 2347

Answers (2)

Jerther
Jerther

Reputation: 5995

Let's say you STILL need to call a custom function. OData V4 allows that. The question is a bit old but I believe it is still valid today.

First, you need to specify a Namespace to the ODataConventionModelBuilder since OData V4 needs this for actions and functions:

var builder = new ODataConventionModelBuilder();
builder.Namespace = "Default";

Be aware that this might result in IIS giving 404 because the dot is interpreted by IIS. To prevent this, add the following to your Web.config:

<system.webServer>
    <handlers>
      <clear/>
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/*" 
          verb="*" type="System.Web.Handlers.TransferRequestHandler" 
          preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

then, you need to declare the function too:

builder.EntitySet<Group>("Groups").EntityType
    .Collection
    .Function("GetGroupByName")
    .ReturnsFromEntitySet<Group>("Groups")
    .Parameter<string>("name");

Notice the function is declared on the COLLECTION, so it's bound to it. Then the ODataController method:

[ODataRoute("Groups/Default.GetGroupByName(name={name})")]
[HttpGet]
public IHttpActionResult GetGroupByName([FromODataUri]string name)
{
    return Ok(new Group() or whatever);
}

Here the ODataRoute annotation is necessary only if you use a parameter. It defines in order: the entityset, the namespace, the method and the parameters.

You can then use:

http://localhost:xxxx/api/Groups/Default.GetByGroupName(name='sausage')

Also, see this SO answer to pass multiple parameters

Upvotes: 1

Greg Ennis
Greg Ennis

Reputation: 15379

You are already using QueryableAttribute so if you define something like:

[Queryable]
public IQueryable<Group> Get()
{
    // Returns all groups as a queryable (do not execute query here!)
}

You can then access this with an ajax request and any odata query, like:

http://server/Api/Group?$filter=GroupName eq 'test'

The query is built on the client, not restricted by server parameters.

Upvotes: 1

Related Questions