ORDS vs C# for REST APIs: Which is Easier? (Side-by-Side Comparison)
ORDS vs C# for Building REST APIs: Which is Easier?
If you've ever built REST APIs using commonly used programming languages, you know the drill: create models, wire up a controller, connect to a database, map DTOs, validate, inject dependencies… and that's before you even touch the security aspect of the API. 😅
Being a past C# lover, I started questioning how easy it was to build REST APIs in ORDS compared to C#.
Use Case
We want to expose the following operations via REST:
GET /countries— list all countriesGET /countries/{id}— get one countryPOST /countries— create a new countryPUT /countries/{id}— update a countryDELETE /countries/{id}— remove a country
Let's see how much effort it takes.
In C#, you need to set up quite a bit before your REST API starts rolling.
Step 1: Define Your Models
public class Country {
public int Id { get; set; }
public string Name { get; set; }
}
Step 2: 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();
}
}
}
Step 3: Set Up EF Core (ORM)
public class AppDbContext : DbContext {
public DbSet<Country> Countries { get; set; }
}
Step 4: Build the API 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, PUT, DELETE methods...
// (truncated for brevity - check full code for complete implementation)
}
}
Step 5: Interface - ICountryRepository.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);
}
}
Step 6: Implementation - CountryRepository.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);
}
}
Step 7: Unit of Work Pattern
Interface - IUnitOfWork.cs
namespace ReviewApp.Interfaces
{
public interface IUnitOfWork
{
ICountryRepository Country { get; }
bool Save();
}
}
Implementation - UnitOfWork.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;
}
}
Phew! That's a lot of code just to expose a simple CRUD API. 😮💨
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 PL/SQL Block
BEGIN
ORDS.ENABLE_OBJECT(
p_schema => 'MY_SCHEMA',
p_object => 'COUNTRY',
p_object_type => 'TABLE',
p_auto_rest_auth => FALSE
);
END;
And voilà ! 🪄
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!
The Verdict
Did you see how much easier it is 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 and code.
The Best of Both Worlds
That said, if you're building a full-fledged app, you can still use C# or other programming languages 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're not a big fan of coding, you can also use APEX to create your REST APIs with low code! It integrates really well with ORDS! 🚀
Summary
C# Approach: Requires models, DTOs, controllers, repositories, unit of work pattern, dependency injection, and AutoMapper configuration. Great for complex enterprise applications with lots of business logic.
ORDS Approach: One simple PL/SQL block and you're done. Perfect for quickly exposing database tables as REST APIs without writing mountains of code.
Choose the right tool for the job! 💪
Comments
Post a Comment