Plant API 컨트롤러를 완성하고, 다음 API인 Plant Number
에 대해 같은 작업을 수행하는 연습을 할 것입니다.
Plant Number 모델을 생성하고, 이를 기반으로 CRUD 작업을 구현하는 것이 목표입니다. 다음은 강의 내용의 요약 및 번역입니다.
- 모델 생성: Plant Number 클래스를 모델 폴더에 추가합니다. 이 클래스는 각 식물의 고유 번호와 특별한 요구 사항(예: 장애인 접근 가능 여부)을 포함합니다.
- 속성:
PlantNo
: 식물 번호 (사용자가 입력, 고유 키로 사용됨)SpecialDetail
: 특별한 요구 사항에 대한 설명Created
및Updated
날짜
- 데이터 주석:
PlantNo
는 기본 키로 지정되며, 데이터베이스에서 자동으로 생성되지 않도록 설정합니다.
- DTO 생성: CRUD 작업을 위해
PlantNumberDTO
,PlantNumberCreateDTO
,PlantNumberUpdateDTO
를 생성합니다. 이 DTO들은 초기에는 동일한 속성을 가지지만, 향후 요구 사항 변경에 따라 쉽게 수정할 수 있도록 별도로 관리하는 것이 좋습니다.
// PlantNumber.cs
public class PlantNumber
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int PlantNo { get; set; }
public string SpecialDetails { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
// PlantNumberDTO.cs
public class PlantNumberDTO
{
[Required]
public int PlantNo { get; set; }
public string SpecialDetails { get; set; }
}
//PlantNumberCreateDTO.cs
public class PlantNumberCreateDTO
{
[Required]
public int PlantNo { get; set; }
public string SpecialDetails { get; set; }
}
//PlantNumberUpdateDTO.cs
public class PlantNumberUpdateDTO
{
[Required]
public int PlantNo { get; set; }
public string SpecialDetails { get; set; }
}
데이터베이스 테이블 생성
- ApplicationDbContext 수정:
PlantNumbers
에 대한DbSet
을 추가하여 데이터베이스에 테이블을 생성합니다.
리포지토리 추가
- 인터페이스 및 구현:
IPlantNumberRepository
인터페이스와PlantNumberRepository
클래스를 생성하여 CRUD 작업을 정의하고 구현합니다.
컨트롤러 추가
- PlantNumberAPIController 생성: CRUD 작업을 위한 RESTful API 엔드포인트를 구현합니다. 각 메소드는
PlantNumberRepository
를 사용하여 데이터베이스 작업을 수행합니다.
AutoMapper 설정
- 매핑 구성:
PlantNumber
와 관련 DTO 간의 매핑을MappingConfig
파일에 추가하여 객체 변환을 자동화합니다.
// PlantNumberAPIController.cs 추가
[Route("api/PlantNumberAPI")]
[ApiController]
public class PlantNumberAPIController : ControllerBase
{
protected APIResponse _response;
private readonly IPlantNumberRepository _dbPlantNumber;
private readonly IMapper _mapper;
public PlantNumberAPIController(IPlantNumberRepository dbPlantNumber, IMapper mapper)
{
_dbPlantNumber = dbPlantNumber;
_mapper = mapper;
this._response = new();
}
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<APIResponse>> GetPlantNumbers()
{
try
{
IEnumerable<PlantNumber> plantNumberList = await _dbPlantNumber.GetAllAsync();
_response.Result = _mapper.Map<List<PlantNumberDTO>>(plantNumberList);
_response.StatusCode = HttpStatusCode.OK;
return Ok(_response);
}
catch (Exception ex)
{
_response.IsSuccess = false;
_response.ErrorMessages
= new List<string>() { ex.ToString() };
}
return _response;
}
[HttpGet("{id:int}", Name = "GetPlantNumber")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<APIResponse>> GetPlantNumber(int id)
{
try
{
if (id == 0)
{
_response.StatusCode = HttpStatusCode.BadRequest;
return BadRequest(_response);
}
var plantNumber = await _dbPlantNumber.GetAsync(u => u.PlantNo == id);
if (plantNumber == null)
{
_response.StatusCode = HttpStatusCode.NotFound;
return NotFound(_response);
}
_response.Result = _mapper.Map<PlantNumberDTO>(plantNumber);
_response.StatusCode = HttpStatusCode.OK;
return Ok(_response);
}
catch (Exception ex)
{
_response.IsSuccess = false;
_response.ErrorMessages
= new List<string>() { ex.ToString() };
}
return _response;
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<APIResponse>> CreatePlantNumber([FromBody] PlantNumberCreateDTO createDTO)
{
try
{
if (await _dbPlantNumber.GetAsync(u => u.PlantNo == createDTO.PlantNo) != null)
{
ModelState.AddModelError("CustomError", "Plant Number already Exists!");
return BadRequest(ModelState);
}
if (createDTO == null)
{
return BadRequest(createDTO);
}
PlantNumber plantNumber = _mapper.Map<PlantNumber>(createDTO);
await _dbPlantNumber.CreateAsync(plantNumber);
_response.Result = _mapper.Map<PlantNumberDTO>(plantNumber);
_response.StatusCode = HttpStatusCode.Created;
return CreatedAtRoute("GetPlant", new { id = plantNumber.PlantNo }, _response);
}
catch (Exception ex)
{
_response.IsSuccess = false;
_response.ErrorMessages
= new List<string>() { ex.ToString() };
}
return _response;
}
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[HttpDelete("{id:int}", Name = "DeletePlantNumber")]
public async Task<ActionResult<APIResponse>> DeletePlantNumber(int id)
{
try
{
if (id == 0)
{
return BadRequest();
}
var plantNumber = await _dbPlantNumber.GetAsync(u => u.PlantNo == id);
if (plantNumber == null)
{
return NotFound();
}
await _dbPlantNumber.RemoveAsync(plantNumber);
_response.StatusCode = HttpStatusCode.NoContent;
_response.IsSuccess = true;
return Ok(_response);
}
catch (Exception ex)
{
_response.IsSuccess = false;
_response.ErrorMessages
= new List<string>() { ex.ToString() };
}
return _response;
}
[HttpPut("{id:int}", Name = "UpdatePlantNumber")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<APIResponse>> UpdatePlantNumber(int id, [FromBody] PlantNumberUpdateDTO updateDTO)
{
try
{
if (updateDTO == null || id != updateDTO.PlantNo)
{
return BadRequest();
}
PlantNumber model = _mapper.Map<PlantNumber>(updateDTO);
await _dbPlantNumber.UpdateAsync(model);
_response.StatusCode = HttpStatusCode.NoContent;
_response.IsSuccess = true;
return Ok(_response);
}
catch (Exception ex)
{
_response.IsSuccess = false;
_response.ErrorMessages
= new List<string>() { ex.ToString() };
}
return _response;
}
}
// MappingConfig.cs 수정
CreateMap<PlantNumber, PlantNumberDTO>().ReverseMap();
CreateMap<PlantNumber, PlantNumberCreateDTO>().ReverseMap();
CreateMap<PlantNumber, PlantNumberUpdateDTO>().ReverseMap();
// Program.cs 수정
builder.Services.AddScoped<IPlantNumberRepository, PlantNumberRepository>();
// IPlantNumberRepository.cs 추가
public interface IPlantNumberRepository : IRepository<PlantNumber>
{
Task<PlantNumber> UpdateAsync(PlantNumber entity);
}
// PlantNumberRepository.cs 추가
public class PlantNumberRepository : Repository<PlantNumber>, IPlantNumberRepository
{
private readonly ApplicationDbContext _db;
public PlantNumberRepository(ApplicationDbContext db) : base(db)
{
_db = db;
}
public async Task<PlantNumber> UpdateAsync(PlantNumber entity)
{
entity.UpdatedDate = DateTime.Now;
_db.PlantNumbers.Update(entity);
await _db.SaveChangesAsync();
return entity;
}
}
패키지 콘솔 실행
PM> add-migration AddPlantNumberTable
PM> update-database
이를 통해 는 .Net Core Web API를 사용하여 데이터베이스와 상호작용하는 방법, 리포지토리 패턴의 적용, AutoMapper를 사용한 객체 매핑, 그리고 RESTful API 엔드포인트의 구현 방법을 실습합니다.