4.6: Adding User Service
Adding Abstract Interface
In the Contracts
project, add a new interface called IUserService.cs
where we'll define the methods we'll be using in our concrete implementation.
public interface IUserService
{
void Add<T>(T entity) where T: class;
void Delete<T>(T entity) where T: class;
Task<bool> SaveAll();
Task<IEnumerable<UserForList>> GetUsers();
Task<UserForDetail> GetUser(int id);
}
The Add()
and Delete()
methods are using generics. We're using generics here so we can use the same methods for different types - if we want to add a User
- but, also if we want to add Photos
or other entities we may add later.
Adding Concrete Implementation
Next, in the .Services
project - add a new class called UserService.cs
.
Indicate that it will implement the IUserService
(: IUserService
).
Add a constructor with the DbContext
as a parameter:
public class UserService : IUserService
{
private readonly EFConnectContext _context;
public UserService(EFConnectContext context)
{
_context = context;
}
// ...
Next, ctrl + .
on IUserService
and select Implement Interface
.
Now we'll implement the methods
Add and Delete Methods
public void Add<T>(T entity) where T : class
{
_context.Add(entity);
}
public void Delete<T>(T entity) where T : class
{
_context.Remove(entity);
}
These look confusing because they're using Generics. They allow us to use these methods for any type (that we supply). Instead of writing Add()
and Delete()
methods for Users
and Photos
- we can just use these methods for both. Generics are a great way to keep it [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself)!
GetUser(id) Method
Next we'll implement the GetUser(id)
method.
public async Task<UserForDetail> GetUser(int id)
{
var user = await _context.Users
.Include(p => p.Photos)
.FirstOrDefaultAsync(u => u.Id == id); // 1
if (user == null)
return null;
var photosToReturn = new List<PhotoForDetail>(); // 2
foreach (var photo in user.Photos) // 3
{
var userPhoto = new PhotoForDetail
{
Id = photo.Id,
Url = photo.Url,
Description = photo.Description,
DateAdded = photo.DateAdded,
IsMain = photo.IsMain
};
photosToReturn.Add(userPhoto);
}
var userToReturn = new UserForDetail // 4
{
Id = user.Id,
Username = user.Username,
Specialty = user.Specialty,
Age = user.DateOfBirth.CalculateAge(), // (extension method)
KnownAs = user.KnownAs,
Created = user.Created,
LastActive = user.LastActive,
Introduction = user.Introduction,
LookingFor = user.LookingFor,
Interests = user.Interests,
City = user.City,
State = user.State,
PhotoUrl = user.Photos.FirstOrDefault(p => p.IsMain).Url,
Photos = photosToReturn
};
return userToReturn; // 5
}
There is a lot going on here! We can break it down into 5 parts. Refer to the steps are commented above.
We're getting all of the users from the context and including the
Photo
s along with them.Next, we're declaring a variable to hold a list of
PhotoForDetail
DTOsHere we're looping through the photos attached to the user, translating them into
PhotoForDetail
DTOs and pushing them onto the list we created in step 2.Now we're creating the
UserForDetail
DTO. We're using our extension method to calculate the Age property. We're using our List ofPhotoForDetail
s as our collection ofPhoto
s.Finally, we're returning the new
UserForDetail
we just created
GetUsers() Method
public async Task<IEnumerable<UserForList>> GetUsers()
{
var users = await _context.Users
.Include(p => p.Photos)
.Select(
e => new UserForList
{
Id = e.Id,
Username = e.Username,
Specialty = e.Specialty,
Age = e.DateOfBirth.CalculateAge(),
KnownAs = e.KnownAs,
Created = e.Created,
LastActive = e.LastActive,
City = e.City,
State = e.State,
PhotoUrl = e.Photos.FirstOrDefault(p => p.IsMain).Url
}
)
.ToListAsync();
return users;
}
There is less going on in this method. If the previous method makes sense to you, this one should be pretty straightforward.
SaveAll() Method
We won't be using this yet, but we'll go ahead and add it since we added it to the interface.
This will be used for saving changes to the database. Right now we're just querying the database, not making any changes.
public async Task<bool> SaveAll()
{
return await _context.SaveChangesAsync() > 0;
}
Register Service in Startup.cs
In the Startup.cs file in the .API project, register the new UserService for dependency injection as we previously added the AuthService:
// ...
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IUserService, UserService>(); // <--- Added
// ...
Last updated