|
WCF 서비스 생성 |
작 성 일 : 2011-11-09 |
작 성 자 : 이혁 |
.NET 프레임워크 3.0을 위한 비주얼 스튜디오 개발툴은 WCF 서비스를 생성할 수 있는 프로젝트 템플릿을 갖고 있다. 이 템플릿을 이용하면 데이터베이스에 저장된 정보를 조회하는 메소드를 가진 간단한 서비스를 만들어 볼 수 있다. 데이터베이스로는 AdventureWorks샘플 데이터베이스를 사용한다.AdventureWorks 사는 자전거와 악세서리를 만드는 회사이다. 이 데이터베이스는 판매하는 제품의 상세 정보, 영업 정보, 고객 정보, 직원데이터를 담고 있다.
실습 사전 단계
Enterprise Library Configuration프로그램을 실행한다.
처음 화면에서 File메뉴의 New Application을 누른다. 왼쪽 트리 뷰에서 Application Configuration에서 New – Data Access Application Block을 선택한다.
왼쪽 트리 뷰에서 Connecting String 노드를 선택한다. 오른쪽 패널의 Name 프로퍼티를 AdventureWorksConnection으로 바꾼다.
왼쪽 트리 뷰에서 Database 노드를 선택한다. 오른쪽 패널의 Value프로퍼티를 AdventureWorks로 바꾼다.
왼쪽 트리 뷰에서 Server 노드는 기본 설정으로 한다.
왼쪽 트리 뷰에서 integrated Security 노드를 선택한다. 오른쪽 패널에서 Value프로퍼티가 SSPI로 설정돼 있는지 확인한다.
Action메뉴에서 Validate를 클릭한다. 콘솔 하단의 Configuration Errors 패널에 메시지가 없는지 확인한다.
확인이 되면 찾기 쉬운 폴더에 Web.config로 저장을 한다.
WCF프로젝트 만들기
이름은 “ProductsService”라고 한다.
위치는 “시스템 요구사항 기술문서”에서 공유폴더로 설정한 폴더로 하고 확인 버튼을 눌러 프로젝트를 만든다.
위 그림과 같이 기존의 클래스 명(Service1.cs, IService1.cs)을 ProductsService.cs, ProductsService1.cs로 변경한다. ProductsServiceImpl.cs를 추가한다.
DAAB가 필요로 하는 다음 어셈빌리들을 참조 추가한다.
l Microsoft.Practices.EnterpriseLibrary.Data.dll
l Microsoft.Practices.EnterpriseLibrary.Common.dll
l Microsoft.Practices.ObjectBuilder.dll
위의 파일들은 “c:\Program Files\Microsoft Enterprise Library\bin” 폴더에서 찾을 수 있다.
Web.config파일을 추가한다.
계약정의
WCF서비스의 구조는 개발할 때 “계약 우선”이라는 방법을 사용하게 한다. 계약우선으로 개발을 하게 되면 인터페이스나 계약을 먼저 정의한 후 서비스를 구현하고 이 계약을 따르는 서비스를 빌드한다. 계약 우선 개발에서는 서비스 설계에 집중하게 해준다는 것이 중요하다. 필요하다면 개발이 진행되기 전에 특정이나 소프트웨어 종속성이 있는지를 빠르게 살펴볼 수 있다. 클라이언트 애플리케이션은 WCF를 이용해 개발되지 않을 수 도 있으며 윈도우에서 실행되지 않을 수도 있다는 사실을 기억하라.
ProductsService.cs
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text;
namespace Products { [DataContract] public class Product { [DataMember] public string Name;
[DataMember] public string ProductNumber;
[DataMember] public string Color;
[DataMember] public decimal ListPrice; } } |
DataContract 속성을 WCF에 의해 XML 스트림으로 직렬화/역직렬화되는 타입을 정의한 클래스에 추가한다. WCF 오퍼레이션에 넘겨지는 인자들과 오퍼레이션이 반환하는 값의 타입은 모두 WCF에 의해 직렬화돼야 한다. 따라서 데이터 계약 속성은 클래스, 구조체, 열거형에 적용해야만 한다.
타입의 각 맴버는 DataMember 속성임을 표시해야 하며, 속성으로 정의가 되지 않으면 이 방법으로 직렬화가 되지 않는다.
IProductsService.cs
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text;
namespace Products { [ServiceContract] public interface IProductsService { //모든 제품에 대한 제품번호를 구한다. [OperationContract] List<string> ListProducts();
//한 제품에 대한 상제 내역을 구한다. [OperationContract] Product GetProduct(string productNumber);
//제품의 현 재고량을 구한다. [OperationContract] int CurrentStockLevel(string productNumber);
//제품의 현 재고량을 변경한다. bool ChangStockLevel(string productNumber, int newStockLevel, string shelf, int bin); } } |
서비스 계약은 클래스보다 인터페이스를 이용해 정의해야 한다. 이렇게 하면 계약의 정의와 구현을 분리할 수 있다. 인터페이스를 서비스 계약으로 표시하기 위해 ServiceContract속성을 이용한다. 드러내고자 하는 각 메소드는 OperationContract속성으로 표시해야한다.
서비스 구현
ProductsServiceImpl.cs
class ProductsServiceImpl : IProductsService { public List<string> ListProducts() { Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");
string queryString = @"SELECT ProductNumber FROM Production.Product";
IDataReader productsReader = dbAdventureWorks.ExecuteReader(CommandType.Text, queryString);
List<string> productsList = new List<string>(); while (productsReader.Read()) { string productNumber = productsReader.GetString(0); productsList.Add(productNumber); } return productsList;
} ////////////////////////////////////////////////////////////////// public Product GetProduct(string productNumber) { Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");
string queryString = @"SELECT ProductNumber,Name,Color,ListPrice FROM Production.Product WHERE ProductNumber = '" + productNumber + "'";
IDataReader productsReader = dbAdventureWorks.ExecuteReader(CommandType.Text, queryString);
Product product = new Product();
if (productsReader.Read()) { product.ProductNumber = productsReader.GetString(0); product.Name = productsReader.GetString(1);
if (productsReader.IsDBNull(2)) { product.Color = "N/A"; } else { product.Color = productsReader.GetString(2); } product.ListPrice = productsReader.GetDecimal(3);
} return product;
}
////////////////////////////////////////////////////////////////// public int CurrentStockLevel(string productNumber) { Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");
string queryString = @"SELECT SUM(Quantity) FROM Production.ProductInventory WHERE ProductID = (SELECT ProductID FROM Production.Product WHERE ProductNumber ='" + productNumber + "')";
int stockLevel = (int)dbAdventureWorks.ExecuteScalar(CommandType.Text, queryString);
return stockLevel; } ////////////////////////////////////////////////////////////////// public bool ChangStockLevel(string productNumber, int newStockLevel, string shelf, int bin) { Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");
string updateString = @"UPDATE Production.ProductInventory SET Quantity = Quantity +" + newStockLevel + "WHERE Shelf = '" + shelf + "'" + "AND Bin =" + bin + @"ANDProductID = (SELECT ProductID FROM Production.Product WHERE ProductNumber ='" + productNumber + "')";
int numRowChanged = (int)dbAdventureWorks.ExecuteNonQuery(CommandType.Text, updateString);
return (numRowChanged != 0); } } |
ListProducts()메소드
AdventureWorksConnection설정을 참조하기 위한 연결 인자를 얻기 위해 DAAB의 DatabaseFactory.CreateDatabase메소드를 이용한다. ExecuteReader메소드는 데이터베이스에 연결하고 쿼리를 실행하기 위해 이용한다. ListProduct메소드가 완료되면 DAAB는 자동으로 데이터와 연결을 끊는다.
데이터베이스로부토 제품 번호 목록을 반환하는 SQL쿼리를 실행하기 위해 DAAB데이터베이스 객체의 ExecuteReader메소드를 실행한다. 반환되는 값은 DataReader객체이다. 그리고 나서 리스트를 반복하면서 각제품 번호를 읽어서 list<string>에 저장한다.
GetProduct()메소드
데이터베이스와 연결해 특정 제품의 상세 정보를 얻는다.
CurrentStockLevel()메소드
제품은 창고의 여러 보관함에 저장되며 각 보관함들은 이름표가 붙은 선반에 놓여진다. 저장되 있는 모든 선반의 모든 보관함 안에 들어있는 특정 제품의 현재 재고량을 구해준다.
ChangeStockLevel()메소드
특정 선반에 놓여있는 보관함의 특정 삼품의 재고량을 갱신해준다. 이 상품이 실제로 선반 위의 보관함에 있지 않으면 메소드는 사용자의 에러를 나타내는 false를 반환하고 아니면 true를 반환한다.
WCF 서비스의 설정, 배포, 테스트
프로젝트의 속성페이지로 가서 빌드 탭의 출력 경로를 bin으로 해준다.
프로젝트 텍스트 파일 템플릿 선택후 ProductService.svc로 이름을 변경하고 추가한다.
ProductService.svc
<%@ServiceHost Service="Products.ProductsServiceImpl" %> <%@Assembly Name="ProductsService" %> |
ServiceHost의 Service속성을 서비스를 구현하는 클래스와 네임스패이스로 지정한다. Assembly는 네임스페이스와 클래스를 갖는 어셈블리명을 갖는다.
Web.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" /> </configSections> <dataConfiguration defaultDatabase="AdventureWorksConnection" /> <connectionStrings> <add name="AdventureWorksConnection" connectionString="Database=AdventureWorks;Server=(local)\SQLEXPRESS;Integrated Security=SSPI;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.serviceModel> <services> <service name="Products.ProductsServiceImpl" behaviorConfiguration="ProductsBehavior"> <endpoint address="" binding="basicHttpBinding" contract="Products.IProductsService" /> </service> </services> </system.serviceModel> </configuration> |
<serviceModel>부분은 WCF웹서비스의 설정 정보를 갖는다.
<service>부분은 각 서비스 구현에 대한 상세 내역을 갖는다. <service>앨리먼트의 name속성은 서비스를 구현하는 네임스패이스와 클리스를 지정한다.
<endpoint>엘리먼트는 클라이언트 애플리케이션이 서비스와 커뮤니케이션하기 위한 서비스 상세 정보를 제공한다.
address는 서비스를 알리기 위해 서비스가 이용하는 애플리케이션 호스팅 위치를 카르킨다.
binding은 웹서비스에 접근하기 위해 사용하는 전송 방법, 다른 항목 간의 프로토콜과 같은 부분을 정의한다.
contract는 서비스가 구현한 계약을 가르킨다.
여기 까지 완료가 되면 빌드한다.
WCF서비스를 IIS에 배포하기(윈도우 7 전용)
Default Web Site에 마우스 오른쪽 버튼을 눌러 “응용프로그램추가”를 누른다.
별칭에 ProductsService를 입력한다.
실제 경로에 프로젝트 경로로 지정한다.
ex) C:\Users\Administrator\Documents\Microsoft Press\WCF Step By Step\Chapter 1\ProductsService\ProductsService
WCF 서비스 배포테스트
인터넷 익스플로러 실행
주소란에 “http://localhost/ProductsService/ProductsService.svc”를 입력하고 이동한다.
클라이언트 애플리케이션은 서비스가 구현하고 있는 오퍼레이션이 무엇인지 알기위해 서비스 메타데이터가 필요하다 보안산의 이유로 WCF는 서비스가 메타데이터를 발행하는 것을 허용하지 않는다 메타데이터 정보는 서비스 계약이 들어있는 어셈블리에서 직접 얻을수 있다.
아래는 개발자가 서비스에 조회를 요청해서 서비스 메타데이터를 얻을 수 있는 방법이다.
Web.config 부분에 굵은 코드를 추가한다.
<system.serviceModel> <services> <service name="Products.ProductsServiceImpl"> <endpoint address="" binding="basicHttpBinding" contract="Products.IProductsService"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="ProductsBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> |
이 페이지는 서비스 메타데이터를 얻는 방법을 설명하고 있는데, 이 정보는 클라이언트 애플리케이션을 만들 때 이용할 수 있다.