ASP.NET Web API 사용하여 OData v4 엔드포인트 만들기
OData(Open Data Protocol)는 웹용 데이터 액세스 프로토콜입니다. OData는 CRUD 작업(만들기, 읽기, 업데이트 및 삭제)을 통해 데이터 집합을 쿼리하고 조작하는 균일한 방법을 제공합니다.
ASP.NET Web API 프로토콜의 v3 및 v4를 모두 지원합니다. v3 엔드포인트와 나란히 실행되는 v4 엔드포인트를 가질 수도 있습니다.
이 자습서에서는 CRUD 작업을 지원하는 OData v4 엔드포인트를 만드는 방법을 보여 줍니다.
자습서에서 사용되는 소프트웨어 버전
- Web API 5.2
- OData v4
- Visual Studio 2017( 여기에서 Visual Studio 2017 다운로드)
- Entity Framework 6
- .NET 4.7.2
자습서 버전
OData 버전 3의 경우 OData v3 엔드포인트 만들기를 참조하세요.
Visual Studio 프로젝트 만들기
Visual Studio의 파일 메뉴에서 새>프로젝트를 선택합니다.
설치된Visual C#>Web을> 확장하고 ASP.NET 웹 애플리케이션(.NET Framework) 템플릿을 선택합니다. 프로젝트 이름을 "ProductService"로 지정합니다.
확인을 선택합니다.
비어 있는 템플릿을 선택합니다. 에 대한 폴더 및 핵심 참조 추가에서 Web API를 선택합니다. 확인을 선택합니다.
OData 패키지 설치
도구 메뉴에서 NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다. 패키지 관리자 콘솔 창에서 다음을 입력합니다.
Install-Package Microsoft.AspNet.Odata
이 명령은 최신 OData NuGet 패키지를 설치합니다.
모델 클래스 추가
모델은 애플리케이션의 데이터 엔터티를 나타내는 개체입니다.
솔루션 탐색기에서 Models 폴더를 마우스 오른쪽 단추로 클릭합니다. 상황에 맞는 메뉴에서클래스추가>를 선택합니다.
참고
규칙에 따라 모델 클래스는 Models 폴더에 배치되지만 자체 프로젝트에서 이 규칙을 따를 필요는 없습니다.
클래스 Product
이름을 지정합니다. Product.cs 파일에서 상용구 코드를 다음으로 바꿉니다.
namespace ProductService.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
}
속성은 Id
엔터티 키입니다. 클라이언트는 키별로 엔터티를 쿼리할 수 있습니다. 예를 들어 ID가 5인 제품을 가져오려면 URI는 입니다 /Products(5)
. 속성은 Id
백 엔드 데이터베이스의 기본 키이기도 합니다.
Entity Framework 사용
이 자습서에서는 EF(Entity Framework) Code First를 사용하여 백 엔드 데이터베이스를 만듭니다.
참고
Web API OData에는 EF가 필요하지 않습니다. 데이터베이스 엔터티를 모델로 변환할 수 있는 데이터 액세스 계층을 사용합니다.
먼저 EF용 NuGet 패키지를 설치합니다. 도구 메뉴에서 NuGet 패키지 관리자>패키지 관리자 콘솔을 선택합니다. 패키지 관리자 콘솔 창에서 다음을 입력합니다.
Install-Package EntityFramework
Web.config 파일을 열고 configSections 요소 다음에 구성 요소 내에 다음 섹션을 추가합니다.
<configuration>
<configSections>
<!-- ... -->
</configSections>
<!-- Add this: -->
<connectionStrings>
<add name="ProductsContext" connectionString="Data Source=(localdb)\mssqllocaldb;
Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True;
AttachDbFilename=|DataDirectory|ProductsContext.mdf"
providerName="System.Data.SqlClient" />
</connectionStrings>
이 설정은 LocalDB 데이터베이스에 대한 연결 문자열을 추가합니다. 이 데이터베이스는 앱을 로컬로 실행할 때 사용됩니다.
다음으로 Models 폴더에 라는 ProductsContext
클래스를 추가합니다.
using System.Data.Entity;
namespace ProductService.Models
{
public class ProductsContext : DbContext
{
public ProductsContext()
: base("name=ProductsContext")
{
}
public DbSet<Product> Products { get; set; }
}
}
생성자 "name=ProductsContext"
에서 은 연결 문자열의 이름을 지정합니다.
OData 엔드포인트 구성
파일 App_Start/WebApiConfig.cs를 엽니다. 다음 using 문을 추가합니다.
using ProductService.Models;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
그런 다음 Register 메서드에 다음 코드를 추가합니다.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// New code:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
}
}
이 코드는 다음 두 가지 작업을 수행합니다.
- EDM(엔터티 데이터 모델)을 만듭니다.
- 경로를 추가합니다.
EDM은 데이터의 추상 모델입니다. EDM은 서비스 메타데이터 문서를 만드는 데 사용됩니다. ODataConventionModelBuilder 클래스는 기본 명명 규칙을 사용하여 EDM을 만듭니다. 이 방법을 사용하려면 최소 코드가 필요합니다. EDM에 대한 더 많은 제어를 원하는 경우 ODataModelBuilder 클래스를 사용하여 속성, 키 및 탐색 속성을 명시적으로 추가하여 EDM을 만들 수 있습니다.
경로는 HTTP 요청을 엔드포인트로 라우팅하는 방법을 Web API에 알려줍니다. OData v4 경로를 만들려면 MapODataServiceRoute 확장 메서드를 호출합니다.
애플리케이션에 여러 OData 엔드포인트가 있는 경우 각각에 대해 별도의 경로를 만듭니다. 각 경로에 고유한 경로 이름 및 접두사를 지정합니다.
OData 컨트롤러 추가
컨트롤러는 HTTP 요청을 처리하는 클래스입니다. OData 서비스에서 설정된 각 엔터티에 대해 별도의 컨트롤러를 만듭니다. 이 자습서에서는 엔터티에 대해 하나의 컨트롤러를 Product
만듭니다.
솔루션 탐색기 Controllers 폴더를 마우스 오른쪽 단추로 클릭하고클래스추가>를 선택합니다. 클래스 ProductsController
이름을 지정합니다.
참고
OData v3용 이 자습서 버전에서는 컨트롤러 추가 스캐폴딩을 사용합니다. 현재 OData v4에 대한 스캐폴딩이 없습니다.
ProductsController.cs의 상용구 코드를 다음으로 바꿉니다.
using ProductService.Models;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;
namespace ProductService.Controllers
{
public class ProductsController : ODataController
{
ProductsContext db = new ProductsContext();
private bool ProductExists(int key)
{
return db.Products.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
컨트롤러는 클래스를 ProductsContext
사용하여 EF를 사용하여 데이터베이스에 액세스합니다. 컨트롤러는 Dispose 메서드를 재정의하여 ProductsContext를 삭제합니다.
컨트롤러의 시작점입니다. 다음으로 모든 CRUD 작업에 대한 메서드를 추가합니다.
엔터티 집합 쿼리
에 다음 메서드를 추가합니다 ProductsController
.
[EnableQuery]
public IQueryable<Product> Get()
{
return db.Products;
}
[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
IQueryable<Product> result = db.Products.Where(p => p.Id == key);
return SingleResult.Create(result);
}
매개 변수가 없는 버전의 Get
메서드는 전체 Products 컬렉션을 반환합니다. 키 매개 변수가 있는 메서드는 Get
키로 제품을 조회합니다(이 경우 Id
속성).
[EnableQuery] 특성을 사용하면 클라이언트가 $filter, $sort 및 $page 같은 쿼리 옵션을 사용하여 쿼리를 수정할 수 있습니다. 자세한 내용은 OData 쿼리 옵션 지원을 참조하세요.
엔터티 집합에 엔터티 추가
클라이언트가 데이터베이스에 새 제품을 추가할 수 있도록 하려면 다음 메서드를 에 추가합니다 ProductsController
.
public async Task<IHttpActionResult> Post(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
await db.SaveChangesAsync();
return Created(product);
}
엔터티 업데이트
OData는 엔터티 PATCH 및 PUT을 업데이트하기 위한 두 가지 의미 체계를 지원합니다.
- PATCH는 부분 업데이트를 수행합니다. 클라이언트는 업데이트할 속성만 지정합니다.
- PUT은 전체 엔터티를 대체합니다.
PUT의 단점은 클라이언트가 변경되지 않는 값을 포함하여 엔터티의 모든 속성에 대한 값을 보내야 한다는 것입니다. OData 사양은 PATCH가 선호됨을 표시합니다.
어쨌든 PATCH 및 PUT 메서드 모두에 대한 코드는 다음과 같습니다.
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await db.Products.FindAsync(key);
if (entity == null)
{
return NotFound();
}
product.Patch(entity);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != update.Id)
{
return BadRequest();
}
db.Entry(update).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(update);
}
PATCH의 경우 컨트롤러는 Delta<T> 형식을 사용하여 변경 내용을 추적합니다.
엔터티 삭제
클라이언트가 데이터베이스에서 제품을 삭제할 수 있도록 하려면 다음 메서드를 에 추가합니다 ProductsController
.
public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
var product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
db.Products.Remove(product);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}