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#.

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 — without diving into security (we'll ignore auth and tokens and 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

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. 😮‍💨

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 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

Popular posts from this blog

ORDS REST-Enabled SQL Service: How to Customize Response Format (Complete Guide)

Introduction to Oracle Rest Data Services (ORDS) – A Beginner’s Guide