ODataTutorial
OData Tutorial
LICENSE
MIT
In this tutorial, I will give you how to create Web API using .NET 6 and OData. We will create simple CRUD of Note App.
Install .NET 6 SDK (Currently at RC.1, but maybe after this article published, it will become stable): https://dotnet.microsoft.com/download/dotnet/6.0
Install PostgreSQL and Setup (Feel free to use another database provider, since we will use EF Core which support In-Memory too): https://www.postgresql.org/download/
Install dotnet ef tools (need to install .NET 6 SDK first): dotnet tool install -g dotnet-ef --version 6.0.0-rc.1
dotnet new webapi -o ODataTutorial
dotnet new sln
dotnet sln add ODataTutorial
dotnet add ODataTutorial package Microsoft.AspNetCore.OData
dotnet add ODataTutorial package Microsoft.EntityFrameworkCore.Design --version 6.0.0-rc.1 --prerelease
dotnet add ODataTutorial package Microsoft.EntityFrameworkCore.Tools --version 6.0.0-rc.1 --prerelease
dotnet add ODataTutorial package Npgsql.EntityFrameworkCore.PostgreSQL --version 6.0.0-rc.1 --prerelease
Swashbuckle.AspNetCore.Swagger
: dotnet add ODataTutorial package Swashbuckle.AspNetCore.Swagger --version 6.4.0
Swashbuckle.AspNetCore.SwaggerGen
: dotnet add ODataTutorial package Swashbuckle.AspNetCore.SwaggerGen --version 6.4.0
Swashbuckle.AspNetCore.SwaggerUI
: dotnet add ODataTutorial package Swashbuckle.AspNetCore.SwaggerUI --version 6.4.0
Thanks for hajsf
to raise the issue when setup the project.
Thanks for the great tutorial, but looks you missed mentioning the installtion required for:
Swashbuckle.AspNetCore.Swagger
Swashbuckle.AspNetCore.SwaggerGen
Swashbuckle.AspNetCore.SwaggerUI
For setup dependencies, you can see this result (file: ODataTutorial/ODataTutorial.csproj
:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OData" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0-rc.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0-rc.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0-rc.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.4.0" />
</ItemGroup>
</Project>
ConnectionString
to appsettings.json
. (This is only partial of file to make it short. Please change with your database settings.)
{
"ConnectionStrings": {
"Default": "Host=localhost;Username=postgres;Password=;Database=odatatutorial"
}
}
ODataTutorial/Entities/Note.cs
using System.ComponentModel.DataAnnotations;
namespace ODataTutorial.Entities;
public class Note
{
public Guid Id { get; set; }
[Required]
public string MessageNote { get; set; } = default!;
}
ODataTutorial/EntityFramework/NoteAppContext.cs
using Microsoft.EntityFrameworkCore;
using ODataTutorial.Entities;
namespace ODataTutorial.EntityFramework;
public class NoteAppContext : DbContext
{
public DbSet<Note> Notes { get; set; } = default!
public NoteAppContext(DbContextOptions<NoteAppContext> options) : base(options)
{
}
}
ODataTutorial/Program.cs
to setup the DbContext
using Microsoft.EntityFrameworkCore;
using ODataTutorial.Entities;
using ODataTutorial.EntityFramework;
// ... all existing using if have
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<NoteAppContext>(
options => options.UseNpgsql(builder.Configuration.GetConnectionString("Default"))
);
// ... the rest of Program.cs
Create New Migration: dotnet ef migrations --project ODataTutorial add AddNoteTable
Update Database: dotnet ef database update --project ODataTutorial
ODataTutorial/Controllers/NotesController.cs
.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Deltas;
using Microsoft.AspNetCore.OData.Formatter;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Results;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using Microsoft.EntityFrameworkCore;
using ODataTutorial.Entities;
using ODataTutorial.EntityFramework;
namespace ODataTutorial.Controllers;
public class NotesController : ODataController
{
private readonly NoteAppContext _db;
private readonly ILogger<NotesController> _logger;
public NotesController(NoteAppContext dbContext, ILogger<NotesController> logger)
{
_logger = logger;
_db = dbContext;
}
[EnableQuery(PageSize = 15)]
public IQueryable<Note> Get()
{
return _db.Notes;
}
[EnableQuery]
public SingleResult<Note> Get([FromODataUri] Guid key)
{
var result = _db.Notes.Where(c => c.Id == key);
return SingleResult.Create(result);
}
[EnableQuery]
public async Task<IActionResult> Post([FromBody] Note note)
{
_db.Notes.Add(note);
await _db.SaveChangesAsync();
return Created(note);
}
[EnableQuery]
public async Task<IActionResult> Patch([FromODataUri] Guid key, Delta<Note> note)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var existingNote = await _db.Notes.FindAsync(key);
if (existingNote == null)
{
return NotFound();
}
note.Patch(existingNote);
try
{
await _db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!NoteExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(existingNote);
}
[EnableQuery]
public async Task<IActionResult> Delete([FromODataUri] Guid key)
{
var existingNote = await _db.Notes.FindAsync(key);
if (existingNote == null)
{
return NotFound();
}
_db.Notes.Remove(existingNote);
await _db.SaveChangesAsync();
return StatusCode(StatusCodes.Status204NoContent);
}
private bool NoteExists(Guid key)
{
return _db.Notes.Any(p => p.Id == key);
}
}
ODataTutorial/Program.cs
to add OData Settings.
using Microsoft.AspNetCore.OData;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
// ... another existing using
static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new();
builder.EntitySet<Note>("Notes");
return builder.GetEdmModel();
}
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// another services setup
builder.Services.AddControllers().AddOData(opt => opt.AddRouteComponents("v1", GetEdmModel()).Filter().Select().Expand());
The ODataTutorial/Program.cs
will become like this.
using Microsoft.AspNetCore.OData;
using Microsoft.EntityFrameworkCore;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
using ODataTutorial.Entities;
using ODataTutorial.EntityFramework;
static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new();
builder.EntitySet<Note>("Notes");
return builder.GetEdmModel();
}
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<NoteAppContext>(
options => options.UseNpgsql(builder.Configuration.GetConnectionString("Default"))
);
builder.Services.AddControllers().AddOData(opt => opt.AddRouteComponents("v1", GetEdmModel()).Filter().Select().Expand());
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "ODataTutorial", Version = "v1" });
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ODataTutorial v1"));
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
You can visit the repository in here.
You can explore the OData
with postman. The advantage you use OData
you can query the list with filter, sort, etc., and it reflect to database query. You can the log for the query, as example like this (when filter with equal).
Happy exploring!
Thank you. Hope you like it, if you have another suggestion about the article/tutorial please comment in here.
Have a nice day!