How to Web API .Net Core Basics to Advanced Part 4 Service Layer

Service Layer
Welcome back! In AspNet Core Web API Basics Part 3 Data Access Layer article we talked about separation of concern and how to isolate our application in different layers based on their domain and we implemented Data Access Layer by using Repository pattern. In this article we are going to Implement Business Layer. AspNet Core Web API Service Layer To implement our Business/Service layer we will also make use of Dtos to pass data from our Entities/Models to our presentation layer (Controller) which further isolate our front end. *Question is what is a service layer? *
Service layer is the middle layer between presentation layer and data layer (Repository). It abstracts business logic from data access.This provides easier management, better abstraction and scalability. AspNet Core Web API Service Layer We can start with creating five folders in our project, first one Service to keep our CompanyService.cs class file, at the same time let us create Contract folder inside Service folder to keep our interface class file ICompanyService.cs next, we can create a folder Dtos and create another folder called Company to keep our Company Dtos and the last folder ServiceResponder to keep our ServiceResponse.cs file which we will use to wrap our API response to provide a more meaningful response when a request is sent to our API. Let us start by creating our Dtos CompanyDto.cs

public class CompanyDto  public int Id  get; set; > public Guid GUID  get; set; > [Required] [RegularExpression(@"[a-zA-Z0-9._@+-]", ErrorMessage = "The must be 1 to 150 valid characters which are any digit, any letter and -._@+.")] [StringLength(150, ErrorMessage = "The must be at least and at max characters long.", MinimumLength = 2)] [Display(Name = "CompanyName")] public string CompanyName  get; set; > public DateTimeOffset CreatedDate  get; set; > public bool IsEnabled  get; set; > public bool IsDeleted  get; set; > > 
Enter fullscreen mode

Exit fullscreen mode

CreateCompanyDto.cs
public class CreateCompanyDto  [Required(ErrorMessage = "Company name is required")] [MinLength(2, ErrorMessage = "Company Name can not be less than two characters")] [MaxLength(150, ErrorMessage = "Company Name to long")] public string CompanyName  get; set; > > 
Enter fullscreen mode

Exit fullscreen mode

UpdateCompanyDto.cs
public class UpdateCompanyDto  public Guid GUID  get; set; > [Required] public string CompanyName  get; set; > public bool IsEnabled  get; set; > public bool IsDeleted  get; set; > > 
Enter fullscreen mode

Exit fullscreen mode

Once we are happy with our Dtos we can create our generic API response wrapper.
public class ServiceResponseT> /// /// Generic wrapper for web api response. ///  /// public class ServiceResponseT>  public T Data  get; set; > public bool Success  get; set; > = true; public string Message  get; set; > = null; public string Error  get; set; > = null; public Liststring> ErrorMessages  get; set; > = null; > 
Enter fullscreen mode

Exit fullscreen mode

Now we can start writing code in our ICompanyService.cs and CompanyService.cs files. ICompanyService.cs

 public interface ICompanyService  /// /// Return list of companies which are not marked as deleted. ///  /// List Of CompanyDto TaskServiceResponseListDtos.Company.CompanyDto>>> GetCompaniesAsync(); /// /// Return company record. ///  /// /// CompanyDto TaskServiceResponseDtos.Company.CompanyDto>> GetByIdAsync(int Id); /// /// Return company record. ///  /// /// CompanyDto TaskServiceResponseDtos.Company.CompanyDto>> GetByGUIDAsync(Guid guid); /// /// Add new company record in db ///  /// /// CompanyDto TaskServiceResponseDtos.Company.CompanyDto>> AddCompanyAsync(Dtos.Company.CreateCompanyDto createCompanyDto); /// /// Update company record ///  /// /// CompanyDto TaskServiceResponseDtos.Company.CompanyDto>> UpdateCompanyAsync(Dtos.Company.UpdateCompanyDto updateCompanyDto); /// /// Mark the company record as deleted ///  /// /// bool TaskServiceResponsestring>> SoftDeleteCompanyAsync(Guid guid); > 
Enter fullscreen mode

Exit fullscreen mode

And here is our ICompanyService.cs file. Before we start implementing our service class let us first install another package AutoMapper.Extensions.Microsoft.DependencyInjections after that we can create a new folder called mapper and add a class file . I am going to call it DtoMapping.cs, you can name it what ever you think is best. Basic example how to use AutoMapper In Startup.cs file we need initialize AutoMapper.

public void ConfigureServices(IServiceCollection services)  services.AddAutoMapper(typeof(Startup)); > 
Enter fullscreen mode

Exit fullscreen mode

Now in DtoMapping.cs file we can set up mapping.
 Public DtoMapping()  CreateMapCompany, CompanyDto>().ReverseMap(); > 
Enter fullscreen mode

Exit fullscreen mode

Once we have set up mapping between our Dtos and Entities we can set up our service class.
 public class CompanyService : ICompanyService  private readonly ICompanyRepository _compRepo; private readonly IMapper _mapper; public CompanyService(ICompanyRepository companyRepository, IMapper mapper)  this._compRepo = companyRepository; this._mapper = mapper; > public async TaskServiceResponseCompanyDto>> AddCompanyAsync(CreateCompanyDto createCompanyDto)  ServiceResponseCompanyDto> _response = new(); try  //Check If company exist if (await _compRepo.CompanyExistAsync(createCompanyDto.CompanyName))  _response.Message = "Exist"; _response.Success = false; _response.Data = null; return _response; > Entities.Company _newCompany = new()  CompanyName = createCompanyDto.CompanyName, GUID = Guid.NewGuid(), CreatedDate = DateTimeOffset.UtcNow, IsEnabled = true, IsDeleted = false >; //Add new record if (!await _compRepo.CreateCompanyAsync(_newCompany))  _response.Error = "RepoError"; _response.Success = false; _response.Data = null; return _response; > _response.Success = true; _response.Data = _mapper.MapCompanyDto>(_newCompany); _response.Message = "Created"; > catch (Exception ex)  _response.Success = false; _response.Data = null; _response.Message = "Error"; _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; > return _response; > public async TaskServiceResponseCompanyDto>> GetByGUIDAsync(Guid CompanyGUID)  ServiceResponseCompanyDto> _response = new(); try  var _Company = await _compRepo.GetCompanyByGUIDAsync(CompanyGUID); if (_Company == null)  _response.Success = false; _response.Message = "NotFound"; return _response; > var _CompanyDto = _mapper.MapCompanyDto>(_Company); _response.Success = true; _response.Message = "ok"; _response.Data = _CompanyDto; > catch (Exception ex)  _response.Success = false; _response.Data = null; _response.Message = "Error"; _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; > return _response; > public async TaskServiceResponseCompanyDto>> GetByIdAsync(int Id)  ServiceResponseCompanyDto> _response = new(); try  var _Company = await _compRepo.GetCompanyByIDAsync(Id); if (_Company == null)  _response.Success = false; _response.Message = "Not Found"; return _response; > var _CompanyDto = _mapper.MapCompanyDto>(_Company); _response.Success = true; _response.Message = "ok"; _response.Data = _CompanyDto; > catch (Exception ex)  _response.Success = false; _response.Data = null; _response.Message = "Error"; _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; > return _response; > public async TaskServiceResponseListCompanyDto>>> GetCompaniesAsync()  ServiceResponseListCompanyDto>> _response = new(); try  var CompaniesList = await _compRepo.GetCompaniesAsync(); var CompanyListDto = new ListCompanyDto>(); foreach (var item in CompaniesList)  CompanyListDto.Add(_mapper.MapCompanyDto>(item)); > //OR //CompanyListDto.AddRange(from item in CompaniesList select _mapper.Map(item)); _response.Success = true; _response.Message = "ok"; _response.Data = CompanyListDto; > catch (Exception ex)  _response.Success = false; _response.Data = null; _response.Message = "Error"; _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; > return _response; > public async TaskServiceResponsestring>> SoftDeleteCompanyAsync(Guid CompanyGUID)  ServiceResponsestring> _response = new(); try  //check if record exist var _existingCompany = await _compRepo.CompanyExistAsync(CompanyGUID); if (_existingCompany == false)  _response.Success = false; _response.Message = "NotFound"; _response.Data = null; return _response; > if (!await _compRepo.SoftDeleteCompanyAsync(CompanyGUID))  _response.Success = false; _response.Message = "RepoError"; return _response; > _response.Success = true; _response.Message = "SoftDeleted"; > catch (Exception ex)  _response.Success = false; _response.Data = null; _response.Message = "Error"; _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; > return _response; > public async TaskServiceResponseCompanyDto>> UpdateCompanyAsync(UpdateCompanyDto updateCompanyDto)  ServiceResponseCompanyDto> _response = new(); try  //check if record exist var _existingCompany = await _compRepo.GetCompanyByGUIDAsync(updateCompanyDto.GUID); if (_existingCompany == null)  _response.Success = false; _response.Message = "NotFound"; _response.Data = null; return _response; > //Update _existingCompany.CompanyName = updateCompanyDto.CompanyName; _existingCompany.IsEnabled = updateCompanyDto.IsEnabled; _existingCompany.IsDeleted = updateCompanyDto.IsDeleted; if (!await _compRepo.UpdateCompanyAsync(_existingCompany))  _response.Success = false; _response.Message = "RepoError"; _response.Data = null; return _response; > //Map updateCompanyDto To Company var _companyDto = _mapper.MapCompanyDto>(_existingCompany); _response.Success = true; _response.Message = "Updated"; _response.Data = _companyDto; > catch (Exception ex)  _response.Success = false; _response.Data = null; _response.Message = "Error"; _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; > return _response; > > 
Enter fullscreen mode

Exit fullscreen mode

Point To Note In my projects I never return exception in my service response because application exception should not be exposed to front-end, I am only using it here as an example. I prefer to log application exception.

 _response.ErrorMessages = new Liststring>  Convert.ToString(ex.Message) >; 
Enter fullscreen mode

Exit fullscreen mode

Well done! We managed to get our service layer up and running, time to move on and set up our presentation layer which is controllers. In the next article we are going to set up our first controller. >>>Part 5 How to Web API Core Controllers/Get/Put/Post/Delete