Living life by the motto, "You didn't get this far by giving up." It hasn't failed her yet.
Do you know what loosely coupled code is? How about the service layer? Applying these principles to your code will help your applications be more flexible and have better scalability.
Fellow developers, I have a question for you: Have you ever copy-pasted code?
Trick question—of course you have! Copy-pasting is a hallmark of our field, whether it’s borrowing from your own past projects or taking inspiration from code shared elsewhere on the internet. For this article, I’d like to focus on reusing code within the same project. Let’s begin with an example:
public async Task<IEnumerable<Book>> GetAllMyBooks()
{
return await _context.Books
.Where(b => !b.LoanedToFriend)
.Include(b => b.Author)
.Include(b => b.Genre)
.ToListAsync();
}
In the code above, we have a method for getting all the books in my possession. I’ve excluded books I’ve loaned to a friend, since I don’t have them on hand, and I’ve told my code to include data regarding other models my books are related to: the author, publisher, and genre.
Now let’s say I don’t just want to know what books I have in my possession. I also want a way to sort that information by the publish date, a property of the book, and I also want to do the same except by the name of the book’s genre. For both methods, I want all the same information as before; I just want it sorted differently. Since the only change is the order, I would copy-paste the code from above and add the necessary line to set the order:
public async Task<IEnumerable<Book>> GetAllMyBooksSortedByDate()
{
return await _context.Books
.Where(b => !b.LoanedToFriend)
.Include(b => b.Author)
.Include(b => b.Genre)
.OrderByDescending(b => b.PublishDate)
.ToListAsync();
}
public async Task<IEnumerable<Book>> GetAllMyBooksSortedByGenre()
{
return await _context.Books
.Where(b => !b.LoanedToFriend)
.Include(b => b.Author)
.Include(b => b.Genre)
.OrderBy(b => b.Genre.Name)
.ToListAsync();
}
Now the code can now handle these three tasks. But what happens if I forgot I needed to include the Publisher object the books are related to? Now I need to back track and fix that code in three separate places. That’s a minor task here, but in a larger project, that could potentially be a substantial amount of work, and more importantly, there’s a higher risk of making an error like failing to update everywhere I copy-pasted that code before. That could be very, very bad!
If you're not careful, problems can stack up fast
Let’s kick things up a notch. How about we use this code instead?
public async Task<IEnumerable<Book>> GetAllMyBooks()
{
return await _context.Books
.Where(b => !b.LoanedToFriend)
.Include(b => b.Author)
.Include(b => b.Genre)
.ToListAsync();
}
public async Task<IEnumerable<Book>> GetAllMyBooksSortedByDate()
{
return await GetAllMyBooks().OrderByDescending(b => b.PublishDate);
}
public async Task<IEnumerable<Book>> GetAllMyBooksSortedByGenre()
{
return await GetAllMyBooks().OrderBy(b => b.Genre);
}
Better! Now if we find ourselves wanting to include the Publisher objects related to each Book, we only need to add in:
.Include(b => b.Publisher)
This is a step in the right direction, but what if we want to use on of those methods to display information about my books on a Razor View? This is where is loosely coupled code utilizing a service layer really shines.
public class BookService : IBookService
{
public async Task<IEnumerable<Book>> GetAllMyBooks()
{
return await _context.Books
.Where(b => !b.LoanedToFriend)
.Include(b => b.Author)
.Include(b => b.Publisher)
.Include(b => b.Genre)
.ToListAsync();
}
}
@inject IBookService _Bookservice
<ul>
@foreach (Book book in _Bookservice.GetAllMyBooks())
{
<li>@book.Title</li>
<li>Author: @book.Author.Name</li>
<li>Genre: @book.Genre.name</li>
}
</ul>
As you can see, using a service layer makes things much easier, and it doesn’t require changing the Razor View’s model. You can make changes to the original method GetAllMyBooks() and rest easy knowing it’s changed everywhere, and if you forget where you’re calling that method of the service layer, Visual Studio will track all references to the method and provide hyperlinks to those calls.
Loosely coupled code is flexible and has better scalability, so stop serving that old copy-pasta and knock things up a notch!
0 Comments