Web API 방법 .Net Core 기초부터 고급까지 4부 서비스 계층
15128 단어 csharpdotnetasparchitecture
다시 오신 것을 환영합니다!
기사에서 우리는 관심의 분리와 도메인을 기반으로 다른 계층에서 응용 프로그램을 격리하는 방법에 대해 이야기했으며 저장소 패턴을 사용하여 데이터 액세스 계층을 구현했습니다. 이 기사에서는 비즈니스 계층을 구현하려고 합니다.
비즈니스/서비스 레이어를 구현하기 위해 Dtos를 사용하여 엔터티/모델의 데이터를 프런트 엔드를 추가로 격리하는 프레젠테이션 레이어(컨트롤러)로 전달할 것입니다.
*질문은 서비스 레이어란 무엇입니까? *
서비스 계층은 프레젠테이션 계층과 데이터 계층(Repository) 사이의 중간 계층입니다. 데이터 액세스에서 비즈니스 로직을 추상화합니다. 이를 통해 보다 쉬운 관리, 더 나은 추상화 및 확장성을 제공합니다.
프로젝트에 5개의 폴더를 생성하는 것으로 시작할 수 있습니다. 첫 번째는 CompanyService.cs 클래스 파일을 보관할 Service입니다. 동시에 Service 폴더 안에 Contract 폴더를 생성하여 인터페이스 클래스 파일 ICompanyService.cs를 보관하고 다음으로 생성할 수 있습니다. Dtos 폴더를 만들고 Company라는 또 다른 폴더를 만들어 회사 Dtos를 유지하고 마지막 폴더인 ServiceResponder를 만들어 ServiceResponse.cs 파일을 유지합니다. 이 파일은 요청이 API로 전송될 때 보다 의미 있는 응답을 제공하기 위해 API 응답을 래핑하는 데 사용할 것입니다.
Dtos를 생성하여 시작하겠습니다.
회사Dto.cs
public class CompanyDto
{
public int Id { get; set; }
public Guid GUID { get; set; }
[Required]
[RegularExpression(@"[a-zA-Z0-9._@+-]{2,150}",
ErrorMessage = "The {0} must be 1 to 150 valid characters which are any digit, any letter and -._@+.")]
[StringLength(150, ErrorMessage = "The {0} must be at least {2} and at max {1} 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; }
}
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; }
}
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; }
}
Dtos에 만족하면 일반 API 응답 래퍼를 만들 수 있습니다.
public class ServiceResponse<T>
/// <summary>
/// Generic wrapper for web api response.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ServiceResponse<T>
{
public T Data { get; set; }
public bool Success { get; set; } = true;
public string Message { get; set; } = null;
public string Error { get; set; } = null;
public List<string> ErrorMessages { get; set; } = null;
}
이제 ICompanyService.cs 및 CompanyService.cs 파일에서 코드 작성을 시작할 수 있습니다.
ICompanyService.cs
public interface ICompanyService
{
/// <summary>
/// Return list of companies which are not marked as deleted.
/// </summary>
/// <returns>List Of CompanyDto</returns>
Task<ServiceResponse<List<Dtos.Company.CompanyDto>>> GetCompaniesAsync();
/// <summary>
/// Return company record.
/// </summary>
/// <param name="Id"></param>
/// <returns>CompanyDto</returns>
Task<ServiceResponse<Dtos.Company.CompanyDto>> GetByIdAsync(int Id);
/// <summary>
/// Return company record.
/// </summary>
/// <param name="guid"></param>
/// <returns>CompanyDto</returns>
Task<ServiceResponse<Dtos.Company.CompanyDto>> GetByGUIDAsync(Guid guid);
/// <summary>
/// Add new company record in db
/// </summary>
/// <param name="createCompanyDto"></param>
/// <returns>CompanyDto</returns>
Task<ServiceResponse<Dtos.Company.CompanyDto>> AddCompanyAsync(Dtos.Company.CreateCompanyDto createCompanyDto);
/// <summary>
/// Update company record
/// </summary>
/// <param name="updateCompanyDto"></param>
/// <returns>CompanyDto</returns>
Task<ServiceResponse<Dtos.Company.CompanyDto>> UpdateCompanyAsync(Dtos.Company.UpdateCompanyDto updateCompanyDto);
/// <summary>
/// Mark the company record as deleted
/// </summary>
/// <param name="guid"></param>
/// <returns>bool</returns>
Task<ServiceResponse<string>> SoftDeleteCompanyAsync(Guid guid);
}
그리고 여기 ICompanyService.cs 파일이 있습니다. 서비스 클래스 구현을 시작하기 전에 먼저 다른 패키지 AutoMapper.Extensions.Microsoft.DependencyInjections를 설치한 다음 mapper라는 새 폴더를 만들고 클래스 파일을 추가할 수 있습니다. .... DtoMapping.cs라고 부르겠습니다. 당신이 최고라고 생각하는 이름을 지정할 수 있습니다.
AutoMapper를 사용하는 기본 예제
Startup.cs 파일에서 AutoMapper를 초기화해야 합니다.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(Startup));
}
이제 DtoMapping.cs 파일에서 매핑을 설정할 수 있습니다.
Public DtoMapping()
{
CreateMap<Company, CompanyDto>().ReverseMap();
}
Dto와 엔티티 간의 매핑을 설정하면 서비스 클래스를 설정할 수 있습니다.
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 Task<ServiceResponse<CompanyDto>> AddCompanyAsync(CreateCompanyDto createCompanyDto)
{
ServiceResponse<CompanyDto> _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.Map<CompanyDto>(_newCompany);
_response.Message = "Created";
}
catch (Exception ex)
{
_response.Success = false;
_response.Data = null;
_response.Message = "Error";
_response.ErrorMessages = new List<string> { Convert.ToString(ex.Message) };
}
return _response;
}
public async Task<ServiceResponse<CompanyDto>> GetByGUIDAsync(Guid CompanyGUID)
{
ServiceResponse<CompanyDto> _response = new();
try
{
var _Company = await _compRepo.GetCompanyByGUIDAsync(CompanyGUID);
if (_Company == null)
{
_response.Success = false;
_response.Message = "NotFound";
return _response;
}
var _CompanyDto = _mapper.Map<CompanyDto>(_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 List<string> { Convert.ToString(ex.Message) };
}
return _response;
}
public async Task<ServiceResponse<CompanyDto>> GetByIdAsync(int Id)
{
ServiceResponse<CompanyDto> _response = new();
try
{
var _Company = await _compRepo.GetCompanyByIDAsync(Id);
if (_Company == null)
{
_response.Success = false;
_response.Message = "Not Found";
return _response;
}
var _CompanyDto = _mapper.Map<CompanyDto>(_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 List<string> { Convert.ToString(ex.Message) };
}
return _response;
}
public async Task<ServiceResponse<List<CompanyDto>>> GetCompaniesAsync()
{
ServiceResponse<List<CompanyDto>> _response = new();
try
{
var CompaniesList = await _compRepo.GetCompaniesAsync();
var CompanyListDto = new List<CompanyDto>();
foreach (var item in CompaniesList)
{
CompanyListDto.Add(_mapper.Map<CompanyDto>(item));
}
//OR
//CompanyListDto.AddRange(from item in CompaniesList select _mapper.Map<CompanyDto>(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 List<string> { Convert.ToString(ex.Message) };
}
return _response;
}
public async Task<ServiceResponse<string>> SoftDeleteCompanyAsync(Guid CompanyGUID)
{
ServiceResponse<string> _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 List<string> { Convert.ToString(ex.Message) };
}
return _response;
}
public async Task<ServiceResponse<CompanyDto>> UpdateCompanyAsync(UpdateCompanyDto updateCompanyDto)
{
ServiceResponse<CompanyDto> _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.Map<CompanyDto>(_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 List<string> { Convert.ToString(ex.Message) };
}
return _response;
}
}
참고 사항 내 프로젝트에서는 응용 프로그램 예외가 프런트 엔드에 노출되어서는 안 되기 때문에 서비스 응답에서 예외를 반환하지 않습니다. 여기서는 예제로만 사용하고 있습니다. 응용 프로그램 예외를 기록하는 것을 선호합니다.
_response.ErrorMessages = new List<string> { Convert.ToString(ex.Message) };
잘했어요! 우리는 서비스 계층을 시작하고 실행하고 컨트롤러인 프레젠테이션 계층을 설정하고 설정할 시간을 관리했습니다. 다음 기사에서는 첫 번째 컨트롤러를 설정하겠습니다.
Reference
이 문제에 관하여(Web API 방법 .Net Core 기초부터 고급까지 4부 서비스 계층), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/scorpio69/how-to-web-api-net-core-basics-to-advanced-part-4-service-layer-31gk텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)