다음을 통해 공유


ASP.NET Web API 2.2를 사용하여 OData v4의 작업 및 함수

작성자: Mike Wasson

OData에서 작업 및 함수는 엔터티에서 CRUD 작업으로 쉽게 정의되지 않는 서버 쪽 동작을 추가하는 방법입니다. 이 자습서에서는 Web API 2.2를 사용하여 OData v4 엔드포인트에 작업 및 함수를 추가하는 방법을 보여 줍니다. 이 자습서는 ASP.NET Web API 2를 사용하여 OData v4 엔드포인트 만들기 자습서를 기반으로 합니다.

자습서에서 사용되는 소프트웨어 버전

  • Web API 2.2
  • OData v4
  • Visual Studio 2013(여기서 Visual Studio 2017 다운로드)
  • .NET 4.5

자습서 버전

OData 버전 3의 경우 ASP.NET Web API 2의 OData Actions를 참조하세요.

작업과 함수의 차이점은 동작에 부작용이 있을 수 있고 함수는 그렇지 않다는 것입니다. 작업과 함수 모두 데이터를 반환할 수 있습니다. 작업에 대한 몇 가지 용도는 다음과 같습니다.

  • 복잡한 트랜잭션.
  • 여러 엔터티를 한 번에 조작합니다.
  • 엔터티의 특정 속성에 대해서만 업데이트를 허용합니다.
  • 엔터티가 아닌 데이터 보내기

함수는 엔터티 또는 컬렉션에 직접 해당하지 않는 정보를 반환하는 데 유용합니다.

작업(또는 함수)은 단일 엔터티 또는 컬렉션을 대상으로 할 수 있습니다. OData 용어에서 바인딩입니다. 서비스에서 정적 작업으로 호출되는 "언바운드" 작업/함수를 가질 수도 있습니다.

예: 작업 추가

제품을 평가하는 작업을 정의해 보겠습니다.

먼저 등급을 나타내는 모델을 추가 ProductRating 합니다.

namespace ProductService.Models
{
    public class ProductRating
    {
        public int ID { get; set; }
        public int Rating { get; set; }
        public int ProductID { get; set; }
        public virtual Product Product { get; set; }  
    }
}

또한 EF가 데이터베이스에 ProductsContext Ratings 테이블을 만들도록 클래스에 DbSet을 추가합니다.

public class ProductsContext : DbContext
{
    public ProductsContext() 
            : base("name=ProductsContext")
    {
    }

    public DbSet<Product> Products { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    // New code:
    public DbSet<ProductRating> Ratings { get; set; }
}

EDM에 작업 추가

WebApiConfig.cs에서 다음 코드를 추가합니다.

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>()
    .Action("Rate")
    .Parameter<int>("Rating");

EntityTypeConfiguration.Action 메서드는 EDM(엔터티 데이터 모델)에 작업을 추가합니다. Parameter 메서드는 작업에 대해 형식화된 매개 변수를 지정합니다.

이 코드는 EDM의 네임스페이스도 설정합니다. 작업의 URI에는 정규화된 작업 이름이 포함되므로 네임스페이스가 중요합니다.

http://localhost/Products(1)/ProductService.Rate

참고

일반적인 IIS 구성에서 이 URL의 점으로 인해 IIS에서 오류 404를 반환합니다. Web.Config 파일에 다음 섹션을 추가하여 이를 resolve 수 있습니다.

<system.webServer>
    <handlers>
      <clear/>
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="/*" 
          verb="*" type="System.Web.Handlers.TransferRequestHandler" 
          preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

작업에 대한 컨트롤러 메서드 추가

"Rate" 작업을 사용하도록 설정하려면 에 다음 메서드를 추가합니다 ProductsController.

[HttpPost]
public async Task<IHttpActionResult> Rate([FromODataUri] int key, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    int rating = (int)parameters["Rating"];
    db.Ratings.Add(new ProductRating
    {
        ProductID = key,
        Rating = rating
    });

    try
    {
        await db.SaveChangesAsync();
    }
    catch (DbUpdateException e)
    {
        if (!ProductExists(key))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return StatusCode(HttpStatusCode.NoContent);
}

메서드 이름이 작업 이름과 일치하는지 확인합니다. [HttpPost] 특성은 메서드가 HTTP POST 메서드임을 지정합니다.

작업을 호출하기 위해 클라이언트는 다음과 같이 HTTP POST 요청을 보냅니다.

POST http://localhost/Products(1)/ProductService.Rate HTTP/1.1
Content-Type: application/json
Content-Length: 12

{"Rating":5}

"Rate" 작업은 Product 인스턴스에 바인딩되므로 작업의 URI는 엔터티 URI에 추가된 정규화된 작업 이름입니다. (정규화된 작업 이름이 "ProductService.Rate"가 되도록 EDM 네임스페이스를 "ProductService"로 설정했음을 기억하세요.)

요청 본문에는 작업 매개 변수가 JSON 페이로드로 포함됩니다. Web API는 JSON 페이로드를 매개 변수 값의 사전인 ODataActionParameters 개체로 자동으로 변환합니다. 이 사전을 사용하여 컨트롤러 메서드의 매개 변수에 액세스합니다.

클라이언트가 잘못된 형식으로 작업 매개 변수를 보내는 경우 ModelState.IsValid 값은 false입니다. 컨트롤러 메서드에서 이 플래그를 확인하고 IsValid 가 false이면 오류를 반환합니다.

if (!ModelState.IsValid)
{
    return BadRequest();
}

예: 함수 추가

이제 가장 비싼 제품을 반환하는 OData 함수를 추가해 보겠습니다. 이전과 마찬가지로 첫 번째 단계는 EDM에 함수를 추가하는 것입니다. WebApiConfig.cs에서 다음 코드를 추가합니다.

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
builder.EntitySet<Supplier>("Suppliers");

// New code:
builder.Namespace = "ProductService";
builder.EntityType<Product>().Collection
    .Function("MostExpensive")
    .Returns<double>();

이 경우 함수는 개별 Product 인스턴스가 아닌 Products 컬렉션에 바인딩됩니다. 클라이언트는 GET 요청을 전송하여 함수를 호출합니다.

GET http://localhost:38479/Products/ProductService.MostExpensive

이 함수의 컨트롤러 메서드는 다음과 같습니다.

public class ProductsController : ODataController
{
    [HttpGet]
    public IHttpActionResult MostExpensive()
    {
        var product = db.Products.Max(x => x.Price);
        return Ok(product);
    }

    // Other controller methods not shown.
}

메서드 이름이 함수 이름과 일치하는지 확인합니다. [HttpGet] 특성은 메서드가 HTTP GET 메서드임을 지정합니다.

HTTP 응답은 다음과 같습니다.

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 00:44:07 GMT
Content-Length: 85

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Decimal","value":50.00
}

예: 언바운드 함수 추가

이전 예제는 컬렉션에 바인딩된 함수였습니다. 다음 예제에서는 언바운드 함수를 만듭니다. 언바운드 함수는 서비스에서 정적 작업으로 호출됩니다. 이 예제의 함수는 지정된 우편 번호에 대한 판매세를 반환합니다.

WebApiConfig 파일에서 함수를 EDM에 추가합니다.

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");

// New code:
builder.Function("GetSalesTaxRate")
    .Returns<double>()
    .Parameter<int>("PostalCode");

엔터티 형식이나 컬렉션 대신 ODataModelBuilder에서 함수를 직접 호출합니다. 그러면 모델 작성기에서 함수가 바인딩되지 않음을 알 수 있습니다.

함수를 구현하는 컨트롤러 메서드는 다음과 같습니다.

[HttpGet]
[ODataRoute("GetSalesTaxRate(PostalCode={postalCode})")]
public IHttpActionResult GetSalesTaxRate([FromODataUri] int postalCode)
{
    double rate = 5.6;  // Use a fake number for the sample.
    return Ok(rate);
}

이 메서드를 배치하는 Web API 컨트롤러는 중요하지 않습니다. 에 ProductsController배치하거나 별도의 컨트롤러를 정의할 수 있습니다. [ODataRoute] 특성은 함수에 대한 URI 템플릿을 정의합니다.

다음은 클라이언트 요청의 예입니다.

GET http://localhost:38479/GetSalesTaxRate(PostalCode=10) HTTP/1.1

HTTP 응답:

HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
OData-Version: 4.0
Date: Sat, 28 Jun 2014 01:05:32 GMT
Content-Length: 82

{
  "@odata.context":"http://localhost:38479/$metadata#Edm.Double","value":5.6
}