WCF 서비스를 이용한 Silverlight 구현 소스 참고

ü  Sharepoint 서버에 구현하였던 WCF서비스 파일 배포

1.     WCF 서비스 가상 디렉토리 생성하여 해당 디렉토리에 WCF 서비스 전담으로 만들었던 웹사이트 프로젝트의 파일을 아래 그림과 같이 배포합니다.


 -   Bin 폴더에 dll파일 배포 하지 않고, GAC에 등록하여 svc파일 상단에 아래에 음영 부분을 추가시켜 주시면 됩니다.

Code

Service.svc

<%@ Assembly Name="WCFServiceLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f1a7a4b23352b442"%>

<%@ ServiceHost Debug="true" Language="C#" Service="WCFServiceLibrary.ProductService" %>


2.     생성한 가상디렉토리는 익명 액세스 가능하게 체크합니다.

 

3.     Service.svc 파일을 선택한 후, 마우스 오른쪽 버튼을 클릭하여 웹 페이지로 보기항목을 선택하여 웹 페이지를 보면 Sharepoint 웹사이트에서는 VirtualPath에 관련된 아래와 같은 에러 화면을 볼 수 있습니다.

4.     VirtualPath 해결 방안 (관련 링크)

-     클래스 라이브러리 프로젝트를 만듭니다.

-   자동으로 생성된 Class1.cs파일은 삭제하고 System.Web.dll을 참조시킵니다.

  아래 두 클래스를 추가하여 줍니다.

Code

WCFPatchupModule.cs

public class WCFPatchupModule : IHttpModule

{

    private static bool virtualPathProviderInitialized;

    private static readonly object virtualPathProviderInitializedSyncLock = new object();

 

    public void Dispose()

    {

    }

 

    public void Init(HttpApplication context)

    {

        if (!virtualPathProviderInitialized)

        {

            lock (virtualPathProviderInitializedSyncLock)

            {

                if (!virtualPathProviderInitialized)

                {

                    WCFVirtualPathProvider virtualPathProvider = new WCFVirtualPathProvider();

                    HostingEnvironment.RegisterVirtualPathProvider(virtualPathProvider);

                    virtualPathProviderInitialized = true;

                }

            }

        }

    }

}

 

WCFVirtualPathProvider.cs

public class WCFVirtualPathProvider : System.Web.Hosting.VirtualPathProvider

{

    public override string CombineVirtualPaths(string basePath, string relativePath)

    {

        return base.Previous.CombineVirtualPaths(basePath, relativePath);

    }

 

    public override ObjRef CreateObjRef(Type requestedType)

    {

        return base.Previous.CreateObjRef(requestedType);

    }

 

    public override bool FileExists(string virtualPath)

    {

        string str = virtualPath;

        if (virtualPath.StartsWith("~", StringComparison.Ordinal) && virtualPath.EndsWith(".svc", StringComparison.InvariantCultureIgnoreCase))

        {

            str = virtualPath.Remove(0, 1);

        }

        return base.Previous.FileExists(str);

    }

 

    public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)

    {

        return base.Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);

    }

 

    public override string GetCacheKey(string virtualPath)

    {

        return base.Previous.GetCacheKey(virtualPath);

    }

 

    public override VirtualDirectory GetDirectory(string virtualDir)

    {

        return base.Previous.GetDirectory(virtualDir);

    }

 

    public override VirtualFile GetFile(string virtualPath)

    {

        return base.Previous.GetFile(virtualPath);

    }

 

    public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)

    {

        return base.Previous.GetFileHash(virtualPath, virtualPathDependencies);

    }

}

-     어셈블리 서명을 추가해주고, 해당 어셈블리 파일을 GAC에 등록합니다.

-     해당 웹사이트의 Web.Config 파일을 보면, <httpModules>세션에 음영된 부분을 추가합니다

Code

Web.Confing

<httpModules>

  <clear />

  <add name="SPRequest" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

  <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />

  <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />

  <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />

  <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />

  <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />

  <!-- <add name="Session" type="System.Web.SessionState.SessionStateModule"/> -->

  <add name="PublishingHttpModule" type="Microsoft.SharePoint.Publishing.PublishingHttpModule, Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

  <!-- Ajax & SIlverlight -->

  <add name="Session" type="System.Web.SessionState.SessionStateModule" />

  <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

  <add name="WCFVirtualPathProvider" type="WCFVirtualPathProvider.WCFPatchupModule, WCFVirtualPathProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2bea20c5375d001d"/>

</httpModules>

-   Service.svc 파일을 웹 브라우저로 열어 서비스가 정상 작동하지는 확인합니다.

ü  Silverlight 응용 프로그램 프로젝트 수정하기

1.     기존에 만든 Silverlight 프로젝트를 열어 참조된 서비스를 클릭하고 서비스 참조 구성이라는 항목을 선택합니다.

2.    클라이언트 주소가 나오는데 배포한 WCF 서비스 URL로 변경합니다. 업데이트 진행 상태가 나타나며, 문제가 없으면 업데이트가 완료됩니다.

ü  Sharepoint Silverlight WebPart 만들기

1.  Sharepoint 웹파트 추가 하는 종류

-   IIS Silverlight 전담 웹사이트를 만들고 배포한 후 Sharepoint의 기본 웹파트 중에 하나인 페이지 뷰어 웹파트를 이용하여 실버라이트를 보여주는 방법이 있습니다.

-   System.Web.UI.SilverlightControls.Silverlight 컨트롤을 사용하여 웹파트 클래스를 만드는 방법이 있습니다.

2.  우선 여기서는 Silverlight 컨트롤을 이용하여 웹파트를 추가하도록 하겠습니다. 새 프로젝트 추가합니다. (프로젝트 형식은 클래스 라이브러리을 선택합니다.)

-   실버라이트 응용 프로그램 빌드 후, xap 파일을 _layouts의 특정 폴더 배포합니다. )다른 방법으로는 문서 라이브러리 등에 배포 하셔도 상관없습니다.)

-  Silverlight 웹파트 클래스 만들기

Code

DemoSilverlight.cs

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using Microsoft.SharePoint;

 

namespace SilverlightWebParts

{

    public class DemoSilverlight : Microsoft.SharePoint.WebPartPages.WebPart

    {

        protected override void OnLoad(EventArgs e)

        {

            base.OnLoad(e);

 

            ScriptManager sm = ScriptManager.GetCurrent(this.Page);

            if (sm == null)

            {

                sm = new ScriptManager();

                Controls.AddAt(0, sm);

            }

        }

 

        protected override void CreateChildControls()

        {

            base.CreateChildControls();

 

            System.Web.UI.SilverlightControls.Silverlight ctrlSilverlight = new System.Web.UI.SilverlightControls.Silverlight();

            ctrlSilverlight.ID = "SilverlightControl";

            ctrlSilverlight.Source = SPContext.Current.Web.Url + "/_layouts/Silverlight/SilverlightApplication.xap";

            ctrlSilverlight.Width = new Unit(600);

            ctrlSilverlight.Height = new Unit(400);

 

            this.Controls.Add(ctrlSilverlight);

        }

    }

}

Description

ð  로드 이벤트(OnLoad 이벤트)에 보시면 ScriptManager 라는 컨트롤이 추가되어 있는데, 반드시 추가하여야 합니다. 클래스 안에 추가하는 방법 말고, 마스터 페이지 상단에 <asp:ScriptManager ID=”ScriptManager1” ></asp:ScriptManager>라고 추가하셔도 됩니다.

ð Silverlight 컨트롤에 많은 속성들이 있는데 그 중에 Source를 보시면 xap파일(Silverlight 패키지 파일)을 배포한 URL 주소가 들어 있습니다.

3.    해당 dll 파일을 GAC에 등록하시고, Sharepoint 웹파트 갤러리에 추가한 후, 원하는 화면에 웹파트를 추가하시면 아래와 같은 Silverlight 웹파트가 완성됩니다.

1.     WCF 서비스를 구동시키기

-       서비스를 담당할 전담 웹사이트 프로젝트를 만듭니다.


-       저장 위치를 지정하고, WCF 서비스 항목을 선택합니다.


-       App_Code폴더와 그 안에 IService.cs, Service.cs 파일 그리고, Service.svc 파일이 자동 생성된 것을 볼 수 있는데, 서비스 구현은 이미 했기 때문에 Service.svc파일을 제외한 두 개의 파일들을 과감하게 삭제 해버립니다.


-       참조 추가 항목을 선택하고, WCF서비스 라이브러리 프로젝트 항목을 선택합니다. 그리고, Bin 폴더에 dll이 잘 추가된 것을 볼 수 있습니다.

 

-       Service.svc 파일을 수정합니다.

자동 생성되었던 Service.svc

<%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

 

수정된 Service.svc

<%@ ServiceHost Debug="true" Language="C#" Service="WCFServiceLibrary.ProductService" %>

ð  기존 파일에는 Service태그에 클래스명을 지정되어 있고, CodeBehind에 해당 코드 주소를 입력하였으나, 별도의 컴포넌트로 구현하였기에, 컴포넌트 이름에 해당 서비스 클래스명을 지정해 주면 됩니다.

-       Web.config 수정하여야 하는데, 전에 app.config 파일을 WCF 구성 편집 항목을 통해 수정해 본 적이 있습니다. 그 방식과 같이 WCF서비스 구성을 편집합니다. (9번 항목)

-       Web.config 파일을 열어 보면 <system.serviceModel>부분을 제외하고 나머지는 과감하게 삭제해버립니다.

Web.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <system.serviceModel>

    <behaviors>

      <serviceBehaviors>

        <behavior name="ServiceBehavior">

          <serviceMetadata httpGetEnabled="true" />

          <serviceDebug includeExceptionDetailInFaults="false" />

        </behavior>

      </serviceBehaviors>

    </behaviors>

    <services>

      <service behaviorConfiguration="ServiceBehavior" name="WCFServiceLibrary.ProductService">

        <endpoint address="" binding="basicHttpBinding" bindingConfiguration=""

          contract="WCFServiceLibrary.IProductService">

          <identity>

            <dns value="localhost" />

          </identity>

        </endpoint>

        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

      </service>

    </services>

  </system.serviceModel>

</configuration>

-       WCFServiceWebSite 웹사이트의 속성에 가상 경로가 “/”가 아닌 다른 경로가 지정된 경우가 있는데, “/”로 수정합니다. 조금 있다 알게 되겠지만, 정책 파일을 찾는 경로는 웹사이트 최상위 루트이기 때문에 바꿔주어야 합니다.

 

-       Service.svc브라우저에서 보기 항목을 선택하여 잘 구동되는지를 확인합니다.


-       WCF 테스트 브라우저 창을 통해 WCF 서비스 역할이 잘 작동하는지 확인한 적이 있습니다. 웹사이트에서도 잘 되는지 확인하겠습니다. Visual Studio 2008 명령 프롬프트를 선택하고, cmd창이 나타나는 것을 볼 수 있습니다. (cmd창에서 Visual Studio 2008 설치 경로\VC로 들어가시면 됩니다. 기본 설치시 C:\Program Files\Microsoft Visual Studio 9.0\VC 입니다.)


-       좀 전에 svc 파일을 브라우저에서 봤었는데, cmd창에 명령어를 “wcfTestClient + 브라우저에서 봤던 url”를 입력하시면 WCF 테스트 브라우저 창이 나타나며, 전에 했던 방식으로 서비스 역할 테스트를 해보시면 됩니다. (부가 설명은 하지 않겠습니다.)


2.     Silverlight 응용 프로그램 만들기 (위에서 설명한 바가 있어, 자세한 설명은 하지 않겠습니다)

-       솔루션에 새 프로젝트 추가 한 후, 프로젝트명과 저장 경로를 지정하고, Silverlight 응용 프로그램을 선택합니다.

-       Silverlight를 호스팅할 웹 사이트 지정하는 브라우저 창이 나타나며, WCFServiceWebSite WCF 서비스 전담으로 만든 프로젝트이므로, “솔루션에 Silverlight를 호스팅할 새 ASP.NET 웹 프로젝트 추가항목을 선택하여 웹 사이트를 추가합니다.

-       Silverlight 영역에 DataGrid 컨트롤 및 각 이벤트의 버튼을 추가하여 UI을 구상합니다. Microsoft Blend2를 실행시켜 디자인 작업하면 손쉽게 진행할 수 있습니다.

Code

Page.xaml

<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="SilverlightApplication.Page"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >

    <Grid x:Name="LayoutRoot" Background="White">

        <Grid.RowDefinitions>

            <RowDefinition Height="60"></RowDefinition>

            <RowDefinition Height="*"></RowDefinition>

        </Grid.RowDefinitions>

       

<StackPanel Orientation="Horizontal" Grid.Row="0">

            <Button x:Name="btnSelect" Width="60"  Height="35" Margin="5" Content="보기"></Button>

            <Button x:Name="btnInsert" Width="60"  Height="35" Margin="5" Content="추가"></Button>

            <Button x:Name="btnUpdate" Width="60"  Height="35" Margin="5" Content="수정"></Button>

            <Button x:Name="btnDelete" Width="60"  Height="35" Margin="5" Content="삭제"></Button>

        </StackPanel>

<data:DataGrid Grid.Row="1"></data:DataGrid>

    </Grid>

</UserControl>

-       Silverlight 응용 프로젝트에 WCF 서비스를 참조합니다. 좀전에 WCF 서비스 전담 웹사이트에서의 svc 파일 URL을 주소에 입력하고 이동하면, 문제가 없다면 인터페이스 목록이 나타납니다. 그리고 서비스 참조 네임스페이스를 입력하고 추가합니다.


-       UI 구성 및 서비스 참조가 완료한 후, 모든 버튼의 이벤트 함수를 구현하기에 앞 서, “보기 버튼에 대한 이벤트를 구현해보겠습니다.

Code

Page.xaml

<!-- 생략 -->

<StackPanel Orientation="Horizontal" Grid.Row="0">

    <Button x:Name="btnSelect" Width="60"  Height="35" Margin="5" Content="보기" Click="btnSelect_Click"></Button>

    <Button x:Name="btnInsert" Width="60"  Height="35" Margin="5" Content="추가" Click="btnInsert_Click"></Button>

    <Button x:Name="btnUpdate" Width="60"  Height="35" Margin="5" Content="수정" Click="btnUpdate_Click"></Button>

    <Button x:Name="btnDelete" Width="60"  Height="35" Margin="5" Content="삭제" Click="btnDelete_Click"></Button>

</StackPanel>

<data:DataGrid Grid.Row="1" x:Name="dlResult" Background="Beige"></data:DataGrid>

<!-- 생략 -->

 

Page.xaml.cs

public partial class Page : UserControl

{

    ProductDBService.ProductServiceClient serivce = new ProductDBService.ProductServiceClient();

 

    public Page()

    {

        InitializeComponent();

    }

 

    private void btnSelect_Click(object sender, RoutedEventArgs e)

    {

        serivce.SelectProductAsync();

        serivce.SelectProductCompleted += new EventHandler<SelectProductCompletedEventArgs>(serivce_SelectProductCompleted);

    }

 

    void serivce_SelectProductCompleted(object sender, SelectProductCompletedEventArgs e)

    {

        dlResult.ItemsSource = e.Result;

    }

 

    private void btnInsert_Click(object sender, RoutedEventArgs e) {  }

 

    private void btnUpdate_Click(object sender, RoutedEventArgs e) {  }

 

    private void btnDelete_Click(object sender, RoutedEventArgs e) {  }

}

Description

ð  WCF 서비스에 노출된 서비스를 보면, Async 함수와 Completed 이벤트를 가지게 되는 것을 볼 수 있습니다. 간단히 설명드리면, Async 함수로 필요한 인자를 넣고 실행시키면, Completed 이벤트 함수에서 Result(WCF 서비스 호출한 데이터)를 보내주는 방식입니다.


ð  DataList 컨트롤에 서비스 호출하여 Select한 데이터를 바인딩한 후, 잘 동작하는 지 실행(F5)합니다. 하지만, 아래와 같은 오류가 발생하는 것을 볼 수 있으며, IE 개발 툴인 HttpWatch, Fidder, WebDevHelper 등을 이용하여 조회하여 보면, 2개의 파일에서 404. 파일이 없다는 결과를 볼 수 있습니다.


ð  Silverlight는 크로스 도메인에 대해서 제약이 되어 있다고 설명한 바가 있습니다. 크로스 도메인 접근을 위해서는 웹사이트의 최상위 루트 디렉토리에 보안 정책 파일인 clientaccesspolicy.xml 파일과 crossdomain.xml 파일을 배포하여야 합니다.

(참고 : Silverlight 2.0 Tutorial)

-       보안 정책 파일을 WCF 서비스 웹사이트의 최상위 루트에 배포한 후, 다시 실행하면, 잘 작동하는 것을 볼 수 있습니다.


-       마지막으로 남은 버튼의 이벤트도 구현하겠습니다.

Code

public partial class Page : UserControl

{

    ProductDBService.ProductServiceClient serivce = new ProductDBService.ProductServiceClient();

 

    public Page()

    {

        InitializeComponent();

    }

 

    private void btnSelect_Click(object sender, RoutedEventArgs e)

    {

        serivce.SelectProductAsync();

        serivce.SelectProductCompleted += new EventHandler<SelectProductCompletedEventArgs>(serivce_SelectProductCompleted);

    }

 

    void serivce_SelectProductCompleted(object sender, SelectProductCompletedEventArgs e)

    {

        dlResult.ItemsSource = e.Result;

    }

 

    private void btnInsert_Click(object sender, RoutedEventArgs e)

    {

        Products product = new Products();

        product.ProductName = "TEST";

 

        serivce.InsertProductAsync(product);

        serivce.InsertProductCompleted += new EventHandler<InsertProductCompletedEventArgs>(serivce_InsertProductCompleted);

    }

 

    void serivce_InsertProductCompleted(object sender, InsertProductCompletedEventArgs e)

    {

        Refresh(e.Result);

    }

 

    private void btnUpdate_Click(object sender, RoutedEventArgs e)

    {

        serivce.UpdateProductAsync(dlResult.SelectedItem as Products);

        serivce.UpdateProductCompleted += new EventHandler<UpdateProductCompletedEventArgs>(serivce_UpdateProductCompleted);

    }

 

    void serivce_UpdateProductCompleted(object sender, UpdateProductCompletedEventArgs e)

    {

        Refresh(e.Result);

    }

 

    private void btnDelete_Click(object sender, RoutedEventArgs e)

    {

        serivce.DeleteProductAsync((dlResult.SelectedItem as Products).ProductName);

        serivce.DeleteProductCompleted += new EventHandler<DeleteProductCompletedEventArgs>(serivce_DeleteProductCompleted);

    }

 

    void serivce_DeleteProductCompleted(object sender, DeleteProductCompletedEventArgs e)

    {

        Refresh(e.Result);

    }

 

    private void Refresh(bool result)

    {

        if (result)

        {

            MessageBox.Show("작업 완료 되었습니다.");

            serivce.SelectProductAsync();

        }

        else

        {

            MessageBox.Show("작업 실패하였습니다.");

        }

    }

}

Description

ð  각 버튼의 이벤트를 구현하였으며, 추가, 수정, 삭제 시 리플래시하였습니다.

 

 

l  참고 사이트

-       MSDN Library (WCF)

http://msdn.microsoft.com/ko-kr/library/ms735119.aspx

 

1.     WCF 서비스 구현하기

-      이름을 넣고 WCF 서비스 항목을 선택하여 추가하면 App.config, ProductService.cs 그리고 인터페이스 IProductService.cs 파일이 자동 생성되어 있으며, 샘플 코드는 삭제합니다.

-       CRUD 기능 구현을 위해 인터페이스에 메서드 재정의를 선언합니다.

Code

IProductService.cs

[ServiceContract]
public interface IProductService
{
    [OperationContract]
    bool InsertProduct(Products product);

    [OperationContract]
    bool UpdateProduct(Products product);

    [OperationContract]
    bool DeleteProduct(string name);

    [OperationContract]
    List<Products> SelectProduct();
}

ð  인터페이스 IProductService.cs 상단 부분에 보면 [ServiceContract]이라는 속성이 명시되어 있습니다. WCF 응용 프로그램의 서비스 계약을 정의하는 것으로, 반드시 선언하여야 합니다.

ð  메서드 재정의 부분에 [OperationContract]이라는 속성이 명시되어 있으며, WCF 응용 프로그램의 서비스 계약의 일부인 작업을 정의합니다.

-       CRUD(Insert, Update, Delete, Select) 로직을 구현합니다.

Code

ProductService.cs

namespace WCFServiceLibrary

{

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

    public class ProductService : IProductService

    {

        NorthwindDBDataContext dc = new NorthwindDBDataContext();

 

        public bool InsertProduct(Products product)

        {

            try

            {

                dc.Products.InsertOnSubmit(product);

                dc.SubmitChanges();

 

                return true;

            }

            catch { return false; }

        }

 

        public bool UpdateProduct(Products product)

        {

            try

            {

                var target = (from tmp in dc.Products

                              where tmp.ProductID == product.ProductID

                              select tmp).Single();

               

                target.ProductID = product.ProductID;

                target.ProductName = product.ProductName;

                target.SupplierID = product.SupplierID;

                target.CategoryID = product.CategoryID;

                target.QuantityPerUnit = product.QuantityPerUnit;

                target.UnitPrice = product.UnitPrice;

                target.UnitsInStock = product.UnitsInStock;

                target.UnitsOnOrder = product.UnitsOnOrder;

                target.ReorderLevel = product.ReorderLevel;

                target.Discontinued = product.Discontinued;

                dc.SubmitChanges();

 

                return true;

            }

            catch { return false; }

        }

 

        public bool DeleteProduct(string name)

        {

            try

            {

                var target = (from tmp in dc.Products

                              where tmp.ProductName.Equals(name)

                              select tmp).ToList();

 

                dc.Products.DeleteOnSubmit(target[0]);

                dc.SubmitChanges();

 

                return true;

            }

            catch { return false; }

        }

 

        public List<Products> SelectProduct()

        {

            var target = (from tmp in dc.Products

                          select tmp).ToList();

 

            return target;

        }

    }

}

ð  클래스 상단 부분에 [ServiceBehavior]이라는 속성이 명시되어 있으며, 서비스 계약 구현의 내부 실행 동작을 지정합니다. InstanceContextMode는 들어오는 메시지에 포함된 호출을 처리할 수 있는 서비스 인스턴스 수를 지정합니다. WCF 서비스 구현에 중요한 요소이므로 자세히 알아보겠습니다.

모드

설명

Single

개체가 들어오는 모든 호출에 대해 사용되며 호출 후에는 재활용되지 않습니다. 서비스 개체가 없는 경우 새로 만들어집니다. (공유개념)

PerCall

매번 호출하기 전에 만들어지고 매번 호출한 후에 재활용됩니다. 채널에서 세션을 만들지 않으면 이 값은 PerCall인 것처럼 동작합니다. (비동기인 Silverlight에서는 의미없는 모드라고 볼 수 있습니다.)

PerSession

각 세션마다 개체가 만들어집니다. (클라이언트가 더 이상 필요로 하지 않을 때까지 인스턴스가 존재합니다.)

ð  LINQ에 대한 MSDN Library 링크

2.    빌드(F6), App.config 파일 수정 (해당 기능 꼭 알아두기)

-       해당 파일을 열어 수정하는 방법도 있지만, WCF 구성 편집 항목을 이용하여 편집해보겠습니다.

-      App.config 파일에 마우스 오른쪽 버튼을 클릭하면 WCF 구성 편집 항목을 선택합니다.

-       WCF 구성 편집 창이 나타나며, WCF 서비스 라이브러리에 정의된 것과 같은지 확인합니다. 틀릴 경우 해당 항목을 선택하면 <!--[if !vml]--><!--[endif]-->이 나타나는데 클릭합니다. 그리고, bin폴더/Debug폴더 안에 WCFServiceLibrary.dll을 선택한 후, 알맞은 형식 이름을 선택하시면 자동으로 변경됩니다.

-       위 파트에서 설명한 바가 있지만, Silverlight에서는 바인딩을 basicHttpBinding만 지원하므로 반드시 변경해주여야 합니다.

-       그리고 Contact 속성이 맞게 지정되어 있는지 확인합니다. 틀릴 경우 위에서 했던 방식으로 알맞은 형식 이름을 선택합니다.

3.     WCF 서비스 구성이 잘 되어 작동이 이상유무를 확인해 봅니다.

-       실행(F5)를 해보면, 아래와 같이 WCF 테스트 클라이언트 창이 나타나며, 트라이아이콘에 WCF 서비스 호스트 아이콘이 나타나며 구성에는 이상없음을 나타냅니다.

-       SelectProduct()을 더블클릭 한 후, 별다른 인자가 특별 없기 때문에 우측에 호출 버튼을 클릭합니다. Length에 아마 NorthWind 데이터베이스의 Products테이블의 아이템 개수와 동일하게 나나타나는 것을 볼 수 있습니다.

-       이번에는 InsertProduct()을 더블클릭 한 후, 각 변수의 임의의 값을 넣어주고 호출을 하면 성공시, 응답 화면에 return 값에 true가 나타나며, DB 테이블에 아이템 개수가 한 개 늘어난 것을 볼 수 있습니다.

ü  WCF 서비스 라이브러리를 사용하여 WCF Service 만들기

1.     구현 설명

-       Linq to Sql 클래스를 통해 DB 서비스 구현

-       CRUD(Select, Insert, Update, Delete) 기능 구현

-       Silverlight DataGrid 컨트롤 추가하여 데이터 바인딩 및 CRUD 이벤트 구현

2.     WCF 서비스 라이러브리 프로젝트 추가하기

-       웹사이트에 Silverlight 사용 WCF 서비스 항목을 선택하여 서비스 구현하여도 상관없지만, 확장성 및 유지 보수를 고려해 볼 때, WCF 서비스 라이브러리 프로젝트로 서비스 구현하는 것이 관리하기 좋습니다.

-       WCF 서비스 라이브러리 항목을 선택 한 후, 새 프로젝트를 추가합니다.

-       IService1.cs Service1.cs, App.config 세 개의 파일이 생성되어 있는 것을 볼 수 있으며, 모두 삭제 한 후 새로 구현해 보겠습니다.

3.     “Northwind and pubs Sample Databases for SQL Server 2000” 를 다운 받아 샘플 데이터베이스 환경을 만듭니다.

-       링크 페이지에서 SQL2000SampleDb.msi 파일을 다운 받아 설치 합니다. (링크)

-       기본적으로 C:\SQL Server 2000 Sample Databases 아래 데이터 베이스 파일이 저장됩니다.

-       Microsoft SQL Server Studio를 열어, Northwind DB를 연결 추가합니다.

-      연결할 데이터 베이스 추가할 항목을 선택한 후, 추가합니다.

 =>

4.    LINQ to SQL 클래스 추가한 후, DB 연결 하기

-      WCF 서비스 라이브러리 프로젝트에 LINQ to SQL 클래스를 선택하고, 이름 작성한 후, 추가버튼을 클릭합니다.

-      아래와 같은 화면이 나타나며 서버 탐색기를 열어(메뉴에서 보기à서버 탐색기 혹은 Ctrl + W, L) 샘플 데이터 베이스(Northwind) 스키마를 추가합니다.

-      서버 탐색기에서 데이터 연결에 마우스 오른쪽 버튼을 클릭하면 연결 추가가 나오며, Microsoft SQL Server를 선택한 후, 계속 버튼을 클릭합니다.

 =>

-       연결할 서버 이름 및 데이터베이스 이름을 선택하며, 연결 테스트 버튼을 클릭한 후, “테스트 연결에 성공하였습니다.” 라는 성공 메시지가 나타나면 확인을 누릅니다.

5.     Northwind 데이터베이스 스키마가 추가된 것을 볼 수 있으며, 테이블 및 저장 프로시저를 볼 수 있습니다.

-       SQL Server 2005 Management Studio 에서의 기본적인 기능은 제공한다고 합니다.

(데이블 데이터 조회, 쿼리 디자이너, 저장 프로시저 작성)

6.     작업을 진행할 테이블 및 저장 프로시저를 오른쪽 화면으로 드래그하면 아래 화면과 같이 추가됩니다. 여러 개의 테이블을 사용할 경우에는 여러 개를 드래그하여 스키마를 관리하면 됩니다.

7.    속성 창을 열어 DataContext 정의 할 때, Serialization Mode None에서 Unidirectional으로 변경해 줍니다. (객체를 직렬화)

ü  WCF Service의 개념

1.     닷넷의 여러 분산 기술들을 하나로 통합하여 SOA기반의 애플리케이션을 쉽게 개발해 줄 수 있게 지원해주는 기술이며, 기존의 복잡했던 각각의 개발 방식을 일관된 방식으로 프로그래밍할 수 있게 제공해줍니다.

2.     보안이나 트랜잭션과 같은 기능을 구현할 수 있는 높은 생산성을 제공해주고 있고, 기존의 닷넷 분산 기술과 웹 서비스 확장 스펙(WS-*)과의 높은 상호 운용성을 제공해 주고 있습니다. , 닷넷 웹 서비스를 포함하여 좀 더 포괄적인 분산 기술을 지원한 닷넷 기술이라고 보시면 됩니다.

     참고 사이트

MSDN - WCF(Windows Communication Foundation) 서비스 구축

ü  Silverlight에서 WCF Service를 사용하는 경우

1.     DB에 접근할 때

-       Silverlight에서는 .NET의 데이터 처리(DataSet)가 지원하지 않습니다. 웹 서비스 및 WCF 서비스를 통해 데이터를 가져와야 합니다.

2.    다른 플랫폼과 소통을 위한 경우

3.     기타 외부에서 필요한 인자를 읽어오려 할 때

ü  Silverlight를 위한 WCF Service 만들기

1.     WCF의 가장 기본이 되는 개념인 엔드포인트(EndPoint)의 개념

-       WCF서비스와 클라이언트가 엔드포인트를 통해 메시지를 주고 받게 됩니다..

-      엔드포인트는 주소(Address), 바인딩(Binding), 계약(Contact) 3가지 요소로 구성되어 있습니다. , 어디에서(Where) 무엇을(What) 어떻게(How) 통신할 것인지에 대한 개념입니다.

2.   웹사이트에 WCF Service 만들기 방법

-       새로 만들기에 웹사이트를 새로 만듭니다.

-       WCF 서비스를 선택하고, 폴더 경로를 선택하여 프로젝트를 생성합니다.

-       App_Code폴더에 IService.cs Service.cs 두개의 파일이 생성되어 있는 것을 볼 수 있으며, 파일에 작성되어 있는 샘플 코드를 지우고 아래와 같이 작성합니다.

Code

IService.cs

[ServiceContract]

public interface IService

{

    [OperationContract]

    DateTime GetNowDate();

}

 

Service.cs

public class Service : IService

{

    public DateTime GetNowDate()

    {

        return DateTime.Now;

    }

}

-       Web.config을 열어보시면, EndPoint binding 속성을 basicHttpBinding으로 변경합니다.

Code

<!-- 중략 -->

<system.serviceModel>

    <services>

      <service name="Service" behaviorConfiguration="ServiceBehavior">

        <!-- Service Endpoints -->

        <endpoint address="" binding="basicHttpBinding" contract="IService">

          <!--

              배포 시 다음 identity 요소는 배포된 서비스를 실행할 때 사용하는 ID를 반영하도록

              제거되거나 대체되어야 합니다. 해당 요소가 제거되면 WCF에서는 적합한 ID

              자동으로 유추합니다.

          -->

          <identity>

            <dns value="localhost"/>

          </identity>

        </endpoint>

        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

      </service>

    </services>

    <behaviors>

      <serviceBehaviors>

        <behavior name="ServiceBehavior">

          <!-- 메타데이터 정보를 공개하지 않으려면 배포하기 전에 아래의 값을 false로 설정하고 위의 메타데이터 끝점을 제거하십시오. -->

          <serviceMetadata httpGetEnabled="true"/>

          <!-- 디버깅 목적으로 오류에서 예외 정보를 받으려면 아래의 값을 true로 설정하십시오. 예외 정보를 공개하지 않으려면 배포하기 전에 false로 설정하십시오. -->

          <serviceDebug includeExceptionDetailInFaults="false"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Description

ð  Silverlight Web에 종속된 기술이기 떄문에 기본적인 바인딩인 basicHttpBinding만 지원합니다.

-      컴파일 후, Service.svc 파일을 브라우저에서 보기 하면 다음과 같은 화면이 나타나며, 제대로 작동하고 있다는 것을 알 수 있습니다. 이 서비스는 WCF 서비스이자 웹 서비스라 할 수 있습니다.

3.     Silverlight 에서 WCF Service 참조하기

-       Silverlight 응용 프로그램을 선택한 후, 새 프로젝트를 추가합니다.

-       WCF 서비스 프로젝트에서 결과를 확인할 수 있게 기존 웹 사이트에 연결합니다.

-       Silverlight 응용 프로젝트가 생성이 되면, 프로젝트 참조 부분에 오른쪽 마우스를 눌러 서비스 참조를 선택합니다. (이하, ASP.NET에 웹 서비스 참조 하는 방식과 유사합니다.)

-       좀전에 Service.svc URL를 주소에 넣고, 이동을 누르면 해당 서비스 목록이 나타나며, 서비스 네임스페이스명을 지정합니다.

-       서비스 참조된 것을 볼 수 있으며, 서비스 설정 파일(ServcieReferences.ClientConfig)이 생성된 것을 볼 수 있으며, ServcieReferences.ClientConfig 파일을 열어보면 WCF 서비스 설정이 생성된 것을 볼 수 있습니다.

Code

ServcieReferences.ClientConfig

<configuration>

    <system.serviceModel>

        <bindings>

            <basicHttpBinding>

                <binding name="BasicHttpBinding_IService" maxBufferSize="2147483647"

                    maxReceivedMessageSize="2147483647">

                    <security mode="None" />

                </binding>

            </basicHttpBinding>

        </bindings>

        <client>

            <endpoint address="http://localhost:8553/WCFService1/Service.svc"

                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService"

                contract="WCFService.IService" name="BasicHttpBinding_IService" />

        </client>

    </system.serviceModel>

</configuration>

ð  basicHttpBinding전송수준에서의 보안이나 메시지자체의 보안기능은 제공하지만, 트랙젼선이나 세션과 같은 기능은 제공하지 않습니다.

4.    WCF Service 호출하여 Silverlight에 데이터 결과를 보여주기

-       Page.xaml 파일에 데이터를 받기 위한 텍스트블록 컨트롤을 추가하고, 페이지 로드시, 서비스 호출하여 화면에 보여줍니다.

Code

Page.xaml

<TextBlock x:Name="txtDateNow"></TextBlock>

 

Page.xaml.cs

public partial class Page : UserControl

{

    public Page()

    {

        InitializeComponent();

 

        this.Loaded += new RoutedEventHandler(Page_Loaded);

    }

 

    void Page_Loaded(object sender, RoutedEventArgs e)

    {

        //WCF 서비스 객체 생성

        WCFService.ServiceClient service = new WCFBinding.WCFService.ServiceClient();

        //GetDateNow 메서드 결과를 리턴받는 이벤트

        service.GetDateNowCompleted += new EventHandler<WCFBinding.WCFService.GetDateNowCompletedEventArgs>(service_GetDateNowCompleted);

        //비동기 호출

        service.GetDateNowAsync();

    }      

 

    //서비스로부터 호출결과를 반환받을 경우

    void service_GetDateNowCompleted(object sender, WCFBinding.WCFService.GetDateNowCompletedEventArgs e)

    {

        txtDateNow.Text = e.Result.ToShortDateString();

    }

}

Result


목차

소개

몇 주 전부터 실버라이트를 사용하기 시작했데, 정말 놀랍다. WPF의 강력한 표현력과 C#의 능력을 겸한 실버라이트는 정말 강력한 툴이 아닐까 생각해본다. 이 포스팅에서는 데이터베이스로 부터 데이터를 가져와서 실버라이트 응용 프로그램에 적용시키는 것을 다룰것이다. 실버라이트와 ASP.NET의 공통점에 대해서도 다룰것이기 때문에 ASP.NET 개발을 하고 있는 개발자이고 실버라이트에 대해 알고 싶은 개발자또한 이 포스팅을 보는것을 추천한다.



다룰 내용

이 포스팅을 통해 비지니스 어플리케이션을 제작하고자 한다. 비지니스 어플리케이션은 대부분 데이터(데이터베이스에서 가져온)를 다루기 때문에, 실버라이트에서 데이터가 어떻게 표현되는지를 살펴보기로 하자. Northwind 데이터베이스를 예제로 한다. LINQ 클래스를 생성하고, WCF 서비스를 사용하여 데이터를 받아올것이고, 최종적으로 리스트박스와 데이터그리드(데이터템플릿이 딸린)에 뿌려보기로 하자. 우리가 만들 UI는 고객, 주문, 주문 상세가 있는 마스터-디테일 형태가 될것이다. 그리고 이 포스팅에 소개되는 코드는 실버라이트2 RC1 을 기준으로 작성되었다. (역주:번역하면서 한글 실버라이트2 정식으로 테스트 하였습니다.)



예제 프로그램의 실행

예제 프로젝트를 실행하기위해 조금 해야할것이 있다. 기본적으로 솔루션에는 시작 프로젝트가 없기때문에, 솔루션의 설정 윈도우에서 수작업으로 DataApplication.Web 을 시작 프로젝트로 설정해 준다. (역주:이부분은 뭔가 이해가 안되는데, 제공되는 솔루션에는 이미 DataApplication.Web이 시작 프로젝트로 설정되어 있다.)



ASP.NET 프로그래머가 알아둘것

ASP.NET 프로그래머라면, ASP.NET 과는 다르게 실버라이트는 서버가 아닌 클라이언트에서 C#코드가 실행된다는것을 알아두자. 클라이언트라는 말에 자바스크립트라고 생각할 수도 있는데, 아니다. 실버라이트로 코딩함에 있어서 가장 좋은 방법은 자바스크립트를 사용하지 않고 클라이언트 코딩(C#)으로 모두 다 다루는것이다. (적어도 나는 자바스크립트를 잘 다루지 못하니깐)



왜 WCF를 사용하나? 그냥 DB에 접근하면 안되나?

음, 간단하게 말하자면 C# 코드가 클라이언트에서 돌아가고, 클라이언트에서는 데이터베이스에 직접 연결할 수 없기때문이다. 우리의 실버라이트 프로젝트에는 DataSet이나 DataSource 같은 것이 없다는것을 알아두자. 그리고 System.Data 네임스페이스에는 즐겨쓰던 클래스들이 빠져있다. 하지만, WCF 서비스 같은 좋은 것들이 포함되어 있기도 하다. 이 포스팅은 WCF 서비스를 통해 데이터를 가져오는 방법을 보여준다.



시작해보자

비주얼 스튜디오로 실버라이트 어플리케이션을 만들기 위해, 이곳에서 실버라이트 툴을 다운로드받아서 설치하자. 설치후에 Silverlight 응용 프로그램과 Sliverlight 클래스 라이브러리가 추가된것을 볼 수 있다. 여기서 "Silverlight 응용 프로그램" 을 선택하고 이름을 DataApplication 으로하여 시작하자.

사용자 삽입 이미지

확인하면 실버라이트 응용프로그램을 호스팅할 방법을 선택해야되는데, "ASP.NET 웹 응용프로젝트"를 선택하여 ASP.NET 서버측에서 LINQ 클래스와 WCF서비스를 만들 수 있도록 하자.

사용자 삽입 이미지
여기까지 제대로 했다면 두개의 프로젝트가 추가된 솔루션이 열린다. 그중 DataApplication 프로젝트는 클라이언트에서 돌아갈 실버라이트 프로젝트이고, DataApplication.Web 프로젝트는 서버측의 ASP.NET 프로젝트다.



LINQ 클래스 생성

Northwind 데이터베이스는 가벼우면서도 여러가지 상황에 잘 맞기때문에 쓸모가 많다. (여기서 받을 수 있다.) MDF 파일에는 SQLExpress로 접근할 수 있는 프로젝트도 포함되어있다. LINQ 데이터 클래스를 생성하기위해 ASP.NET 응용 프로그램에서 LINQ to SQL 클래스 추가를 선택하도록 하자. (역주:instNwnd.sql 파일만 실행시켜서 DB생성해도 무관하다.)

사용자 삽입 이미지
이제 서버탐색기에서 Northwind 데이터베이스로 새로운 연결을 하나 만들고(SQLExpress든 SQL Server든 상관없다. 자신이 가진걸로 하자.), Customers, Orders, OrderDetails 테이블을 LINQ 디자이너로 드래그하자. 여기서 중요한 점은 실버라이트 응용프로그램으로 데이터를 전송할 수 있도록 하기 위해서는 LINQ로 생성되는 데이터의 직렬화가 가능하도록 해주어야 한다는것이다. 직렬화가 가능하도록 설정하기 위해서 LINQ 디자이너의 빈공간을 클릭한 후 속성창에서 직렬화모드(Serialization Mode)를 Unidirectional 로 설정하도록 하자.

사용자 삽입 이미지



실버라이트 WCF 서비스 생성

이제 데이터를 받을 수 있는 서비스를 추가하자. 실버라이트2 베타2 이전에는 WCF 서비스를 사용하기 위해서는 몇가지 꼼수가 필요했었다. 그러나 다행히도 실버라이트2 베타2 이상의 버전에서는 "Silverlight 사용 WCF 서비스" 라는 템플릿이 제공된다. 자, 새항목추가 - Silverlight를 선택하고 DataService 라는 이름으로 "Sliverlight 사용 WCF 서비스"를 ASP.NET 프로젝트에 추가하자.

사용자 삽입 이미지
생성한 서비스에 세가지 메서드를 작성할건데, 하나는 모든 고객을 리턴하는것이고, 다른 하나는 한 고객의 주문을 리턴하는것이며, 나머지 하나는 특정 주문의 주문상세내역을 리턴해주는것이다. 이 메서드는 반드시 [OperationContract] 특성이 있다는것을 알아두자. (이 특성은 ASMX 서비스의 [WebMethod]와 유사하다.) 아주 간단한 LINQ를 이용해 데이터를 가져오는 것을 구현하였다. 아래 코드를 DataService.svc.cs 파일에 추가하자. (역주:이후 나오는 코드중 Customer, Order, OrderDetail 테이블은 실제 모두 마지막에 's'가 붙어있다. 원저자의 테이블과 약간 틀린듯하다. 번역본에 소개되는 코드에는 모두 's' 를 붙여 놓았다.)

  1. [OperationContract]   
  2. public List<Customers> GetCustomers()   
  3. {   
  4.     DataClasses1DataContext datacontext = new DataClasses1DataContext();   
  5.     return datacontext.Customers.ToList();   
  6. }   
  7.   
  8. [OperationContract]   
  9. public List<Orders> GetOrders(string customerID)   
  10. {   
  11.     DataClasses1DataContext datacontext = new DataClasses1DataContext();   
  12.     return (from order in datacontext.Orders   
  13.             where order.CustomerID == customerID   
  14.             select order).ToList();   
  15. }   
  16.   
  17. [OperationContract]   
  18. public List<Order_Details> GetOrderDetails(int orderID)   
  19. {   
  20.     DataClasses1DataContext datacontext = new DataClasses1DataContext();   
  21.     return (from orderdetail in datacontext.Order_Details   
  22.             where orderdetail.OrderID == orderID   
  23.             select orderdetail).ToList();   
  24. }  



실버라이트 프로젝트에 서비스 참조 추가

서버측의 ASP.NET 프로젝트에서 할일은 이걸로 끝이다. 데이터베이스에서 데이터를 얻기 위해 LINQ 클래스를 만들었고, 이 LINQ 객체를 전달해주기 위해서 WCF 서비스를 만들었다. 이제, 클라이언트측의 실버라이트에서 데이터를 사용할 준비가 끝난 셈이다. 이제 실제 사용을 위해 실버라이트 프로젝트인 DataApplication 프로젝트에 서비스 참조를 추가하자. 참조 - 서비스 참조 추가를 실행하고 팝업창에서 검색버튼을 누르면 방금 우리가 만들었던 WCF 서비스를 검색해준다.

사용자 삽입 이미지



UI 만들기

실버라이트 페이지, 컨트롤은 레이아웃을 담당하는 XAML 파일과 xaml.cs 파일의 코드 비하인드 파일로 구성된다. 간단하게보면 UI를 구성하기위한 *.aspx 파일과 각종 처리로직과 이벤트 핸들러 등이 있는 *.aspx.cs 파일로 구성되는 ASP.NET 과 유사하다. 자, 우리의 응용 프로그램에도 기본적인 레이아웃을 구성해 보자.



DataGrid 컨트롤을 사용하기위해 어셈블리 추가

데이터를 보여주기위해서 DataGrid 컨트롤을 사용하려고 하는데, 실버라이트에는 기본적으로 DataGrid 컨트롤의 참조가 추가되어 있지 않다. 직접 추가해주자. 이 과정은 ASP.NET 에서 사용자정의 컨트롤을 추가하는 방법과 거의 같다. DLL 참조를 추가하고 aspx 페이지에 태그를 등록했던것을 상기해보도록 하자. 실버라이트에서는 참조 - 참조추가를 클릭하고 목록에서 System.Windows.Controls.Data 를 선택하기만 하면 된다. (이 어셈블리에 DataGrid 컨트롤이 포함되어 있다.)

사용자 삽입 이미지
참조를 추가한 다음, XAML 코드에 추가한 참조의 네임스페이스를 등록해 주어야한다. 아래의 네임스페이스 선언을 Page.xaml 파일에 추가하자.

  1. xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  

사용자 삽입 이미지



UI 레이아웃

이제 우리가 할것은, LayoutRoot 라는 이름의 Grid 를 만들고 3개의 행을 가지도록 한다. 첫번째 행은 어플리케이션 제목을 위해 사용한다.(width=50) 세번째 행은 상태바를 위해 사용한다.(width=20) 그리고 가운데 행은 메인데이터를 위해 사용한다. (width=*, 남는 공간을 전부 사용) LayoutRoot 그리드의 첫번째 행에 TextBlock를 추가하고 마지막 행에 txtStatus 라는 이름의 빈 TextBlock을 추가한다. LayoutRoot 그리드의 가운데 행(컨텐트 홀더로 사용하기로 한)에 ContentRoot 라는 이름의 2행 2열로 구성된 또 다른 그리드를 추가한다. 왼쪽 열은 너비 200이고 오른쪽은 나머지 전체를 차지 하도록 하고, 행들은 각각 60%, 40%의 비율을 가지도록 한다. ContentRoot 그리드의 왼쪽 열에는 ListBox 를 아래 행까지 합쳐서 배치하고, 오른쪽 열에는 위, 아래 행 두곳 각각의 DataGrid 를 배치하는데, 위쪽은 주문 데이터를 위한 그리드이고, 아래쪽은 주문상세를 위한 그리드이다. 말로 줄줄 풀어써서 짜증났다면 이 문장들을 XAML화한 코드가 아래에 있다. Page.xaml 파일에 추가하도록 하자.

  1. <UserControl xmlns:basics="clr-namespace:System.Windows.Controls;   
  2.     assembly=System.Windows.Controls"    
  3.     x:Class="DataApplication.Page"  
  4.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
  5.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    
  6.     xmlns:data="clr-namespace:System.Windows.Controls;   
  7.         assembly=System.Windows.Controls.Data"   
  8.     Width="Auto" Height="Auto">   
  9.     <Grid x:Name="LayoutRoot" Background="White">   
  10.         <Grid.RowDefinitions>   
  11.             <RowDefinition Height="55" x:Name="HeaderRow" />   
  12.             <RowDefinition Height="*" x:Name="ContentRow"/>   
  13.             <RowDefinition Height="20" x:Name="FooterRow"/>   
  14.         </Grid.RowDefinitions>   
  15.         <Grid.ColumnDefinitions>   
  16.             <ColumnDefinition Width="*" />   
  17.         </Grid.ColumnDefinitions>   
  18.   
  19.         <!-- Heading -->   
  20.         <TextBlock x:Name="txtHeader" Grid.Row="0"    
  21.                    FontSize="20" Margin="5,5" Foreground="Blue"  
  22.                    Text="My First Data Application in Silverlight">   
  23.         </TextBlock>   
  24.   
  25.         <!-- A textblock in the footer to be used as an Status bar -->   
  26.         <TextBlock x:Name="txtStatus" Grid.Row="2"    
  27.                FontSize="10" Margin="5,0" Foreground="Red">   
  28.         </TextBlock>   
  29.   
  30.         <!-- Content Holder -->   
  31.         <Grid x:Name="ContentGrid" Grid.Row="1" Margin="5">   
  32.             <Grid.RowDefinitions>   
  33.                 <RowDefinition Height=".6*" />   
  34.                 <RowDefinition Height=".4*" />   
  35.             </Grid.RowDefinitions>   
  36.             <Grid.ColumnDefinitions>   
  37.                 <ColumnDefinition Width="200" />   
  38.                 <ColumnDefinition Width="*" />   
  39.             </Grid.ColumnDefinitions>   
  40.   
  41.             <!-- Listbox for displaying customers -->   
  42.             <ListBox x:Name="lstCustomers" Grid.Column="0" Grid.RowSpan="2"  
  43.                      DisplayMemberPath="ContactName"  
  44.                      Loaded="lstCustomers_Loaded"  
  45.                      SelectionChanged="lstCustomers_SelectionChanged">   
  46.             </ListBox>   
  47.   
  48.             <!-- DataGrid for displaying orders of a customer    
  49.                 (with autogenerated columns) -->   
  50.             <data:DataGrid x:Name="dgOrders" Grid.Row="0" Grid.Column="1"    
  51.                            AutoGenerateColumns="True"  
  52.                            SelectionChanged="dgOrders_SelectionChanged">   
  53.             </data:DataGrid>   
  54.   
  55.             <!-- DataGrid for displaying orderdetais for an order -->   
  56.             <data:DataGrid x:Name="dgOrderDetails" Grid.Row="1" Grid.Column="1"    
  57.                            AutoGenerateColumns="True"  
  58.                            AutoGeneratingColumn="dgOrderDetails_AutoGeneratingColumn">   
  59.             </data:DataGrid>   
  60.   
  61.         </Grid>   
  62.   
  63.     </Grid>   
  64. </UserControl>  

WPF 의 레이아웃에 관해서는 코드 프로젝트와 여러 다른 사이트에서 많이 다루고 있기 때문에 굳이 자세히 설명하지는 않겠다.(Sacha Barber의 이런 것 이 있다.) 우리는 ListBox와 DataGrid에 대해 훓어 보는 편이 낮다.



ListBox

lstCustomers 라고 명명한 리스트박스는 데이터베이스의 고객을을 보여주기위해 사용한다. Loaded 이벤트를 등록해서 바인딩을 할것이다. 리스트박스는 object 소스가 바인딩되었을때 각 items 컬렉션의 object.ToString() 을 해당 값으로 보여준다는것을 알아두자. 다른 값을 보여주려면 세가지 방법을사용할수 있다.

  • object.ToString() 메서드를 오버라이드 한다. (이 포스팅에서는 이렇게 구현하지 않는다.)
  • Data Template 를 정의한다. (이 방법이 제일 유연한 접근방법이다. 나중에 DataGrid의 컬럼을 직접 정의하면서 간단하게 살펴보겠지만, 지금은 이 방법을 사용하지 않는다.)
  • ListBox의 DisplayMemberPath 프러퍼티에 보여줄 객체의 프러퍼티를 설정한다. (아주 간단한 방법이다. 지금, 이 방법으로 구현해 보겠다.)

ListBox에 바인딩된 Customer 객체의 ContactName 프러퍼티를 보여줄 것이기 때문에, DisplayMemberPath = "ContactName" 라고 설정한다. 또한, DataGrid에 선택된 고객의 주문을 보여줄수 있도록 하기위해 SelectionChanged 이벤트도 등록해 주도록 하자.



DataGrids

이제는 DataGrid 를 설정하는것이 좀더 수월해졌다. 바인딘된 데이터에 따라서 자동으로 컬럼을 생성하도록 설정하면 되기 때문이다. 그리고, dgOrderDetails 그리드에 AutoGeneratingColumns 이벤트를 등록해주면 컬럼이 자동으로 생성될때 필요치 않는 컬럼을 제거하는 로직을 구현할 수 있는데, 이렇게 하는것이 특정 칼럼을 수월하게 제거하는 일반적인 방법이다. 그리고 자동생성말고 직접 컬럼을 설정하는것은 나중에 다시 알아보기로 하고, 일단은 자동생성으로 간단하게 구현해 보자.



코드 작성

ListBox 처리

ListBox에는 lstCustomers 라고 명명하고, Loaded 이벤트에서 우리가 보여줄 고객에 대한 정보를 보여주자. 실버라이트의 대부분의 서비스 호출은 비동기적으로 일어날 수 있기때문에 데이터가 바인딩되는 시점에 콜백함수를 등록하여 데이터 바인딩 결과에 대한 처리를 해주면 좋다. 여기서는 데이터 로딩과 관련된 메세지를 텍스트로 뿌려줄것이다. (처음에 LayoutGrid의 마지막행에 만들어 두었던 txtStatus 라는 이름의 텍스트 박스를 사용하기로 한다.)

  1. private void lstCustomers_Loaded(object sender, RoutedEventArgs e)   
  2. {   
  3.     DataServiceClient svc = new DataServiceClient();   
  4.     this.txtStatus.Text = "Loading customers...";   
  5.     svc.GetCustomersCompleted += new  
  6.       EventHandler<GetCustomersCompletedEventArgs>(svc_GetCustomersCompleted);   
  7.     svc.GetCustomersAsync();   
  8. }   
  9.   
  10. void svc_GetCustomersCompleted(object sender, GetCustomersCompletedEventArgs e)   
  11. {   
  12.     if (e.Error == null)   
  13.     {   
  14.         this.lstCustomers.ItemsSource = e.Result;   
  15.         this.txtStatus.Text = string.Empty;   
  16.     }   
  17.     else  
  18.     {   
  19.         this.txtStatus.Text =   
  20.             "Error occurred while loading customers from database";   
  21.     }   
  22. }  



고객에 대한 주문 보여주기

이제, ListBox에서 특정 고객을 선택하면 해당 고객에 대한 주문건을 보여주는 코드를 작성해 보자. ListBox의 SelectionChanged 이벤트 핸들러에서 아까 만들어 두었던 WCF 서비스를 호출하여 데이터를 가져오고, 이를 dgOrders 그리드에 바인딩시켜서 보여주자. 이번 구현에서는 간단한 구현을 위해 익명메서드를 사용해보기로 하자.

  1. private void lstCustomers_SelectionChanged(object sender, SelectionChangedEventArgs e)   
  2. {   
  3.     Customers selectedCustomer = this.lstCustomers.SelectedItem as Customers;   
  4.     if (selectedCustomer != null)   
  5.     {   
  6.         DataServiceClient svc = new DataServiceClient();   
  7.         this.txtStatus.Text = "Loading orders...";   
  8.         svc.GetOrdersCompleted +=   
  9.             delegate(object eventSender, GetOrdersCompletedEventArgs eventArgs)   
  10.             {   
  11.                 if (eventArgs.Error == null)   
  12.                 {   
  13.                     this.dgOrders.ItemsSource = eventArgs.Result;   
  14.                     this.txtStatus.Text = string.Empty;   
  15.                 }   
  16.                 else  
  17.                 {   
  18.                     this.txtStatus.Text =   
  19.                         "Error occurred while loading orders from database";   
  20.                 }   
  21.             };   
  22.         svc.GetOrdersAsync(selectedCustomer.CustomerID);   
  23.     }   
  24. }  



주문에 대한 상세주문정보 보여주기

ListBox의 SelectionChanged 이벤트와 유사하게 dgOrders에도 SelectionChanged 이벤트를 등록하고 코드를 작성하자. 이번에는 람다 표현식을 사용해서 구현해 보겠다.

  1. private void dgOrders_SelectionChanged(object sender, EventArgs e)   
  2. {   
  3.     Orders selectedOrder = this.dgOrders.SelectedItem as Orders;   
  4.     if (selectedOrder != null)   
  5.     {   
  6.         DataServiceClient svc = new DataServiceClient();   
  7.         this.txtStatus.Text = "Loading order details...";   
  8.         svc.GetOrderDetailsCompleted +=   
  9.             (eventSender, eventArgs) =>   
  10.             {   
  11.                 if (eventArgs.Error == null)   
  12.                 {   
  13.                     this.dgOrderDetails.ItemsSource = eventArgs.Result;   
  14.                     this.txtStatus.Text = string.Empty;   
  15.                 }   
  16.                 else  
  17.                 {   
  18.                     this.txtStatus.Text =   
  19.                         "Error occurred while loading order details from database";   
  20.                 }   
  21.             };   
  22.         svc.GetOrderDetailsAsync(selectedOrder.OrderID);   
  23.     }   
  24. }  



dgOrderDetails 에서 일부 자동생성된 칼럼 제거하기

아까 작성한 XAML에서 우리는 DataGrid의 AutoGenerateColumns 속성을 true 로 설정함으로써 바인딩된 데이터에 따라서 컬럼을 생성하도록 하였다. 이제 이렇게 생성된 컬럼중에서 dgOrderDetails 그리드의 OrderID 컬럼을 제거해보자. 아까 말했다시피 AutoGeneratingColumns 이벤트 핸들러를 통해서 가능하다.

  1. private void dgOrderDetails_AutoGeneratingColumn(object sender,   
  2.     DataGridAutoGeneratingColumnEventArgs e)   
  3. {   
  4.     if (e.Column.Header.ToString() == "OrderID")   
  5.         e.Column.Visibility = Visibility.Collapsed;   
  6. }  



중간점검.. 프로젝트 실행

어느정도 볼만한 프로그램이 만들어진것 같다. 실행해서 이것저것 마구 건드려보자. 고객, 주문, 주문상세 셋의 선택이 바뀔때 연관되어 바뀌는 관계도 보고, 그리드 자체의 수정기능, 컬럼헤더클릭으로 정렬기능, 헤더의 컬럼 사이즈 변경, 브라우저 크기에 따른 동적 크기 계산 등등.. 원하는건 뭐든지 해보라. 멋지지 않은가? 자, 이제 DataGrid 의 컬럼과 템플릿에 대해 알아보자.

사용자 삽입 이미지



컬럼 정의

실버라이트의 컬럼 설정방법은 ASP.NET의 방법과 유사하다. DataGrid는 세가지 종류의 컬럼을 설정할 수 있다.

  • DataGridTextBoxColumn - 보여주기만 하거나, 수정도 가능한 TextBlock 형태의 데이터. 바인딩된 객체의 어떤 프러퍼티를 보여줄것인지를 DisplayMemberPath 를 통해 설정해줘야 한다.
  • DataGridCheckBoxColumn - 불린, 널러블불린값의 체크박스의 형태. 읽기전용으로 설정하거나, 수정할 수 있도록 설정할 수도 있다.
  • DataGridTemplateColumn - ASP.NET의 TemplateColumn 처럼 강력한 기능을 제공해준다. DataTemplate 을 정의해서 원하는 컨트롤을 해당 칼럼에 설정할 수있다. DataTemplate에 대한 좀 더 자세한 내용은 MSDN 의 이곳에서 확인하도록 하자.

칼럼들에대해 좀더 자세히 알고 싶으면 Scott Morris 의 블로그를 방문해보자. 꽤 좋은 포스팅을 해놓았다.

자, 아제 실제 응용프로그램에 적용해 보도록하자. 간단한 구현을 위해 네개의 칼럼만 정의하겠다. OrderID, EmployeeID 에는 DataGridTextBoxColumn을 사용하고, OrderDate에는 DataGridTemplateColumn 을 사용할건데, CellTemplate 에는 TextBlock을, CellEditingTemplate에는 DatePicker를 사용해보자. 아, 마지막으로 Frieght 칼럼에도 TemplateColumn을 사용하는데 거기에는 데이터 표현을 위해 TextBlock을 넣고 값 수정을 위해 Slider를 넣어서 값의 증감을 조절할 수 있도록 하겠다. 근데, DataTemplate 에는 하나의 컨트롤만 넣을 수 있도록 되어 있으므로, StackPanel을 만들어 이 패널에 TextBlock과 Slider를 넣은뒤에 DataTemplate에 넣기로 하자.

변경된 dgOrders 부분의 코드는 아래와 같다.

  1. <!-- DataGrid for displaying orders of a customer -->   
  2. <data:DataGrid x:Name="dgOrders" Grid.Row="0" Grid.Column="1"    
  3.    AutoGenerateColumns="False"  
  4.    SelectionChanged="dgOrders_SelectionChanged">   
  5.     <data:DataGrid.Columns>   
  6.         <!-- OrderID text column -->   
  7.         <data:DataGridTextColumn Header="Order ID" Binding="{Binding OrderID}" />   
  8.   
  9.         <!-- EmployeeID text column -->   
  10.         <data:DataGridTextColumn Header="Employee ID" Binding="{Binding EmployeeID}" />   
  11.   
  12.         <!-- OrderDate template column -->   
  13.         <data:DataGridTemplateColumn Header="Order Date" Width="150">   
  14.             <data:DataGridTemplateColumn.CellTemplate>   
  15.                 <DataTemplate>   
  16.                     <TextBlock Text="{Binding OrderDate}" />   
  17.                 </DataTemplate>   
  18.             </data:DataGridTemplateColumn.CellTemplate>   
  19.             <data:DataGridTemplateColumn.CellEditingTemplate>   
  20.                 <DataTemplate>   
  21.                     <basics:DatePicker SelectedDate="{Binding OrderDate, Mode=TwoWay}" />   
  22.                 </DataTemplate>   
  23.             </data:DataGridTemplateColumn.CellEditingTemplate>   
  24.         </data:DataGridTemplateColumn>   
  25.   
  26.         <!-- Freight template column -->   
  27.         <data:DataGridTemplateColumn Header="Freight" Width="150">   
  28.             <data:DataGridTemplateColumn.CellTemplate>   
  29.                 <DataTemplate>   
  30.                     <TextBlock Text="{Binding Freight}"></TextBlock>   
  31.                 </DataTemplate>   
  32.             </data:DataGridTemplateColumn.CellTemplate>   
  33.             <data:DataGridTemplateColumn.CellEditingTemplate>   
  34.                 <DataTemplate>   
  35.                     <StackPanel Orientation="Horizontal">   
  36.                         <TextBlock Text="{Binding Freight}" Width="50" />   
  37.                         <Slider Value="{Binding Freight, Mode=TwoWay}" Width="100"  
  38.                                 Minimum="0" Maximum="500" />   
  39.                     </StackPanel>   
  40.                 </DataTemplate>   
  41.             </data:DataGridTemplateColumn.CellEditingTemplate>   
  42.         </data:DataGridTemplateColumn>   
  43.     </data:DataGrid.Columns>   
  44. </data:DataGrid>  

같은 방식으로 DataTemplate 를 ListBox에도 사용할 수 있다. In a similar way, we can use DataTemplates for our ListBox. 우리가 가져올 데이터중에 비트맵이미지를 리턴해주는 PictureProperty가 있다고 한다면, 아래와 같은 방식으로 ListBox에 이미지를 보여줄 수도 있다.

  1. <ListBox x:Name="lstCustomer">   
  2.     <ListBox.ItemTemplate>   
  3.         <DataTemplate>   
  4.             <StackPanel Orientation="Vertical">   
  5.                 <TextBlock Text="{Binding NameProperty}"></TextBlock>   
  6.                 <Image Source="{Binding PictureProperty}"></Image>   
  7.             </StackPanel>   
  8.         </DataTemplate>   
  9.     </ListBox.ItemTemplate>   
  10. </ListBox>  

위 코드는 이렇게도 할수있다라는 샘플에 불과하니깐 우리 응용프로그램에는 적용시키지 말자. (우리가 가져오는 데이터에는 이미지가 없다.)



두번째 실행

다시 프로젝트를 실행해보자. OrderDate 칼럼을 더블클릭해서 DataPicker가 어떻게 나타나는지 보라. Freight 칼럼의 Slider를 조절하여 값을 조절해보고, 여러개의 컨트롤들이 어떻게 결합되어 하나의 칼럼에 나타나는지 유심히 보라. 알아채고 있었는지 모르겠지만, 지금 우리가 이렇게 칼럼을 수정해서 컨트롤을 집어넣은것은 모두 레이아웃코드에서 해결했다. *.cs 파일은 코딩도 하지 않았는데 말이다. 이게 바로 WPF 프레임웍의 장점이 아니겠는가!

사용자 삽입 이미지




DB에 변경데이터 적용하기

음, DB에 데이터를 다시 적용하는것은 이번 포스팅의 범위에서는 좀 벗어난다. 하지만 여러분이 WCF 서비스가 서버와 클라이언트사이에서 통신하는 것을 알고 있으면, 서비스쪽에 데이터를 저장하는 몇가지 함수를 만들고, 그 함수를 호출해서 사용한다면, 실버라이트에서도 쉽게 데이터 작성도 가능하도록 구현할 수 있다고 본다. 그리드에서 바인딩된 값이 변경될때 변경된값은 서버의 값이 아니라, 그리드에 바인딩된 DataContext 라는 것을 기억하자. 실제 DB에 변경을 하고 싶다면, ASP.NET 프로젝트의 WCF 서비스를 이용해서 변경된 데이터를 보내서 DB에 업데이트하도록 해야할 것이다. Ronnie Saurenmann의 동영상이(여기) 두개있는데, 그는 헬퍼 클래스를 이용해서 데이터를 마치 DataSet 처럼 다루고 있다. 변경된 데이터 관리라든지, 원본데이터라든지, 업데이트시에 변경된 데이터만 보내도록 한다든지 하는 방법을 보여주고 있다. 이 동영상을 꼭 한번 보기를 권장한다.



결론

이걸로 끝이다. 나는 이 포스팅을 통해 얼마나 간편하게 데이터관리 응용프로그램을 만들수있는지를 보여주고 싶었다. 배웠던것을 다시 한번 보자면, LINQ로 데이터엑세스 레이어를 만들었고, ASP.NET 프로젝트의 WCF 서비스를 통해 접근할 수 있도록 하였다. 그리고 실버라이트를 통해 클라이언트 측에서 데이터를 가져왔고, 이를 몇몇 컨트롤들을 사용해서 보여주었다.이 포스팅이 여러분들의 실버라이트 응용프로그램 제작의 동기가 될 수 있기를 바란다. 언제나 즐거운 실버라이트 프로그래밍을 하기를..



문서 이력

  • 2008년 08월 12일 - 기사 게시
  • 2008년 10월 19일 - Silverlight 2 RC1에 맞도록 업데이트
  • 2009년 01월 30일 - 한글로 번역되어 포스팅됨




라이센스

이 포스팅에 관련된 모든 파일과 소스코드는 The Code Project Open License (CPOL)를 따릅니다.



저자에 관해

Syed Mehroz AlamSyed Mehroz Alam has done his Bachelors in Computer and Information Systems Engineering in 2007. He loves logical challenges and has written certain logic games and mathematical applications in C/C#/VB in his early age which can be found at: http://www.geocities.com/smehrozalam/myprogs.html
He writes his blog at http://smehrozalam.wordpress.com
Any questions, comments or suggestions are always welcomed at smehrozalam at yahoo dot com.

번역

이 글의 원저자에게 양해를 구하고 번역하였습니다. 전문 번역가도 하니고 학습을 목적으로 번역을 했기때문에 서툰 표현이 많을 수 있습니다. 번역본을 퍼가실때는 출처를 남겨주시기 바랍니다.

+ Recent posts