Add a service class

You’ve created a model, a view, and a controller. Before you use the model and view in the controller, you also need to write code that will get the user’s to-do items from a database.

You could write this database code directly in the controller, but it’s a better practice to keep all the database code in a separate class called a service. This helps keep the controller as simple as possible, and makes it easier to test and change the database code later.

Separating your application logic into one layer that handles database access and another layer that handles presenting a view is sometimes called a layered, 3-tier, or n-tier architecture.

.NET and C# include the concept of interfaces, where the definition of an object’s methods and properties is separate from the class that actually contains the code for those methods and properties. Interfaces make it easy to keep your classes decoupled and easy to test, as you’ll see here (and later in the Automated testing chapter).

First, create an interface that will represent the service that can interact with to-do items in the database. By convention, interfaces are prefixed with “I”. Create a new file in the Services directory:

Services/ITodoItemService.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;
  4. using AspNetCoreTodo.Models;
  5. namespace AspNetCoreTodo.Services
  6. {
  7. public interface ITodoItemService
  8. {
  9. Task<IEnumerable<TodoItem>> GetIncompleteItemsAsync();
  10. }
  11. }

Note that the namespace of this file is AspNetCoreTodo.Services. Namespaces are a way to organize .NET code files, and it’s customary for the namespace to follow the directory the file is stored in (AspNetCoreTodo.Services for files in the Services directory, and so on).

Because this file (in the AspNetCoreTodo.Services namespace) references the TodoItem class (in the AspNetCoreTodo.Models namespace), it needs to include a using statement at the top of the file to import that namespace. Without the using statement, you’ll see an error like:

  1. The type or namespace name 'TodoItem' could not be found (are you missing a using directive or an assembly reference?)

Since this is an interface, there isn’t any actual code here, just the definition (or method signature) of the GetIncompleteItemsAsync method. This method requires no parameters and returns a Task<IEnumerable<TodoItem>>.

If this syntax looks confusing, think: “a Task that contains a list of TodoItems”.

The Task type is similar to a future or a promise, and it’s used here because this method will be asynchronous. In other words, the method may not be able to return the list of to-do items right away because it needs to go talk to the database first. (More on this later.)

Now that the interface is defined, you’re ready to create the actual service class. I’ll cover database code in depth in the Use a database chapter, but for now you’ll just fake it and return hard-coded values:

Services/FakeTodoItemService.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;
  4. using AspNetCoreTodo.Models;
  5. namespace AspNetCoreTodo.Services
  6. {
  7. public class FakeTodoItemService : ITodoItemService
  8. {
  9. public Task<IEnumerable<TodoItem>> GetIncompleteItemsAsync()
  10. {
  11. // Return an array of TodoItems
  12. IEnumerable<TodoItem> items = new[]
  13. {
  14. new TodoItem
  15. {
  16. Title = "Learn ASP.NET Core",
  17. DueAt = DateTimeOffset.Now.AddDays(1)
  18. },
  19. new TodoItem
  20. {
  21. Title = "Build awesome apps",
  22. DueAt = DateTimeOffset.Now.AddDays(2)
  23. }
  24. };
  25. return Task.FromResult(items);
  26. }
  27. }
  28. }

This FakeTodoItemService implements the ITodoItemService interface but always returns the same array of two TodoItems. You’ll use this to test the controller and view, and then add real database code in Use a database.