[.NETWorld] Getting started with OData v4 in ASP.NET Web API


Since yesterday, the ASP.NET Web stack nightly feed contains the packages supporting OData v4. The package is called Microsoft.AspNet.OData and has a working version 5.2.0 – so I’m guessing this is intended to ship with Web API 2.

It relies on the latest beta of Microsoft.OData.Core. OData v4 is a massive changed compared to v3 – you can read about all of them here.

 

Adding OData v4 to your Web API

You need to use the Web stack nightlies which is available athttp://www.myget.org/F/aspnetwebstacknightly/ and needs to be added to your Nuget repositories.

nightlies

Once added, you can install the new OData nightly packages using:

 

Shell

1
install-package Microsoft.AspNet.OData -pre

 

If your Web API has a reference to Microsoft.AspNet.WebApi.OData (the old OData support package), it should be removed. Also, you may have to add assembly binding redirects – sinceMicrosoft.AspNet.OData will pull latest nightlies of Microsoft.AspNet.WebApi.Core andMicrosoft.AspNet.WebApi.Client, which will replace your current references to those packages.

Below are the necessary redirects:

 

1
2
3
4
5
6
7
8
9
10
    <assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1″>
            <dependentAssembly>
                <assemblyIdentity name=”System.Web.Http” publicKeyToken=”31BF3856AD364E35″ culture=”neutral”/>
                <bindingRedirect oldVersion=”0.0.0.0-5.2.0.0″ newVersion=”5.2.0.0″/>
            </dependentAssembly>
            <dependentAssembly>
                <assemblyIdentity name=”System.Net.Http.Formatting” publicKeyToken=”31BF3856AD364E35″ culture=”neutral”/>
                <bindingRedirect oldVersion=”0.0.0.0-5.2.0.0″ newVersion=”5.2.0.0″/>
            </dependentAssembly>
    </assemblyBinding>

 

Getting started with OData v4 in Web API

I’ll use a simple model and a DbContext for this exercise:

 

C#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Player
{
   public int Id { get; set; }
   public string Name { get; set; }
   public int Age { get; set; }
}
public class PlayerAppContext : DbContext
{
    public PlayerAppContext() : base(“name=PlayerAppContext”)
    {}
    public DbSet<Player> Players { get; set; }
}

 

The controller is the same one as you’d get using scaffolding, with some small changes done to cater for the new OData package. Notice that we now use [EnableQuery] instead of [Queryable]. Also, with OData v4 we could (should?) support upsert (insert with PUT), but I will not do that.

 

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    public class PlayerController : ODataController
    {
        private PlayerAppContext db = new PlayerAppContext();
    [EnableQuery]
    public IQueryable<Player> GetPlayer()
    {
        return db.Players;
    }
    [EnableQuery]
    public SingleResult<Player> GetPlayer([FromODataUri] int key)
    {
        return SingleResult.Create(db.Players.Where(player => player.Id == key));
    }
    public async Task<IHttpActionResult> Put([FromODataUri] int key, Player player)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        if (key != player.Id)
        {
            return BadRequest();
        }
        db.Entry(player).State = EntityState.Modified;
        try
        {
            await db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!PlayerExists(key))
            {
                return NotFound();
            }
            throw;
        }
        return Updated(player);
    }
    public async Task<IHttpActionResult> Post(Player player)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        db.Players.Add(player);
        await db.SaveChangesAsync();
        return Created(player);
    }
    [AcceptVerbs(“PATCH”, “MERGE”)]
    public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Player> patch)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        Player player = await db.Players.FindAsync(key);
        if (player == null)
        {
            return NotFound();
        }
        patch.Patch(player);
        try
        {
            await db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!PlayerExists(key))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return Updated(player);
    }
    public async Task<IHttpActionResult> Delete([FromODataUri] int key)
    {
        Player player = await db.Players.FindAsync(key);
        if (player == null)
        {
            return NotFound();
        }
        db.Players.Remove(player);
        await db.SaveChangesAsync();
        return StatusCode(HttpStatusCode.NoContent);
    }
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            db.Dispose();
        }
        base.Dispose(disposing);
    }
    private bool PlayerExists(int key)
    {
        return db.Players.Count(e => e.Id == key) > 0;
    }
}

 

Registration is done in the same way as before, except the MapODataRoute is replaced withMapODataServiceRoute method.

 

C#

1
2
3
    var builder = new ODataConventionModelBuilder();
    builder.EntitySet<Player>(“Player”);
    config.Routes.MapODataServiceRoute(“odata”, “odata”, builder.GetEdmModel());

 

With these in place, I also added DB migrations using the following steps:

  1. 1. run enable-migrations from the package manager console
  2. 2. updated the generated Configuration.cs with my sample data

    C#

    1
    2
    3
    4
    5
    6
    7
    8
       protected override void Seed(WebApplication3.Models.PlayerAppContext context)
       {
      context.Players.AddOrUpdate(
         new Player { Name = “Phil Kessel”, Age = 26},
         new Player { Name = “Dion Phaneuf”, Age = 28 },
         new Player { Name = “Nazem Kadri”, Age = 23 }
      );
    }
  3. 3. added initial migration with add-migration Initial command
  4. 4. executed the DB update using update-database

Next step is to run the whole solution and navigate to the $metadata to see what version of OData we are running – and indeed it is v4.

odata

In OData v4, $inlinecount has been replaced with $count.

count

getall

getplayer

While these are only nightlies, it’s great to see the progress on the OData v4 front. So go ahead and start playing with the next generation web services built on top of OData v4!

Advertisements

2 thoughts on “[.NETWorld] Getting started with OData v4 in ASP.NET Web API

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s