microsoft/migrating-webapi-odata
> Migrates legacy ASP.NET Web API OData (Microsoft.AspNet.WebApi.OData) to ASP.NET Core OData (Microsoft.AspNetCore.OData). Use ONLY when Microsoft.AspNet.WebApi.OData has been flagged as obsolete or deprecated and must be replaced — not for version-bump scenarios where Microsoft.AspNet.WebApi.OData is still supported.
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill migrating-webapi-odata
Migrate OData services from ASP.NET Web API (Microsoft.AspNet.WebApi.OData or Microsoft.AspNet.OData) to ASP.NET Core OData (Microsoft.AspNetCore.OData). The core changes are registering OData via dependency injection with AddOData(), replacing convention-based routing with endpoint routing, and updating controllers to inherit from the ASP.NET Core ODataController base class. EDM model building remains similar but is wired differently.
<PackageReference Include="Microsoft.AspNet.WebApi.OData" Version="5.*" />
<PackageReference Include="Microsoft.AspNet.OData" Version="7.*" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="{version-for-target-framework}" />
Migration Progress:
- [ ] Step 1: Detect OData usage
- [ ] Step 2: Update project file references
- [ ] Step 3: Register OData in DI
- [ ] Step 4: Migrate controllers
- [ ] Step 5: Update EDM model configuration
- [ ] Step 6: Migrate routing
- [ ] Step 7: Build and verify
Scan the project for:
using System.Web.OData; or using Microsoft.AspNet.OData; statementsODataController or EntitySetControllerODataConventionModelBuilder or ODataModelBuilder usageMapODataServiceRoute or MapODataRoute calls in WebApiConfig[EnableQuery] or [Queryable] attributesODataQueryOptions<T> parametersRemove old packages and add the new package reference (see "Package Reference Changes" above).
Replace route-based OData registration with DI-based configuration in Program.cs or Startup.ConfigureServices:
// Old: in WebApiConfig.cs
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
// New: in Program.cs
builder.Services.AddControllers()
.AddOData(options =>
{
options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(100);
options.AddRouteComponents("odata", GetEdmModel());
});
using directives from System.Web.OData or Microsoft.AspNet.OData to Microsoft.AspNetCore.OData.Routing.Controllers and Microsoft.AspNetCore.OData.QueryMicrosoft.AspNetCore.OData.Routing.Controllers.ODataController instead of the legacy base classEntitySetController<T, TKey> with ODataController — implement CRUD actions manually[EnableQuery] attributes — they work in ASP.NET Core OData with the same semanticsODataConventionModelBuilder still exists in ASP.NET Core OData with a compatible API. Extract the model builder into a helper method:
static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Order>("Orders");
return builder.GetEdmModel();
}
If using explicit ODataModelBuilder (non-convention), review property/navigation bindings — the API is mostly compatible but some extension methods have moved.
| Old Pattern | New Pattern | Notes |
|------------|------------|-------|
| config.MapODataServiceRoute(name, prefix, model) | options.AddRouteComponents(prefix, model) | Registered inside AddOData |
| Convention routing with ODataRoute | Endpoint routing via attribute routes | Use [ODataAttributeRouting] or convention-based with AddRouteComponents |
| [ODataRoute("Products({key})")] | [HttpGet("Products({key})")] or convention routing | Explicit OData route attributes still supported |
| Batch endpoint ($batch) | options.AddRouteComponents(...).Services | Enable with routeOptions.EnableBatchRequests() |
For debugging route issues, enable the OData route debug endpoint:
app.UseODataRouteDebug();
This exposes /$odata to list all registered OData routes.
dotnet build
curl https://localhost:5001/odata/$metadata
$select, $filter, and $expand to confirm query options workVerify the route prefix in AddRouteComponents matches the URL path. Check /$odata debug endpoint for registered routes.
Ensure Select(), Filter(), and other query options are enabled in AddOData. ASP.NET Core OData disables all query options by default — they must be explicitly opted in.
EntitySetController does not exist in ASP.NET Core OData. Inherit from ODataController and implement standard CRUD action methods (Get, Post, Put, Patch, Delete).
ASP.NET Core OData uses System.Text.Json by default. If entities have circular references or complex inheritance, configure serializer options or switch to Newtonsoft.Json via Microsoft.AspNetCore.OData.NewtonsoftJson.
If controller actions return 404, add [ODataAttributeRouting] to the controller or ensure convention routing matches the entity set name to the controller name (e.g., ProductsController for entity set Products).
Take microsoft/migrating-webapi-odata from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.