Introduction
Minimal API is a simplified approach for building HTTP APIs fast within ASP.NET Core, compared to the controller-based APIs. Developers can build fully functioning REST endpoints with minimal code and configuration, especially without controller, action and even the formatters. See ASP.NET Core Minimal API details at here.
Since Microsoft.AspNetCore.OData version 9.4.0, it enables developers to achieve OData functionalities on ASP.NET Core minimal APIs. In this post, I’d like to go through the basics of enabling OData functionalities on a Minimal API application. Let’s get started.
Prerequisites
First of first, let’s create an ASP.NET Core Empty application named ODataMinimalAPI with following Nuget packages installed.
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="9.4.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.11" />
</ItemGroup>
The Program.cs contains the following codes:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
The lambda expression in preceding MapGet is called Route Handler. They are methods that execute when the route matches. Route handlers can be:
- A lambda expression,
- A local function,
- An instance method
- A static method
- Or a RequestDelegate
Basically, ASP.NET Core OData decorates the endpoint with metadata or even changes the route handler to enable OData functionalities.
Models and database context
In the project, Let’s create the following classes to represent data managed in the application:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Address Location { get; set; }
public Info Info { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int Amount { get; set; }
}
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
public class Info
{
public string Email { get; set; }
public string Phone { get; set; }
}
Let’s also create a DbContext as below:
using Microsoft.EntityFrameworkCore;
public class ApplicationDb : DbContext
{
public ApplicationDb(DbContextOptions<ApplicationDb> options) : base(options) { }
public DbSet<Customer> Customers=> Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Customer>().HasKey(x => x.Id);
modelBuilder.Entity<Order>().HasKey(x => x.Id);
modelBuilder.Entity<Customer>().OwnsOne(x => x.Info);
modelBuilder.Entity<Customer>().OwnsOne(x => x.Location);
}
}
Then, update Program.cs as below:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationDb>(options => options.UseInMemoryDatabase("CustomerOrderList"));
var app = builder.Build();
app.MakeSureDbCreated(); // Create sample data ahead
app.MapGet("/json/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
app.Run();
Run the application. Open ‘Endpoints Explorer’ by clicking [View]-> [Other Windows]-> [Endpoints Explorer] menu, you may see an endpoint listed as:

Right-click on the /json/customers and select [Generate Request] menu, a ODataMinimalApi.http is created with a Http Request. We can send a request by clicking [Send request] on the endpoint in the http file and check the response. Below is the sample data created ahead by sending request to /json/customers.
[
{
"id": 1,
"name": "Alice",
"location": {
"city": "Redmond",
"street": "123 Main St"
},
"info": {
"email": "[email protected]",
"phone": "123-456-7819"
},
"orders": [
{
"id": 11,
"amount": 9
},
{
"id": 12,
"amount": 19
}
]
},
{
"id": 2,
"name": "Johnson",
"location": {
"city": "Sammamish",
"street": "228 Ave NE"
},
"info": {
"email": "[email protected]",
"phone": "233-468-7289"
},
"orders": [
{
"id": 21,
"amount": 8
},
{
"id": 22,
"amount": 76
}
]
}
]
OData Edm Model
Let’s create the following OData Edm model ahead which is used in some scenarios below. Be noted, I mark Address as entity type, and Info as complex type explicitly.
public class EdmModelBuilder
{
public static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");
builder.EntitySet<Order>("Orders");
builder.EntityType<Address>(); // Intentionally built it as entity type
builder.ComplexType<Info>(); // Intentionally built it as complex type
return builder.GetEdmModel();
}
}
OData basic scenarios
WithODataResult()
We enable developers to call an extension method named WithODataResult() on the endpoint to get ‘OData-formatted‘ response. Let’s add a new endpoint as below:
app.MapGet("odata/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
.WithODataResult();
Then, generate a request in ODataMinimalApi.http as GET {{ODataMinimalApi_HostAddress}}/odata/customers. Run the application, click the [Send request], (I will omit these steps in the following section), we can get the OData response payload as below:
{
"@odata.context": "http://localhost:5134/$metadata#Customer",
"value": [
{
"Id": 1,
"Name": "Alice",
"Location": {
"City": "Redmond",
"Street": "123 Main St"
},
"Info": {
"Email": "[email protected]",
"Phone": "123-456-7819"
}
},
{
"Id": 2,
"Name": "Johnson",
"Location": {
"City": "Sammamish",
"Street": "228 Ave NE"
},
"Info": {
"Email": "[email protected]",
"Phone": "233-468-7289"
}
}
]
}
Be noted, Location and Info are returned as complex type properties (without an explicit key property), meanwhile Orders is not presented by default since it’s a navigation property (with a key property).
IODataModelConfiguration
One way to change the model is to provide an implementation of IODataModelConfiguration interface. For example, we can create a class MyODataModelConfiguration implemented IODataModelConfiguration interface to config the Info type as an entity type. As a result, the Info property on Customer is built as a navigation property.
public class MyODataModelConfiguration : IODataModelConfiguration
{
public ODataModelBuilder Apply(HttpContext context, ODataModelBuilder builder, Type clrType)
{
if (context.Request.Path.StartsWithSegments("/modelconfig/customers", StringComparison.OrdinalIgnoreCase))
{
builder.EntityType<Info>();
}
return builder;
}
}
Register the configuration as a global service into the Dependency Injection container as below:
builder.Services.AddSingleton<IODataModelConfiguration, MyODataModelConfiguration>();
Meanwhile, add a new endpoint as:
app.MapGet("modelconfig/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
.WithODataResult();
Then, run the application and send request to GET {{ODataMinimalApi_HostAddress}}/modelconfig/customers, we can get a different payload as:
{
"@odata.context": "http://localhost:5134/$metadata#Customer",
"value": [
{
"Id": 1,
"Name": "Alice",
"Location": {
"City": "Redmond",
"Street": "123 Main St"
}
},
{
"Id": 2,
"Name": "Johnson",
"Location": {
"City": "Sammamish",
"Street": "228 Ave NE"
}
}
]
}
Be noted, Info property is not presented by default since it’s a navigation property.
WithODataModel()
Another way to configure the model for an endpoint to call WithODataModel(model) extension method as below example:
IEdmModel model = EdmModelBuilder.GetEdmModel();
app.MapGet("/withmodel/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
.WithODataResult()
.WithODataModel(model);
Then, run and send request to GET {{ODataMinimalApi_HostAddress}}/withmodel/customers, we can get the following payload:
{
"@odata.context": "http://localhost:5134/$metadata#Customers",
"value": [
{
"Id": 1,
"Name": "Alice",
"Info": {
"Email": "[email protected]",
"Phone": "123-456-7819"
}
},
{
"Id": 2,
"Name": "Johnson",
"Info": {
"Email": "[email protected]",
"Phone": "233-468-7289"
}
}
]
}
Be noted, in the given model, Info is built as complex type meanwhile Address is built as entity type explicitly.
WithODataVersion()
We can change the OData spec version by calling WithODataVersion(version) extension method as:
app.MapGet("/v401/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
.WithODataResult()
.WithODataModel(model)
.WithODataVersion(ODataVersion.V401);
Then, run and send request to GET {{ODataMinimalApi_HostAddress}}/v401/customers, we can get an OData v4.01 payload:
{
"@context": "http://localhost:5134/$metadata#Customers",
"value": [
{
...
},
{
...
}
]
}
We can see the odata. prefix is omitted in the context URL by default in OData v4.01 version.
WithODataBaseAddressFactory()
We can change the base address in the payload by calling WithODataBaseAddressFactory(lambda). For example:
app.MapGet("/baseaddress/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
.WithODataResult()
.WithODataModel(model)
.WithODataBaseAddressFactory(c => new Uri("http://abc.com"));
Then, run and send request to GET {{ODataMinimalApi_HostAddress}}/baseaddress/customers, we can get the following payload:
{
"@odata.context": "http://abc.com/$metadata#Customers",
"value": [
{
...
},
{
...
}
]
}
Be noted, the context URL is changed using the URL specified in the extension method.
OData query option scenarios
OData is so powerful owing to its richful querying capabilities. Let’s see how to enable OData query options on the Minimal API endpoint.
Configure OData query options
For security reason, OData query options are disabled globally by default. If we want to enable them, just call the following method:
builder.Services.AddOData(q => q.EnableAll());
It enables all query options. Of course, we can enable a certain query option by calling its corresponding method only, for example, Expand() only enables $expand, etc.
ODataQueryOptions<T>
One way to enable OData query functionalities on an endpoint is to add a ODataQueryOptions<T> parameter explicitly on the route handler. Here’s the example:
app.MapGet("/queryoptions/customers", (ApplicationDb db, ODataQueryOptions<Customer> queryOptions) =>
queryOptions.ApplyTo(db.Customers.Include(s => s.Orders)))
.WithODataResult();
Then run and send request to GET {{ODataMinimalApi_HostAddress}}/queryoptions/customers, we can get:
{
"@odata.context": "http://localhost:5134/$metadata#Customer",
"value": [
{
"Id": 1,
"Name": "Alice"
},
{
"Id": 2,
"Name": "Johnson"
}
]
}
Be noted, in query option pattern, the Address and Info are built as entity type by default. It’s a little bit different compared to the non-query option scenario mentioned above.
We can change the model by implementing the IODataModelConfiguration interface or calling WithODataModel(model) extension method. If we mark the model configuration class as attribute, we can also decorate the configuration on the parameter as below.
(ApplicationDb db, [MyModelConfiguration] ODataQueryOptions<Customer> queryOptions) => {}
Now, let’s run and test the query options on this endpoint as:GET {{ODataMinimalApi_HostAddress}}/queryoptions/customers?$expand=info($select=Phone)&$filter=startswith(Name,’A’)&$select=Name, we can get the following payload:
{
"@odata.context": "http://localhost:5134/$metadata#Customer(Name,Info(Phone))",
"value": [
{
"Name": "Alice",
"Info": {
"Phone": "123-456-7819"
}
}
]
}
ODataQueryEndpointFilter
The other way to enable OData query on an endpoint is to apply ODataQueryEndpointFilter on the endpoint.
Here’s an example:
app.MapGet("queryfilter/customers", (ApplicationDb db) => db.Customers.Include(s => s.Orders))
.AddODataQueryEndpointFilter()
.WithODataResult();
We can test this endpoint using the following request:
GET {{ODataMinimalApi_HostAddress}}/queryfilter/customers?$select=location($select=street)&$orderby=Id&$select=Name
Send the request and we can get the following payload:
{
"@odata.context": "http://localhost:5134/$metadata#Customer(Location/Street,Name)",
"value": [
{
"Name": "Alice",
"Location": {
"Street": "123 Main St"
}
},
{
"Name": "Johnson",
"Location": {
"Street": "228 Ave NE"
}
}
]
}
Be noted, the Address and Info are built as complex type again in this scenario. You can use the IODataModelConfiguration or WithODataModel(model) to specify the model.
OData advanced scenarios
Service document and Metadata
We can call MapODataServiceDocument() extension method to register an endpoint which returns the service document of an Edm model.
For example:
app.MapODataServiceDocument("/$document", model);
Run the application and send request to GET {{ODataMinimalApi_HostAddress}}/$document, we can get the result as:
{
"@odata.context": "http://localhost:5134/$metadata",
"value": [
{
"name": "Customers",
"kind": "EntitySet",
"url": "Customers"
},
{
"name": "Orders",
"kind": "EntitySet",
"url": "Orders"
}
]
}
Meanwhile, we can call MapODataMetadata() extension method to register an endpoint which returns the schema document of an Edm model.
For example:
app.MapODataMetadata("/$metadata", model);
Run the application and send reqquest to GET {{ODataMinimalApi_HostAddress}}/$metadata, we can get the result as:
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="ODataMinimalApi.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Info" Type="ODataMinimalApi.Models.Info" />
<NavigationProperty Name="Location" Type="ODataMinimalApi.Models.Address" />
<NavigationProperty Name="Orders" Type="Collection(ODataMinimalApi.Models.Order)" />
</EntityType>
<EntityType Name="Order">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="Amount" Type="Edm.Int32" Nullable="false" />
</EntityType>
<EntityType Name="Address">
<Property Name="City" Type="Edm.String" />
<Property Name="Street" Type="Edm.String" />
</EntityType>
<ComplexType Name="Info">
<Property Name="Email" Type="Edm.String" />
<Property Name="Phone" Type="Edm.String" />
</ComplexType>
</Schema>
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="Container">
<EntitySet Name="Customers" EntityType="ODataMinimalApi.Models.Customer">
<NavigationPropertyBinding Path="Orders" Target="Orders" />
</EntitySet>
<EntitySet Name="Orders" EntityType="ODataMinimalApi.Models.Order" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Function and action
We can build endpoint to handle OData function and action. Let’s define the following edm function and action in the model builder as examples:
public static IEdmModel GetEdmModel()
{
// …
var function = builder.EntityType<Customer>().Collection.Function("AddNameSuffixForAllCustomer");
function.Parameter<string>("suffix");
function.ReturnsCollectionFromEntitySet<Customer>("Customers");
var action = builder.EntityType<Customer>().Action("RateByName");
action.Parameter<string>("name");
action.Parameter<int>("age");
action.Returns<string>();
return builder.GetEdmModel();
}
- Edm Function
We can define the following endpoint to handle the edm function:
app.MapGet("function/customers/addsuffix(suffix={suffix})", (ApplicationDb db, string suffix) =>
{
var customers = db.Customers.ToList();
foreach (var customer in customers)
{
customer.Name += suffix;
}
return customers;
})
.WithODataResult()
.WithODataModel(model)
.AddODataQueryEndpointFilter()
.WithODataPathFactory(
(h, t) =>
{
string suffix = h.GetRouteValue("suffix") as string;
IEdmEntitySet customers = model.FindDeclaredEntitySet("Customers");
IEdmOperation function = model.FindDeclaredOperations("Default.AddNameSuffixForAllCustomer").Single();
IList<OperationSegmentParameter> parameters = new List<OperationSegmentParameter>
{
new OperationSegmentParameter("suffix", suffix)
};
return new ODataPath(new EntitySetSegment(customers), new OperationSegment(function, parameters, customers));
});
Then generate the following request and send request to:
GET {{ODataMinimalApi_HostAddress}}/function/customers/addsuffix(suffix=’%20Sr’)?$select=name
We can get the following payload:
{
"@odata.context": "http://localhost:5134/$metadata#Customers(Name)",
"value": [
{
"Name": "Johnson' Sr'"
},
{
"Name": "Alice' Sr'"
}
]
}
Be noted, the parameter suffix value is binded directly from the route data. Therefore, developers should take responsibility to convert the raw value to OData value, for example, to remove the single quote for the string value. We’ll figure out a better solution to improve this scenario in the next version.
- Edm Action
We can add ODataActionParameters as a parameter the route handler to access the Edm action parameters as follows:
app.MapPost("action/customers/{id}/rateByName", (ApplicationDb db, int id, ODataActionParameters parameters) =>
{
Customer customer = db.Customers.FirstOrDefault(s => s.Id == id);
if (customer == null)
{
return null;
}
return $"{customer.Name}: {System.Text.Json.JsonSerializer.Serialize(parameters)}"; // for test only
})
.WithODataResult()
.WithODataModel(model)
.WithODataPathFactory(
(h, t) =>
{
string idStr = h.GetRouteValue("id") as string;
int id = int.Parse(idStr);
IEdmEntitySet customers = model.FindDeclaredEntitySet("Customers");
IDictionary<string, object> keysValues = new Dictionary<string, object>();
keysValues["Id"] = id;
IEdmAction action = model.SchemaElements.OfType<IEdmAction>().First(a => a.Name == "RateByName");
return new ODataPath(new EntitySetSegment(customers),
new KeySegment(keysValues, customers.EntityType, customers),
new OperationSegment(action, null)
);
});
Then generate the following POST request to test:
@id=1
POST {{ODataMinimalApi_HostAddress}}/action/customers/{{id}}/rateByName
Content-Type: application/json
{
"name": "kerry",
"age":16
}
We can get the following response:
{
"@odata.context": "http://localhost:5134/$metadata#Edm.String",
"value": "Alice: {\"name\":\"kerry\",\"age\":16}"
}
Be noted, WithODataPathFactory() is necessary so far to provide an OData path to get the response from edm function and action.
Delta request
We can add Detal<T> parameter on a router handler to change the single entity. For example:
app.MapPatch("delta/customers/{id}", (ApplicationDb db, int id, Delta<Customer> delta) =>
{
Customer customer = db.Customers.FirstOrDefault(s => s.Id == id);
if (customer == null)
{
return null;
}
delta.Patch(customer);
return customer;
})
.WithODataResult()
.WithODataModel(model)
.AddODataQueryEndpointFilter()
.WithODataPathFactory(
(h, t) =>
{
string idStr = h.GetRouteValue("id") as string;
int id = int.Parse(idStr);
IEdmEntitySet customers = model.FindDeclaredEntitySet("Customers");
IDictionary<string, object> keysValues = new Dictionary<string, object>();
keysValues["Id"] = id;
return new ODataPath(new EntitySetSegment(customers), new KeySegment(keysValues, customers.EntityType, customers));
});
Generate the patch request as:
PATCH {{ODataMinimalApi_HostAddress}}/delta/customers/1?$expand=location;$select=id,name,location
Content-Type: application/json
Â
{
"Name": "kerry",
"[email protected]": {
"street": "new street"
}
}
Then run the application and send the request, we can get an updated customer as:
{
"@odata.context": "http://localhost:5134/$metadata#Customers(Id,Name,Location,Location())/$entity",
"Id": 1,
"Name": "kerry",
"Location": {
"City": "Redmond",
"Street": "new street"
}
}
We can also use DeltaSet<T> to handle the changes for an entity set as:
app.MapPatch("deltaset/customers", (ApplicationDb db, DeltaSet<Customer> changes) =>
{
// omit others …
})
I’d omit codes here for simplicity.
Batch request
We can call UseODataMiniBatching() to enable the batch request on the Minimal API. For example:
app.UseODataMiniBatching("odata/$batch", model);
Be noted, we should call this method as early as possible to support OData batching.
Let’s use the following request to test the $batch:
POST {{ODataMinimalApi_HostAddress}}/odata/$batch
Content-Type: application/json
Accept: application/json
Â
{
"requests": [
{
"id": "1",
"atomicityGroup": "f7de7314-2f3d-4422-b840-ada6d6de0f18",
"method": "POST",
"url": "http://localhost:5134/action/customers/2/rateByName",
"headers": {
"OData-Version": "4.0",
"Content-Type": "application/json",
"Accept": "application/json"
},
"body": {
"name": "wu",
"age": 20
}
},
{
"id": "2",
"atomicityGroup": "f7de7314-2f3d-4422-b840-ada6d6de0f18",
"method": "GET",
"url": "http://localhost:5134/queryoptions/customers?$expand=info($select=Phone)&$filter=startswith(Name,'A')&$select=Name",
"headers": {
"OData-Version": "4.0",
"Content-Type": "application/json;odata.metadata=minimal",
"Accept": "application/json;odata.metadata=minimal"
}
}
]
}
Run the application and send the request, we can get the following response:
{
"responses": [
{
"id": "1",
"atomicityGroup": "fd7fb5df-a27f-4dde-a72e-71c142028037",
"status": 200,
"headers": {
"content-type": "application/json",
"odata-version": "4.0"
},
"body": {
"@odata.context": "http://localhost:5134/$metadata#Edm.String",
"value": "Johnson: {\"name\":\"wu\",\"age\":20}"
}
},
{
"id": "2",
"atomicityGroup": "fd7fb5df-a27f-4dde-a72e-71c142028037",
"status": 200,
"headers": {
"content-type": "application/json",
"odata-version": "4.0"
},
"body": {
"@odata.context": "http://localhost:5134/$metadata#Customer(Name,Info(Phone))",
"value": [
{
"Name": "Alice",
"Info": {
"Phone": "123-456-7819"
}
}
]
}
}
]
}
That’s all.
Summary
This post went throw the scenarios to enable OData functionalities on ASP.NET Core Minimal API. For new simple projects, OData recommend using Minimal APIs to build the OData endpoints as they provide a simplified, high-performance approach for building APIs with minimal code and configuration. Of course, please do not hesitate to leave your comments below or let me know your thoughts through [email protected]. Thanks.
I uploaded the whole project to this repository.
0 comments
Be the first to start the discussion.