예약 종류 관리 ASP.NET Core MVC 애플리케이션
안녕하세요. 이번에는 AppointmentsTypes 테이블을 이용하여 예약 종류를 관리하는 방법을 설명하겠습니다. 이 강좌에서는 다음과 같은 단계로 진행됩니다.
- 데이터베이스 테이블 생성
- 모델 클래스 생성
- DbContext 클래스 생성
- 리포지토리 클래스 생성
- Program.cs 파일에서 DI 설정
- ASP.NET Core MVC 컨트롤러와 뷰 페이지 작성
각 단계를 차례대로 진행하면서, 예약 종류를 관리하는 ASP.NET Core MVC 앱을 만들어 보겠습니다.
1. 데이터베이스 테이블 생성
먼저, AppointmentsTypes
테이블을 생성합니다. 이 테이블은 예약 종류의 이름과 활성화 여부, 만들어진 날짜 및 시간 등을 저장하는 테이블입니다. 예약 종류는 "Scheduled", "Completed", "Incomplete", "Canceled" 등과 같이 여러 종류가 있을 수 있습니다.
코드: AppointmentsTypes.sql
CREATE TABLE AppointmentsTypes (
Id INT PRIMARY KEY IDENTITY(1,1),
AppointmentTypeName NVARCHAR(50) NOT NULL,
IsActive BIT NOT NULL DEFAULT 1,
DateCreated DATETIME NOT NULL DEFAULT GETDATE()
)
2. 모델 클래스 생성
이제, AppointmentsTypes 테이블과 매핑되는 모델 클래스를 생성합니다. 예약 종류의 이름과 활성화 여부, 만들어진 날짜 및 시간 등의 정보를 저장할 수 있도록 클래스를 정의합니다.
코드: AppointmentType.cs
using System;
namespace VisualAcademy.Models
{
public class AppointmentType
{
public int Id { get; set; }
public string AppointmentTypeName { get; set; }
public bool IsActive { get; set; }
public DateTime DateCreated { get; set; }
}
}
3. DbContext 클래스 생성
다음으로, AppointmentsTypes 테이블과 연결되는 DbContext 클래스를 생성합니다. 이 클래스는 데이터베이스와 앱을 연결하는 역할을 합니다.
using Microsoft.EntityFrameworkCore;
namespace VisualAcademy.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<AppointmentType> AppointmentTypes { get; set; }
}
}
4. 리포지토리 클래스 생성
AppointmentsTypes 테이블에 대한 데이터베이스 작업을 수행하는 리포지토리 클래스를 생성합니다. 이 클래스는 예약 종류를 추가, 수정, 삭제하고, 조회하는 메서드를 제공합니다.
using VisualAcademy.Models;
using System.Collections.Generic;
namespace VisualAcademy.Repositories
{
public interface IAppointmentTypeRepository
{
IEnumerable<AppointmentType> GetAppointmentTypes();
AppointmentType GetAppointmentType(int id);
void AddAppointmentType(AppointmentType appointmentType);
void UpdateAppointmentType(AppointmentType appointmentType);
void DeleteAppointmentType(int id);
}
}
위 코드에서는 예약 종류 정보를 조회, 추가, 수정, 삭제하는 메서드를 가진 IAppointmentTypeRepository 인터페이스를 정의합니다. 이 인터페이스를 구현하여 AppointmentType 테이블을 관리하는 리포지토리 클래스를 만듭니다.
using System.Collections.Generic;
using System.Linq;
using VisualAcademy.Models;
namespace VisualAcademy.Repositories
{
public class AppointmentTypeRepository : IAppointmentTypeRepository
{
private readonly ApplicationDbContext _context;
public AppointmentTypeRepository(ApplicationDbContext context)
{
_context = context;
}
public void AddAppointmentType(AppointmentType appointmentType
{
_context.AppointmentTypes.Add(appointmentType);
_context.SaveChanges();
}
public void DeleteAppointmentType(int id)
{
var appointmentType = _context.AppointmentTypes.Find(id);
_context.AppointmentTypes.Remove(appointmentType);
_context.SaveChanges();
}
public AppointmentType GetAppointmentType(int id)
{
return _context.AppointmentTypes.Find(id);
}
public IEnumerable<AppointmentType> GetAppointmentTypes()
{
return _context.AppointmentTypes.ToList();
}
public void UpdateAppointmentType(AppointmentType appointmentType)
{
_context.Update(appointmentType);
_context.SaveChanges();
}
}
}
다음은 IDbContextFactory를 사용하는 Repository 클래스입니다.
using VisualAcademy.Data;
using VisualAcademy.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace VisualAcademy.Repositories
{
public class AppointmentTypeRepository : IAppointmentTypeRepository
{
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
public AppointmentTypeRepository(IDbContextFactory<ApplicationDbContext> dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public async Task<IEnumerable<AppointmentType>> GetAppointmentTypesAsync()
{
using (var context = _dbContextFactory.CreateDbContext())
{
return await context.AppointmentTypes.ToListAsync();
}
}
public async Task<AppointmentType> GetAppointmentTypeAsync(int id)
{
using (var context = _dbContextFactory.CreateDbContext())
{
return await context.AppointmentTypes.FindAsync(id);
}
}
public async Task AddAppointmentTypeAsync(AppointmentType appointmentType)
{
using (var context = _dbContextFactory.CreateDbContext())
{
await context.AppointmentTypes.AddAsync(appointmentType);
await context.SaveChangesAsync();
}
}
public async Task UpdateAppointmentTypeAsync(AppointmentType appointmentType)
{
using (var context = _dbContextFactory.CreateDbContext())
{
context.Update(appointmentType);
await context.SaveChangesAsync();
}
}
public async Task DeleteAppointmentTypeAsync(int id)
{
using (var context = _dbContextFactory.CreateDbContext())
{
var appointmentType = await context.AppointmentTypes.FindAsync(id);
context.AppointmentTypes.Remove(appointmentType);
await context.SaveChangesAsync();
}
}
}
}
테스트 프로젝트
using Microsoft.EntityFrameworkCore;
using VisualAcademy.Data;
using VisualAcademy.Models;
using VisualAcademy.Repositores;
namespace VisualAcademy.Tests {
[TestClass]
public class AppointmentTypeRepositoryTests {
private ApplicationDbContext _context;
private AppointmentTypeRepository _repository;
[TestInitialize]
public void Initialize() {
// In-memory database를 사용해서 테스트
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_context = new ApplicationDbContext(options);
_repository = new AppointmentTypeRepository(_context);
}
[TestCleanup]
public void Cleanup() {
// In-memory database를 삭제
_context.Database.EnsureDeleted();
_context.Dispose();
}
[TestMethod]
public void GetAppointmentTypes_ReturnsAllAppointmentTypes() {
// Arrange
_context.AppointmentTypes.Add(new AppointmentType { AppointmentTypeName = "AppointmentType1", IsActive = true });
_context.AppointmentTypes.Add(new AppointmentType { AppointmentTypeName = "AppointmentType2", IsActive = false });
_context.SaveChanges();
// Act
var appointmentTypes = _repository.GetAppointmentTypes();
// Assert
Assert.AreEqual(2, appointmentTypes.Count());
}
[TestMethod]
public void GetAppointmentType_ReturnsAppointmentType() {
// Arrange
_context.AppointmentTypes.Add(new AppointmentType { Id = 1, AppointmentTypeName = "AppointmentType1", IsActive = true });
_context.SaveChanges();
// Act
var appointmentType = _repository.GetAppointmentType(1);
// Assert
Assert.AreEqual("AppointmentType1", appointmentType.AppointmentTypeName);
}
[TestMethod]
public void AddAppointmentType_AddsAppointmentType() {
// Arrange
var appointmentType = new AppointmentType { AppointmentTypeName = "AppointmentType1", IsActive = true };
// Act
_repository.AddAppointmentType(appointmentType);
// Assert
Assert.AreEqual(1, _context.AppointmentTypes.Count());
}
[TestMethod]
public void UpdateAppointmentType_UpdatesAppointmentType() {
//// Arrange
//_context.AppointmentTypes.Add(new AppointmentType { Id = 1, AppointmentTypeName = "AppointmentType1", IsActive = true });
//_context.SaveChanges();
//var appointmentType = new AppointmentType { Id = 1, AppointmentTypeName = "UpdatedAppointmentType1", IsActive = false };
//// Act
//_repository.UpdateAppointmentType(appointmentType);
//// Assert
//Assert.AreEqual("UpdatedAppointmentType1", _context.AppointmentTypes.Find(1).AppointmentTypeName);
//Assert.IsFalse(_context.AppointmentTypes.Find(1).IsActive);
// Arrange
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "UpdateAppointmentType_UpdatesAppointmentType")
.Options;
using (var context = new ApplicationDbContext(options)) {
var repository = new AppointmentTypeRepository(context);
var appointmentType = new AppointmentType { AppointmentTypeName = "Test Appointment Type", IsActive = true };
repository.AddAppointmentType(appointmentType);
context.SaveChanges();
}
using (var context = new ApplicationDbContext(options)) {
var repository = new AppointmentTypeRepository(context);
var appointmentTypeToUpdate = repository.GetAppointmentTypes().First();
appointmentTypeToUpdate.AppointmentTypeName = "Updated Test Appointment Type";
// Detach the appointmentTypeToUpdate object from the DbContext
context.Entry(appointmentTypeToUpdate).State = EntityState.Detached;
// Act
repository.UpdateAppointmentType(appointmentTypeToUpdate);
// Assert
var updatedAppointmentType = repository.GetAppointmentType(appointmentTypeToUpdate.Id);
Assert.AreEqual("Updated Test Appointment Type", updatedAppointmentType.AppointmentTypeName);
}
}
[TestMethod]
public void DeleteAppointmentType_DeletesAppointmentType() {
// Arrange
_context.AppointmentTypes.Add(new AppointmentType { Id = 1, AppointmentTypeName = "AppointmentType1", IsActive = true });
_context.SaveChanges();
// Act
_repository.DeleteAppointmentType(1);
// Assert
Assert.AreEqual(0, _context.AppointmentTypes.Count());
}
}
}
5. Program.cs 파일에서 DI 설정
AppointmentsTypes 테이블에 대한 데이터베이스 작업을 수행하는 리포지토리 클래스에는 ApplicationDbContext 클래스가 필요합니다. 따라서, Program.cs 파일에서 DI 설정을 추가합니다.
using VisualAcademy.Data;
using VisualAcademy.Repositories;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace VisualAcademy
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IAppointmentTypeRepository, AppointmentTypeRepository>();
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=AppointmentTypes}/{action=Index}/{id?}");
});
}
}
}
위 코드에서는 DbContextFactory 및 리포지토리 클래스를 DI 컨테이너에 등록합니다. ConfigureServices 메서드에서 AddScoped 메서드를 사용하여 IAppointmentTypeRepository 인터페이스와 AppointmentTypeRepository 클래스를 연결합니다. 또한 AddDbContextFactory 메서드를 사용하여 ApplicationDbContext 클래스를 등록합니다. 이를 통해 리포지토리에서 데이터베이스 컨텍스트를 생성할 때마다 새로운 인스턴스가 생성되도록 설정할 수 있습니다.
6. ASP.NET Core MVC 컨트롤러와 뷰 페이지 작성
마지막으로, ASP.NET Core MVC를 사용하여 사용자 인터페이스를 제공하는 컨트롤러와 뷰 페이지를 작성합니다.
먼저, AppointmentTypeController를 생성합니다.
using System.Linq;
using VisualAcademy.Models;
using VisualAcademy.Repositories;
using Microsoft.AspNetCore.Mvc;
namespace VisualAcademy.Controllers
{
public class AppointmentTypeController : Controller
{
private readonly IAppointmentTypeRepository _appointmentTypeRepository;
public AppointmentTypeController(IAppointmentTypeRepository appointmentTypeRepository)
{
_appointmentTypeRepository = appointmentTypeRepository;
}
public IActionResult Index()
{
var appointmentTypes = _appointmentTypeRepository.GetAppointmentTypes();
return View(appointmentTypes);
}
public IActionResult Details(int id)
{
var appointmentType = _appointmentTypeRepository.GetAppointmentType(id);
if (appointmentType == null)
{
return NotFound();
}
return View(appointmentType);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([Bind("AppointmentTypeName,IsActive")] AppointmentType appointmentType)
{
if (ModelState.IsValid)
{
_appointmentTypeRepository.AddAppointmentType(appointmentType);
return RedirectToAction(nameof(Index));
}
return View(appointmentType);
}
public IActionResult Edit(int id)
{
var appointmentType = _appointmentTypeRepository.GetAppointmentType(id);
if (appointmentType == null)
{
return NotFound();
}
return View(appointmentType);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, [Bind("Id,AppointmentTypeName,IsActive,DateCreated")] AppointmentType appointmentType)
{
if (id != appointmentType.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
_appointmentTypeRepository.UpdateAppointmentType(appointmentType);
return RedirectToAction(nameof(Index));
}
return View(appointmentType);
}
public IActionResult Delete(int id)
{
var appointmentType = _appointmentTypeRepository.GetAppointmentType(id);
if (appointmentType == null)
{
return NotFound();
}
return View(appointmentType);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(int id)
{
_appointmentTypeRepository.DeleteAppointmentType(id);
return RedirectToAction(nameof(Index));
}
}
}
위 코드에서는 IAppointmentTypeRepository 인터페이스를 사용하여 AppointmentTypes 테이블에서 예약 종류 정보를 조회, 추가, 수정, 삭제하는 메서드를 호출합니다. Index, Details, Create, Edit, Delete 메서드는 각각 예약 종류 정보를 보여주고, 추가, 수정, 삭제하는 기능을 제공합니다.
뷰 페이지
다음으로, 각 메서드에서 사용되는 뷰 페이지를 작성합니다.
Index.cshtml
@model IEnumerable<VisualAcademy.Models.AppointmentType>
<h1>Appointment Types</h1>
<p>
<a asp-action="Create" class="btn btn-primary">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.AppointmentTypeName)
</th>
<th>
@Html.DisplayNameFor(model => model.IsActive)
</th>
<th>
@Html.DisplayNameFor(model => model.DateCreated)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.AppointmentTypeName)
</td>
<td>
@if (item.IsActive)
{
<span class="text-success">Active</span>
}
else
{
<span class="text-danger">Inactive</span>
}
</td>
<td>
@Html.DisplayFor(modelItem => item.DateCreated)
</td>
<td>
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
위 코드에서는 예약 종류 정보를 테이블로 표시하고, 추가, 수정, 삭제할 수 있도록 링크를 제공합니다.
Details.cshtml
@model VisualAcademy.Models.AppointmentType
<h4>Details</h4>
<hr />
<div>
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.AppointmentTypeName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.AppointmentTypeName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.IsActive)
</dt>
<dd class="col-sm-10">
@if (Model.IsActive)
{
<span class="text-success">Active</span>
}
else
{
<span class="text-danger">Inactive</span>
}
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.DateCreated)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.DateCreated)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
위 코드에서는 선택한 예약 종류의 상세 정보를 표시하고, 수정할 수 있는 링크와 목록으로 돌아가는 링크를 제공합니다.
Create.cshtml
@model VisualAcademy.Models.AppointmentType
<h4>Create</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AppointmentTypeName" class="control-label"></label>
<input asp-for="AppointmentTypeName" class="form-control" />
<span asp-validation-for="AppointmentTypeName" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input asp-for="IsActive" /> @Html.DisplayNameFor(model => model.IsActive)
</label>
</div>
<span asp-validation-for="IsActive" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
위 코드에서는 새로운 예약 종류를 추가할 수 있는 폼을 제공하고, 목록으로 돌아가는 링크를 제공합니다.
Edit.cshtml
@model VisualAcademy.Models.AppointmentType
<h4>Edit</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input type="hidden" asp-for="Id" />
<label asp-for="AppointmentTypeName" class="control-label"></label>
<input asp-for="AppointmentTypeName" class="form-control" />
<span asp-validation-for="AppointmentTypeName" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input asp-for="IsActive" /> @Html.DisplayNameFor(model => model.IsActive)
</label>
</div>
<span asp-validation-for="IsActive" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" readonly />
<span asp-validation-for="DateCreated" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
위 코드에서는 선택한 예약 종류를 수정할 수 있는 폼을 제공하고, 목록으로 돌아가는 링크를 제공합니다.
Delete.cshtml
@model VisualAcademy.Models.AppointmentType
<h4>Delete</h4>
<hr />
<div>
<h5>Are you sure you want to delete this?</h5>
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.AppointmentTypeName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.AppointmentTypeName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.IsActive)
</dt>
<dd class="col-sm-10">
@if (Model.IsActive)
{
<span class="text-success">Active</span>
}
else
{
<span class="text-danger">Inactive</span>
}
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.DateCreated)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.DateCreated)
</dd>
</dl>
<form asp-action="Delete" asp-route-id="@Model.Id" method="post">
<div class="form-group">
<input type="submit" value="Delete" class="btn btn-danger" />
<a asp-action="Index" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
위 코드에서는 선택한 예약 종류를 삭제할지 묻는 메시지와 예약 종류 정보를 제공하고, 삭제할지 여부를 묻는 폼을 제공합니다.
결론
위와 같이 ASP.NET Core MVC를 사용하여 AppointmentsTypes 테이블을 관리하는 방법을 알아보았습니다. 이를 바탕으로 데이터베이스 작업을 수행하는 리포지토리 클래스와 DbContext 클래스를 설계하고, DI 설정 및 MVC 컨트롤러와 뷰 페이지를 작성하는 방법을 익혔습니다. 이를 응용하여 다양한 데이터베이스 작업을 수행할 수 있는 ASP.NET Core MVC 앱을 만들어 보세요.
ASP.NET Core Web API Controller 클래스 보충
다음은 ASP.NET Core Web API 컨트롤러 클래스를 만들고 CRUD를 구현하는 방법입니다.
먼저, 컨트롤러 클래스를 생성합니다. 예를 들어, AppointmentTypesController라는 이름으로 생성합니다.
using VisualAcademy.Models;
using VisualAcademy.Repositories;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace VisualAcademy.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class AppointmentTypesController : ControllerBase
{
private readonly IAppointmentTypeRepository _repository;
public AppointmentTypesController(IAppointmentTypeRepository repository)
{
_repository = repository;
}
[HttpGet]
public async Task<IEnumerable<AppointmentType>> Get()
{
return await _repository.GetAppointmentTypesAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<AppointmentType>> GetById(int id)
{
var appointmentType = await _repository.GetAppointmentTypeAsync(id);
if (appointmentType == null)
{
return NotFound();
}
return appointmentType;
}
[HttpPost]
public async Task<ActionResult<AppointmentType>> Create(AppointmentType appointmentType)
{
await _repository.AddAppointmentTypeAsync(appointmentType);
return CreatedAtAction(nameof(GetById), new { id = appointmentType.Id }, appointmentType);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, AppointmentType appointmentType)
{
if (id != appointmentType.Id)
{
return BadRequest();
}
await _repository.UpdateAppointmentTypeAsync(appointmentType);
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
await _repository.DeleteAppointmentTypeAsync(id);
return NoContent();
}
}
}
위 코드에서는 API 컨트롤러 클래스를 정의합니다. ApiController 특성과 Route 특성을 사용하여 경로를 정의합니다. 컨트롤러 클래스 생성자에서는 IAppointmentTypeRepository를 주입합니다. 각 API 엔드포인트에는 HTTP 동사에 따라 메서드가 정의됩니다. GET 요청에 대한 응답으로는 IEnumerable<AppointmentType>
또는 ActionResult<AppointmentType>
이 반환됩니다. POST 요청에 대한 응답으로는 CreatedAtAction 메서드를 사용하여 새로 생성된 리소스의 위치를 반환합니다. PUT 요청에 대한 응답으로는 NoContent 메서드를 사용합니다. DELETE 요청에 대한 응답으로도 NoContent 메서드를 사용합니다.
다음으로, 프로젝트를 실행하고 API를 테스트합니다. 클라이언트는 HTTP GET, POST, PUT, DELETE 메서드를 사용하여 API를 호출할 수 있습니다. 예를 들어, 다음과 같은 GET 요청을 보낼 수 있습니다.
Copy code
GET https://localhost:5001/api/appointmenttypes
이 요청은 모든 예약 종류 정보를 가져옵니다. 다음과 같은 POST 요청을 보낼 수 있습니다.
POST https://localhost:5001/api/appointmenttypes
Content-Type: application/json
{
"appointmentTypeName": "New Appointment Type",
"isActive": true
}
이 요청은 새로운 예약 종류를 추가합니다. 반환된 응답에서는 새로운 예약 종류의 Id와 함께 상세 정보가 포함됩니다. 다음과 같은 PUT 요청을 보낼 수 있습니다.
PUT https://localhost:5001/api/appointmenttypes/1
Content-Type: application/json
{
"id": 1,
"appointmentTypeName": "Updated Appointment Type",
"isActive": false,
"dateCreated": "2023-03-05T00:00:00Z"
}
이 요청은 Id가 1인 예약 종류의 정보를 수정합니다. 반환된 응답에서는 수정된 예약 종류의 Id만 포함됩니다. 마지막으로, 다음과 같은 DELETE 요청을 보낼 수 있습니다.
DELETE https://localhost:5001/api/appointmenttypes/1
이 요청은 Id가 1인 예약 종류를 삭제합니다. 반환된 응답은 없습니다.
이와 같이, ASP.NET Core Web API를 사용하면 HTTP 요청을 처리하고 JSON 형식으로 데이터를 반환할 수 있습니다. 이를 통해 클라이언트 애플리케이션과의 상호작용이 가능해집니다.
Blazor Server에서 CRUD 테스트
AppointmentTypeModalDialog.razor
<div class="modal" tabindex="-1" role="dialog" style="display: @(!Visible ? "none" : null)">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@Title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="() => Close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
@ChildContent
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => Close()">Close</button>
@if (IsSubmitButtonVisible)
{
<button type="button" class="btn btn-primary" @onclick="OnSubmitButtonClick">@SubmitButtonTitle</button>
}
</div>
</div>
</div>
</div>
@code {
[Parameter]
public string Title { get; set; }
[Parameter]
public bool Visible { get; set; }
[Parameter]
public bool IsSubmitButtonVisible { get; set; }
[Parameter]
public string SubmitButtonTitle { get; set; } = "Save";
[Parameter]
public EventCallback OnSubmitButtonClick { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private string SizeClass => Size switch
{
ModalSize.Small => "modal-sm",
ModalSize.Large => "modal-lg",
ModalSize.ExtraLarge => "modal-xl",
_ => null
};
[Parameter]
public ModalSize Size { get; set; } = ModalSize.Default;
public enum ModalSize
{
Default,
Small,
Large,
ExtraLarge
}
public void Close()
{
Visible = false;
}
}
@page "/"
@inject IAppointmentTypeRepository AppointmentTypeRepository
<h1>Appointment Types</h1>
<!-- Add button -->
<button class="btn btn-primary" @onclick="ShowCreateModal">Add</button>
<!-- Appointment types table -->
@if (appointmentTypes != null && appointmentTypes.Any())
{
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Created At</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var appointmentType in appointmentTypes)
{
<tr>
<td>@appointmentType.AppointmentTypeName</td>
<td>@appointmentType.DateCreated</td>
<td>
<!-- Edit button -->
<button class="btn btn-primary btn-sm" @onclick="() => ShowEditModal(appointmentType)">Edit</button>
<!-- Delete button -->
<button class="btn btn-danger btn-sm" @onclick="() => ShowDeleteModal(appointmentType)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<p>No appointment types found.</p>
}
<!-- Modals -->
<ModalDialog @ref="createModal" Title="Add Appointment Type" OnSave="@CreateAppointmentTypeAsync">
<div class="form-group">
<label>Name:</label>
<input class="form-control" @bind="appointmentType.AppointmentTypeName" />
</div>
</ModalDialog>
<ModalDialog @ref="editModal" Title="Edit Appointment Type" OnSave="@UpdateAppointmentTypeAsync">
<div class="form-group">
<label>Name:</label>
<input class="form-control" @bind="appointmentType.AppointmentTypeName" />
</div>
</ModalDialog>
<ModalDialog @ref="deleteModal" Title="Delete Appointment Type" ShowSaveButton="false" OnClose="@ResetAppointmentType">
<p>Are you sure you want to delete this appointment type?</p>
</ModalDialog>
@code {
private List<AppointmentType> appointmentTypes;
private AppointmentType appointmentType = new AppointmentType();
private ModalDialog createModal;
private ModalDialog editModal;
private ModalDialog deleteModal;
protected override async Task OnInitializedAsync()
{
appointmentTypes = await AppointmentTypeRepository.GetAllAppointmentTypesAsync();
}
private void ShowCreateModal()
{
appointmentType = new AppointmentType();
createModal.Visible = true;
}
private void ShowEditModal(AppointmentType appointmentType)
{
this.appointmentType = appointmentType;
editModal.Visible = true;
}
private void ShowDeleteModal(AppointmentType appointmentType)
{
this.appointmentType = appointmentType;
deleteModal.Visible = true;
}
private async Task CreateAppointmentTypeAsync()
{
await AppointmentTypeRepository.AddAppointmentTypeAsync(appointmentType);
appointmentTypes = await AppointmentTypeRepository.GetAllAppointmentTypesAsync();
createModal.Visible = false;
}
private async Task UpdateAppointmentTypeAsync()
{
await AppointmentTypeRepository.UpdateAppointmentTypeAsync(appointmentType);
appointmentTypes = await AppointmentTypeRepository.GetAllAppointmentTypesAsync();
editModal.Visible = false;
}
private async Task DeleteAppointmentTypeAsync()
{
await AppointmentTypeRepository.DeleteAppointmentTypeAsync(appointmentType.Id);
appointmentTypes = await AppointmentTypeRepository.GetAllAppointmentTypesAsync();
deleteModal.Visible = false;
}
private void ResetAppointmentType()
{
appointmentType = new AppointmentType();
}
}