Web API is an application programming interface (API) that is used to enable communication or interaction with software components with each other.
Web API is the enhanced form of the web application to provide services on different devices like laptop, mobile, and others.
The Web API component of the application acts as an HTTP based interface which accepts HTTP requests from a third party client application and performs data operations. The following diagram explain the possible architecture of this hypothetical application using Web API.
This application can expose its business logic to the various client applications using Web API. Web API can effectively use the repositories to decouple the Business and Data Access Layers from the Web API Controller class. The Web API application can use inbuilt Dependency Injection (DI) to inject required dependencies in objects. For e.g. The Data Access can be registered in the DI and can be injected in the Business Layer, further the Business Layer can be registered in DI and injected in the Repository, and Repository in the Web API.
- Principles of REST API
The six principles of REST API are:
- Stateless
- Client-Server
- Uniform Interface
- Cacheable
- Layered System
- Code on demand
we can create a Web API project in two ways:
- Web API with MVC Project
- Stand-alone Web API Project
Entity Framework core(EF core) is an ORM library developed by Microsoft for .NET framework. It helps developers conveniently work with database objects without having need to write SQL scripts.
In this tutorial we shall look into create a Stand-alone Web API project configuring EF Core on a dot net core application and how a table can be created and data operations are performed.
Entity Framework of the .NET comes with two approaches:
- Code-First approach: Focus on the domain of your application and start creating classes for your domain entity rather than design your database first.
- Database-First approach: Database and the related tables are created first and then create an entity data models using database.
we are going to use Code-First approach for our project.
Preparation
- Install .NET 6 SDK: (https://dotnet.microsoft.com/download/dotnet/6.0)
- Install SQL and Setup: (https://www.microsoft.com/en-in/sql-server/sql-server-downloads)
- Install Postman for testing the API: https://www.postman.com/downloads/
Create Project
Add Dependencies
- Install EF Core Design:
Install-Package Microsoft.EntityFrameworkCore.Design -Version 6.0.0
- Install EF Core Tools:
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.0
- Install EF Core SQL:
Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 6.0.2
Connect Project to Database
- Models: A model is an object that represents the data in your application(define the database entities).
(1) Create Models
folder. In the same folder create Movie.cs
file.
Movie.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MovieApiWithEF.Models
{
public class Movie
{
//[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Genre { get; set; }
public DateTime? ReleaseDate { get; set; }
public string? Director { get; set; }
}
}
(2) Create EfCore
folder. Create MovieAppDbContext.cs
file in the same folder.
DbContext: Application code interact with the database. It is this class that manages the database connection and is used to retrieve and save data in the database.
using Microsoft.EntityFrameworkCore;
using MovieApiWithEF.Models;
namespace MovieApiWithEF.EfCore
{
public class MovieAppDbContext: DbContext
{
public DbSet<Movie> Movies { get; set; }
public MovieAppDbContext(DbContextOptions<MovieAppDbContext> options) : base(options)
{
}
}
}
(3) Open appsetings.json
file and add following connection string with MovieApi
database name.
Connection Strings: Keep standard configuration information in connection string. Setup all database related information like server name, database name, user-id, password. Add a connection string property to the appSettings.json file and refer to your DbContext class inside Startup.cs file along with connection string. You will be all set to call your API to connect to a single SQL database for now.
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MovieWebApi;Trusted_Connection=True;MultipleActiveResultSets=True"
},
(4) Open Program.cs
file and add following code for configuring as a service.
UseSqlServer: UseSqlServer() method configures the context to use a sql server database and takes a string representing the connection string as a parameter.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<MovieAppDbContext>(x =>
{
x.UseSqlServer(connectionString);
});
(5) Open Package Manager Console run following commands.
The migration feature in EF core provides a way to incrementally update the database schema to keep it sync with the application's data model while preserving existing data in the database
-
add-migration AddToDb1
: Creates a migration by adding a migration snapshot. -
update-database
: Create or update the database schema.
Repositories
Repository: Repository Pattern is an abstraction of the Data Access Layer. Repositories are classes that hide the logics required to store or retreive data.
(1) Create Repository
folder. In that create Interface named as IMovieRepository
using MovieApiWithEF.Models;
namespace MovieApiWithEF.Repository
{
public interface IMovieRepository
{
IQueryable<Movie> GetMovies();
Movie GetMovie(int id);
bool MovieExists(int id);
bool MovieExists(string title);
bool CreateMovie(Movie movie);
bool UpdateMovie(Movie movie);
bool DeleteMovie(Movie movie);
bool Save();
}
}
(2) Create MovieRepository.cs
file in Repository folder.
using Microsoft.EntityFrameworkCore;
using MovieApiWithEF.EfCore;
using MovieApiWithEF.Models;
using MovieApiWithEF.Repository;
namespace ODataMovieApiWithEF.Repository
{
public class MovieRepository : IMovieRepository
{
private readonly MovieAppDbContext _db;
public MovieRepository(MovieAppDbContext db)
{
_db = db;
}
public bool CreateMovie(Movie movie)
{
_db.Movies.Add(movie);
return Save();
}
public bool DeleteMovie(Movie movie)
{
_db.Movies.Remove(movie);
return Save();
}
public IQueryable<Movie> GetMovies()
{
return _db.Movies.AsQueryable();
}
public bool Save()
{
return _db.SaveChanges() >= 0 ? true : false;
}
public bool UpdateMovie(Movie movie)
{
_db.Movies.Update(movie);
return Save();
}
public bool MovieExists(int id)
{
return _db.Movies.Any(x => x.Id == id);
}
public Movie GetMovie(int id)
{
return _db.Movies.FirstOrDefault(x => x.Id == id);
}
public bool MovieExists(string title)
{
bool value = _db.Movies.Any(y => y.Title.ToLower().Trim() == title.ToLower().Trim());
return value;
}
}
}
(3)Dependency Injection (DI)
Dependency Injection (DI): Is a technique for achieving loose coupling between objects and their collaborators, or dependencies
There are three ways by which dependencies can be registered:
- AddSingleton: Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.
- AddScoped: Scoped lifetime services are created once per request.
- AddTransient: Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.
builder.Services.AddScoped<IMovieRepository, MovieRepository>();
Create API controller
Controller: A controller is responsible for controlling the way that a user interacts with an application. A controller contains the flow control logic. It determines what response to send back to a user when a user makes a browser request
Create a
MoviesController.cs
file under Controllers folder.Routing: Routing is responsible for matching incoming HTTP requests and dispatching those requests to the app's executable endpoints.
The [ApiController] attribute applies inference rules for the default data sources of action parameters. These rules save you from having to identify binding sources manually by applying attributes to the action parameters.
ControllerBase: The ControllerBase Class in provides many methods and properties to handle HTTP Requests and Responses.
IQueryable: IQueryable interface provide the functionality to query data against a specific data source.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using MovieApiWithEF.Models;
using MovieApiWithEF.Repository;
namespace MovieApiWithEF.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MoviesController : ControllerBase
{
private readonly IMovieRepository _movieRepo;
public MoviesController(IMovieRepository movieRepo)
{
_movieRepo = movieRepo;
}
/// <summary>
/// Get list of all Movies
/// </summary>
[HttpGet]
public IQueryable Get()
{
return _movieRepo.GetMovies();
}
/// <summary>
/// Create a new movie
/// </summary>
[HttpPost]
public async Task<IActionResult> Post([FromBody] Movie movie)
{
if (movie == null)
return BadRequest(ModelState);
if (_movieRepo.MovieExists(movie.Title))
{
ModelState.AddModelError("", "Movie already Exist");
return StatusCode(500, ModelState);
}
if (!_movieRepo.CreateMovie(movie))
{
ModelState.AddModelError("", $"Something went wrong while saving movie record of {movie.Title}");
return StatusCode(500, ModelState);
}
return Ok(movie);
}
/// <summary>
/// Update a movie
/// </summary>
/// <return></return>
[HttpPut("{movieId:int}")]
public IActionResult Update(int movieId, [FromBody] Movie movie)
{
if (movie == null || movieId != movie.Id)
return BadRequest(ModelState);
if (!_movieRepo.UpdateMovie(movie))
{
ModelState.AddModelError("", $"Something went wrong while updating movie : {movie.Title}");
return StatusCode(500, ModelState);
}
return NoContent();
}
/// <summary>
/// Update a movie
/// </summary>
/// <return></return>
[HttpDelete("{movieId:int}")]
public IActionResult Delete(int movieId)
{
if (!_movieRepo.MovieExists(movieId))
{
return NotFound();
}
var movieobj = _movieRepo.GetMovie(movieId);
if (!_movieRepo.DeleteMovie(movieobj))
{
ModelState.AddModelError("", $"Something went wrong while deleting movie : {movieobj.Title}");
return StatusCode(500, ModelState);
}
return NoContent();
}
}
}
Testing API on Swagger
- When we run our project it will show following actions to perform.
-Testing GET action: Return all movie objects from database
-Testing POST action: Create a new movie object.
-Testing PUT action: Updating the object with "genre: Action"
-Testing Delete action: Deleting the object with id=3
Repository
You can check for the source code here.
MovieApi-With-Entity-Framework
Keep Learning!
Thank You
Hope this helps, feel free to share your ideas or comment on this article and let me know your thoughts or if you have any questions!
Top comments (1)
Vey helpful,
Started learning.net core recently, this is a good reference, Thanks for your post