ORDS Auto REST vs C# with Entity Framework.
Introduction
Being a past c# lover, I started questioning how easy was it to build REST apis in ORDS compared to c#.
Spoiler: ORDS is crazy simple for the basics. Let me show you a side-by-side comparison using the same use case — managing a Country table — and without diving into security (we’ll ignore auth and tokens, and we'll just focus on the REST experience).
Use Case
We want to expose the following operations via REST:
- GET /countries — list all countries
- GET /countries/{id} — get one country
- POST /countries — create a new country
- PUT /countries/{id} — update a country
- DELETE /countries/{id} — remove a country
Let’s see how much effort it takes.
Option 1: C# with Entity Framework Core
- Define your models:
public class Country {
public int Id { get; set; }
public string Name { get; set; }
}
- Define the DTO and autoMapper profile.
namespace ReviewApp.Dtos
{
public class CountryDto
{
public int Id { get; set; }
public string Name { get; set; }
}
}
namespace ReviewApp.Mapping
{
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Country, CountryDto>().ReverseMap();
}
}
}
- Set up EF Core (ORM)
public class AppDbContext : DbContext {
public DbSet<Country> Countries { get; set; }
}
- Build theAPI Controller
using Microsoft.AspNetCore.Mvc;
using AutoMapper;
using ReviewApp.Interfaces;
using ReviewApp.Models;
using ReviewApp.Dtos;
namespace ReviewApp.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CountryController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
private readonly IMapper _mapper;
public CountryController(IUnitOfWork unitOfWork, IMapper mapper)
{
_unitOfWork = unitOfWork;
_mapper = mapper;
}
// GET: api/country
[HttpGet]
[ProducesResponseType(200, Type = typeof(IEnumerable<CountryDto>))]
public IActionResult GetAll()
{
var countries = _unitOfWork.Country.GetAll().ToList();
var result = _mapper.Map<List<CountryDto>>(countries);
return Ok(result);
}
// GET: api/country/5
[HttpGet("{id}")]
[ProducesResponseType(200, Type = typeof(CountryDto))]
[ProducesResponseType(404)]
public IActionResult GetById(int id)
{
var country = _unitOfWork.Country.Get(c => c.Id == id);
if (country == null)
return NotFound();
var result = _mapper.Map<CountryDto>(country);
return Ok(result);
}
// POST: api/country
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(422)]
public IActionResult Create([FromBody] CountryDto countryDto)
{
if (countryDto == null)
return BadRequest(ModelState);
var exists = _unitOfWork.Country
.GetAll()
.Any(c => c.Name.Trim().ToLower() == countryDto.Name.Trim().ToLower());
if (exists)
{
ModelState.AddModelError("", "The country already exists.");
return StatusCode(422, ModelState);
}
var country = _mapper.Map<Country>(countryDto);
_unitOfWork.Country.Add(country);
bool saved = _unitOfWork.Save();
if (!saved)
{
ModelState.AddModelError("", "Saving to the database failed.");
return StatusCode(500, ModelState);
}
return StatusCode(201, "Country created successfully.");
}
// PUT: api/country/5
[HttpPut("{id}")]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public IActionResult Update(int id, [FromBody] CountryDto countryDto)
{
if (countryDto == null || id != countryDto.Id)
return BadRequest(ModelState);
var existingCountry = _unitOfWork.Country.Get(c => c.Id == id);
if (existingCountry == null)
return NotFound();
_mapper.Map(countryDto, existingCountry);
_unitOfWork.Country.Update(existingCountry);
bool saved = _unitOfWork.Save();
if (!saved)
{
ModelState.AddModelError("", "Update failed.");
return StatusCode(500, ModelState);
}
return NoContent();
}
// DELETE: api/country/5
[HttpDelete("{id}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public IActionResult Delete(int id)
{
var country = _unitOfWork.Country.Get(c => c.Id == id);
if (country == null)
return NotFound();
_unitOfWork.Country.Remove(country);
bool saved = _unitOfWork.Save();
if (!saved)
{
ModelState.AddModelError("", "Delete failed.");
return StatusCode(500, ModelState);
}
return NoContent();
}
}
}
- Interface - ICountryRespository.cs
using ReviewApp.Models;
using System.Linq.Expressions;
namespace ReviewApp.Interfaces
{
public interface ICountryRepository
{
IEnumerable<Country> GetAll();
Country Get(Expression<Func<Country, bool>> predicate);
void Add(Country entity);
void Update(Country entity);
void Remove(Country entity);
bool Exists(int id);Uni
}
}
- Implementation - CountryRespository.cs
using ReviewApp.Data;
using ReviewApp.Interfaces;
using ReviewApp.Models;
using System.Linq.Expressions;
namespace ReviewApp.Repositories
{
public class CountryRepository : ICountryRepository
{
private readonly ApplicationDbContext _context;
public CountryRepository(ApplicationDbContext context)
{
_context = context;
}
public IEnumerable<Country> GetAll() => _context.Countries.ToList();
public Country Get(Expression<Func<Country, bool>> predicate) =>
_context.Countries.FirstOrDefault(predicate);
public void Add(Country entity) => _context.Countries.Add(entity);
public void Update(Country entity) => _context.Countries.Update(entity);
public void Remove(Country entity) => _context.Countries.Remove(entity);
public bool Exists(int id) => _context.Countries.Any(c => c.Id == id);
}
}
- Unit of work - Interface IUnitOfWork.cs
namespace ReviewApp.Interfaces
{
public interface IUnitOfWork
{
ICountryRepository Country { get; }
bool Save();
}
}
- Unit of work - Implementation UnitWork.cs
using ReviewApp.Data;
using ReviewApp.Interfaces;
using ReviewApp.Repositories;
namespace ReviewApp.UnitOfWork
{
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
Country = new CountryRepository(context);
}
public ICountryRepository Country { get; private set; }
public bool Save() => _context.SaveChanges() > 0;
}
}
Option 2: ORDS with Auto REST (Super Fast)
With ORDS you don’t have to write any backend code to get basic REST endpoints. If your table already exists, you can make it RESTful in seconds.
- Execute this PLSQL block.
BEGIN
ORDS.ENABLE_OBJECT(
p_schema => 'MY_SCHEMA',
p_object => 'COUNTRY',
p_object_type => 'TABLE',
p_auto_rest_auth => FALSE
);
END;
And voila. Now you get all the endpoints needed to manage the country table.
ORDS handles the REST plumbing for you — no controllers, no DTOs, no object mappers, no Unit of Work, no nothing. 🪄
Did you see how much easier it’s to create rest endpoints with ORDS rather than doing it with c#.
Using ORDS Auto REST is much easier for quickly exposing your Oracle database as a REST API. With minimal configuration/Code.
That said, if you’re building a full-fledged app, you can still use C# or other programming language to call REST APIs created with ORDS, combining the simplicity of ORDS for quick API creation with the power of C# for more complex application logic!
And if you are not a big fan of coding you can also use APEX to create your rest apis with low code! And it integrate really well with ORDS!
Comments
Post a Comment