Repository
https://github.com/dotnet/core
What Will I Learn?
- You will learn about Dependency Injection.
- You will learn about Repository Pattern.
- You will learn how to architect the Service layer of a Robust Web application.
- You will learn learn to create the service layer of our Hotel Management System.
- You will learn about concepts in C# such as generics and asynchronous programming.
Requirements
- Basic knowledge of C# programming language
- Visual Studio 2017/ VS code/ Any suitable code editor
- Previous Tutorial
Difficulty
Intermediate
Tutorial Contents
Last time we looked at creating the model entities for our application and setting up our database. We went through the thought process of visualizing our prospective software solution and crafting out the relationship between parts of our system. Then we learnt how to connect successfully to any remote database and leverage EntityFramework as an Object Relational Manager to communicate with our database using c# code.
In this tutorial, we are going to create our service layer. Basically, we would naturally just make use of the application context to fetch data from the database straight in our controller class. However, it is a better practice to separate all your database related code in a separate class which represents the service layer. It helps to decouple your application and aid testing. It also helps to keep your controller class neat and simple.
We will make use of dependency injection to further keep our structure decoupled. By dependency injection, I mean that we are go have our service injected into our controller as a dependency. By doing this, at any point in time, we could always alter the service code without having to touch the controller class at all. We would only then need to change the service dependent class which the controller is working with. This pattern of separating database logic and application logic is known as Repository Pattern
Creating Our Service Interface
First off, we create a service interface with methods which the controller will interact with.
Create a new file in the Services folder, call it IGenericHotelService.cs and put in the following code
public interface IGenericHotelService<TEntity>
{
Task<IEnumerable<TEntity>> GetAllItemsAsync();
Task<TEntity> GetItemByIdAsync(Guid? id);
Task<IEnumerable<TEntity>> SearchFor(Expression<Func<TEntity, bool>> expression);
Task CreateItemAsync(TEntity entity);
Task EditItemAsync(TEntity entity);
Task DeleteItemAsync(TEntity entity);
}
As can be seen above, we have created six methods in our interface. Being an interface, any class that inherits it has to implement all the methods defined above.
Notice how we are using a generic name type
IGenericHotelService<TEntity>
. This makes this interface extensible. TEntity could be anything. In our case, it would be one of our models - Room, Booking, Feature, etc.Also notice how each method is of return type
Task
. This implies that the method might not return a result right away. It helps us leverage asynchronous programming such that if the said method is awaited, other actions can be performed on the thread, and thereafter return to the awaited point when the result is ready.
Creating Our Service Class Implementation
Having created the interface, we would now create a class that actually implements this interface and interacts with the database.
In that same Services folder, create another class and call it GenericHotelService
Have it inherit from the IGenericHotelService Class
public class GenericHotelService<TEntity> : IGenericHotelService<TEntity> where TEntity : class
Notice how the GenericHotelService class also depends on the abstract entity TEntity.
Dependency Injection of ApplicationDbContext
Quite coincidentally, this method also depends on an injection instance of the ApplicationDbContext. This is because the ApplicationDbContext can take up any value(that is, it can be connected to any database) depending on the connection string provided and configured in the Startup.cs
class.
So we inject the ApplicationDbContext in our constructor and also assign our abstract DbSet as follows:
private readonly ApplicationDbContext _context;
protected DbSet<TEntity> DbSet;
public GenericHotelService(ApplicationDbContext context)
{
_context = context;
DbSet = context.Set<TEntity>();
}
Specific Methods Implementation
- Get All Items
public async Task<IEnumerable<TEntity>> GetAllItemsAsync()
{
return await DbSet.ToArrayAsync();
}
- Get Single Item
public async Task<TEntity> GetItemByIdAsync(Guid? id)
{
if (id == null)
{
return null;
}
return await DbSet.FindAsync(id);
}
- Search Item
public async Task<IEnumerable<TEntity>> SearchFor(Expression<Func<TEntity, bool>> expression)
{
return await DbSet.Where(expression).ToArrayAsync();
}
- Add New Item
public async Task CreateItemAsync(TEntity entity)
{
DbSet.Add(entity);
await _context.SaveChangesAsync();
}
- Edit Item
public async Task EditItemAsync(TEntity entity)
{
_context.Update(entity);
await _context.SaveChangesAsync();
}
- Delete Item
public async Task DeleteItemAsync(TEntity entity)
{
DbSet.Remove(entity);
await _context.SaveChangesAsync();
}
The method implementations shouldn't seem strange to you if you've worked with older versions of ASP.NET (framework). Even if you haven't, just staring at it a while longer will make it clearer. Its all CRUD operations.
Notice how we call the SaveChangesAsync()
method after every action that required writing to the database. Without calling this method, your actions will be executed against the context items and might not be persisted on the database.
In our next class, we will see how this service layer interfaces with the controllers and how dependency injection actually falls into the equation of the controller/service. As an extra/take-home, try to scaffold a new controller for any of our methods and see the code generated for you. Here's how the Details action of the scaffolded Room controller looks like:
public async Task<IActionResult> Details(Guid? id)
{
if (id == null)
{
return NotFound();
}
var room = await _context.Rooms
.Include(r => r.RoomType)
.SingleOrDefaultAsync(m => m.ID == id);
if (room == null)
{
return NotFound();
}
return View(room);
}
Compare this with the GetItemByIdAsync
method of our service class. Try to juxtapose between them and identify the difference between the codes.
Feel free to shoot your questions in the comment section!
Curriculum
Proof of Work Done
Github Repo for the tutorial solution:
https://github.com/Johnesan/TheHotelApplication
Thanks for your contribution.
Your contribution has been evaluated according to Utopian rules and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post,Click here
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Hey @johnesan
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!
Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!