모듈화 ()가 큰 응용 프로그램으로 구성되어있을 수있는 모듈의 이름은 그 기능 단위의 집합으로 구성되어있습니다 시스템 설계하고있다. A module represents a set of related concerns.

모듈 관련 우려의 집합을 나타냅니다.

It can include a collection of related components, such as features, views, or business logic, and pieces of infrastructure, such as services for logging or authenticating users.

그것은, 로직 기능, 플레이, 또는 사업과 같은 관련 구성 요소의 컬렉션을 포함시킬 수 및 로깅 또는 사용자를 인증하는 서비스와 같은 인프라의 조각,.

Modules are independent of one another but can communicate with each other in a loosely coupled fashion.

모듈은 서로 독립적입니다하지만 각각 느슨하게 결합된 방식으로 서로 통신할 수있습니다.

A composite application exhibits modularity.

종합 응용 프로그램을 전시하고 모듈화. For example, consider an online banking program.

예를 들어, 온라인 뱅킹 프로그램을 고려해보십시오.

The user can access a variety of functions, such as transferring money between accounts, paying bills, and updating personal information from a single user interface (UI).

사용자는, 세금 계산서 등 다양한 기능의 계정 간의 자금 이동으로 액세스 할 수 있으며 단일 사용자 인터페이스 (UI)에서 개인 정보를 업데이 트했다.

However, behind the scenes, each of these functions is a discrete module.

그러나이 장면 뒤에, 각각의 이러한 기능의 개별 모듈입니다. These modules communicate with each other and with back-end systems such as database servers.

이러한 모듈은 서로를 다시 데이터베이스 서버와 통신할 - 엔드 시스템 등.

Application services integrate components within the different modules and handle the communication with the user.

응용 프로그램 서비스는 다른 모듈 내의 구성 요소를 통합하고 사용자와의 커뮤니케이 션을 처리합니다. The user sees an integrated view that looks like a single application.

사용자가 볼 수있는 통합 뷰가 하나의 응용 프로그램처럼 보인다.

Figure 1 illustrates a design of a composite application with multiple modules.

그림 1은 여러 개의 모듈을 가진 복합 애플 리케이션의 디자인을 보여줍니다.

 

Dd490825.ModularityDCFig1 (엉 - 우리, MSDN.10)의 PNG.

 

모듈형 디자인을 선택하는 이유는?

The following scenarios describe why you might want to choose a modular design for your application:

다음 시나리오는 이유 때문에 당신이 애플 리케이션을위한 모듈형 디자인을 선택할 수있습니다 설명 :

  • Simplified modules . 간체 모듈을 제공합니다. Properly defined modules have a high internal cohesion and loose coupling between modules.
  • 제대로 정의된 모듈 높은 내부 응집력와 모듈 사이의 느슨한 결합.
  • The coupling between the modules should be through well-defined interfaces.
  • 이 모듈 사이의 결합을 통해 잘 - 정의된 인터페이스를해야한다.
  • Developing and/or deploying modules independently. Modules can be developed, tested, and/or deployed on independent schedules when modules are developed in a loosely coupled way.
  • 개발 / 또는 독립적으로 모듈을 배포합니다. 모듈, 테스트 및 개발 될 수있는 / 또는 독립적인 일정을 때 느슨하게 결합된 모듈 방식으로 개발에 배치했다. By doing this, you can do the following:
  • 이렇게하면 다음 작업을 수행할 수있습니다 :
    • You can independently version modules. 당신이 수있는 독립적 버전의 모듈을 제공합니다.
    • You can develop and test modules in isolation. 당신이 개발할 수와 고립에 모듈을 테스트합니다.
    • You can have modules developed by different teams. 당신은 다른 팀들에 의해 개발된 모듈을 가질 수있습니다.
  • Loading modules from different locations .
  • 서로 다른 위치에서로드 모듈. A Windows Presentation Foundation (WPF) application might retrieve modules from the Web, from the file system and/or from a database.
  • 파일 시스템 및 데이터베이스 / 또는에서 윈도우 프리젠 테이션 파운데이션 (WPF) 응용 프로그램은 웹에서 모듈을 검색할 수도있다.
  • A Silverlight application might load modules from different XAP files.
  • 한 XAP 파일에서 실버 라이트 응용 프로그램을 다른 모듈을 로드할 수있습니다.
  • However, most of the time, the modules come from one location;
  • for example, there is a specific folder that contains the modules or they are in the same XAP file.
  • 그러나, 대부분의 시간,이 모듈을 하나의 위치에서 온; 예를 들면, 그 모듈이 포함되어하거나 동일한 XAP 파일에있는 특정 폴더입니다.
  • Minimizing download time . 다운로드 시간을 최소화했다.
  • When the application is not on the user's local computer, you want to minimize the time required to download the modules. 때 응용 프로그램은 사용자의 로컬 컴퓨터에없는 경우, 당신의 모듈을 다운로드하는 데 필요한 시간을 최소화하기 위해 싶어요.
  • To minimize the download time, only download modules that are required to start-up the application. 다운로드 시간을 최소화하기 위해, 그 시작에 - 응용 프로그램을 다운로드 모듈에만 필요합니다.
  • The rest are loaded and initialized in the background or when they are required. 로드되고 나머지는 배경에서 초기화 또는 그들이 필요합니다.
  • Minimizing application start-up time . 응용 프로그램을 시작할 - 시간을 최소화했다. To get part of the application running as fast as possible, only load and initialize the module(s) that are required to start the application. 모듈 (들)은 응용 프로그램을 시작하는 데 필요한 응용 프로그램을 최대한 빨리, 가능한 한 유일한로드 및 초기화를 실행의 일부를 얻으려면.
  • Loading module s 로드 모듈 s based on rules . 규칙을 기반으로합니다. This allows you to only load modules that are applicable for a specific role. 이것은 특정 역할에 적용되는 전용 모듈을 로드할 수있습니다. An application might retrieve from a service the list of modules to load. 한 응용 프로그램 모듈의 목록을 검색할 수있는 서비스에서 로드할 수있습니다.

Designing a Modular System 모듈형 시스템 설계

When you develop in a modularized fashion, you structure the application into separate modules that can be individually developed, tested, and deployed by different teams. 패션 modularized 개발하면, 당신은 개별적으로 개발할 수있을 테스트를 별도의 모듈을, 그리고 다른 팀들에 의해 배치로 응용 프로그램을 구조했다. Modules can enforce separation of concerns by vertically partitioning the system and keeping a clean separation between the UI and business functionality. 모듈을 수직으로 시스템 파티션과 깔끔한 UI와 비즈니스 기능 사이의 분리를 유지하여 우려의 분리를 적용할 수있습니다. Not having modularity makes it difficult for the team to introduce new features and makes the system difficult to test and to deploy. 모듈화가 어려운 팀이 아니라 새로운 기능에 대한 소개를하고 시스템을 테스트하고 배포하기 어려워합니다.

The following are general guidelines for developing a modular system: 다음은 모듈러 시스템을 개발하기위한 일반적인 가이드라인은 무엇입니까 :

  • Modules should be opaque to the rest of the system and initialized through a well-known interface. 모듈은 시스템의 나머지를 투과해야하며 잘 - 알려진 인터페이스를 통해 초기화.
  • Modules should not directly reference one another or the application that loaded them. 모듈해야 하나의 직접적인 원인은 아니 참조하거나 그들 로드된 다른 응용 프로그램이있습니다.
  • Modules should use loosely coupled techniques, such as shared services, to communicate with the application or with other modules, instead of communicating directly. 모듈을 공유하는 서비스와 같은 응용 프로그램이나 다른 모듈과 함께, 의사 소통의 직접 의사 소통을 대신 느슨하게 결합된 기법을 사용해야합니다.
  • Modules should not be responsible for managing their dependencies. 모듈들의 의존성을 관리하기위한 책임을지지해서는 안된다. These dependencies should be provided externally, for example, through dependency injection. 이러한 종속성이 외부 의존성을 주입, 예를 들면,를 통해 제공되어야한다.
  • Modules should not rely on static methods that can inhibit testability. 모듈 테스트 억제할 수 정적 방법에 의존해서는 안된다.
  • Modules should support being added and removed from the system in a pluggable fashion. 추가되는 모듈을 지원해야하고 플러그 패션에서 시스템에서 제거됩니다.

When you design a modular system, consider the following steps: 모듈러 시스템을 설계하면, 다음 단계를 고려 :

  1. Define the goals for the modular design. 모듈러 설계를위한 목표를 정의합니다. As described earlier, there are several reasons why you might decide to implement a modular design for your application. 앞에서 설명한대로, 거기에 왜 당신의 애플 리케이션을위한 모듈식 설계를 시행하기로 결정 데는 몇 가지 이유가있다.
  2. Decide how you are going to partition your modules and define your module's responsibilities. 어떻게하면 모듈과 모듈의 책임을 정의하는 파티션에 갈 곳을 결정합니다. Each module should have a distinct set of responsibilities. 각 모듈의 책임은 별개의 설정이 있어야합니다. The most common approach is to partition your application so that each module has its own functional area, such as Customers or Orders. 가장 일반적인 접근 때문에 각 모듈은 고객이나 주문과 같은 기능 영역 자체가 귀하의 응용 프로그램 파티션을하는 것입니다. In this case, each module will consist of its own presentation layer, a business or domain layer, and a resource access layer. 이 경우에는 각 모듈은 자신의 프레 젠 테이션 레이어, 비즈니스 또는 도메인 계층 및 리소스에 액세스 레이어의 구성된다. Figure 2 illustrates an example where a module that addresses a specific functional area contains all these logical layers. 그림 2는 어디 모듈 주소가 특정 기능 영역에서 예를 설명이 논리적 레이어를 포함하고있습니다.

    Dd490825.ModularityFig1 (엉 - 우리, MSDN.10)의 PNG.

    Figure 2 그림 2
    Layered approach to module design 중층적인 접근 방식 설계 모듈

    There are other ways to approach the design of modules. 거기에 다른 모듈의 디자인에 접근하는 방법이있습니다. For example, if you want to make it easier to replace the UI of your application, it might make sense to place the presentation layer in one or more easy-to-replace modules. 만약 여러분의 애플 리케이션의 UI를 쉽게 대체할 수 있도록하고 싶지 예를 들어, 하나 이상의 프레 젠 테이션 레이어는 쉬운 곳으로 감각을 만들어 야지 - - 모듈을 대체할 수있습니다. If you want your application to support different resource-access strategies, another example might be to put your resource access layer in a separate module. 만약 당신이 다른 리소스 - 액세스 전략을 지원하는 응용 프로그램을 원하는 또 다른 예제는 별도의 모듈에서 귀하의 리소스에 액세스 레이어를 잠재울 수있습니다. By replacing the resource access layer module, you can have the application access a local database or a remote Web service. 리소스에 대한 액세스 레이어 모듈을 교체함으로써, 애플 리케이션 액세스, 로컬 데이터베이스 또는 원격 웹 서비스를 할 수있습니다.

  3. Define the communication patterns between the modules. 이 모듈 사이의 통신 패턴을 정의합니다.

    Even though modules should have low coupling between each other, it is common for modules to communicate with each other. 비록 서로 모듈, 모듈에 대한 서로 간의 의사 소통을하는 것이 일반적입니다 낮은 커플링이 있어야합니다. There are several loosely coupled communication patterns, each with their own strengths. 몇 가지를 느슨하게 결합된 커뮤니케이션 패턴, 각자의 강점이있다. Typically, combinations of these patterns are used to create the resulting solution. 일반적으로 이러한 패턴의 조합 결과가 솔루션을 만드는 데 사용됩니다. The following are some of these patterns: 다음의 몇 가지 패턴의 위치 :

    • Loosely coupled events . 느슨하게 결합된 이벤트. A module can broadcast that a certain event has occurred. 모듈을 특정 이벤트가 발생했습니다 방송하실 수있습니다. Other modules can subscribe to those events so they will be notified when the event occurs. 다른 모듈이 그 행사를 구독할 수 있도록 그들이 통보됩니다 이벤트가 발생할 때. Loosely coupled events are a lightweight manner of setting up communication between two modules; therefore, they are easily implemented. 느슨하게 결합된 이벤트를 두 모듈 간의 통신을 설정하는 방식의 경량; 그러므로, 그들은 쉽게 구현됩니다. However, a design that relies too heavily on events can become hard to maintain, especially if many events have to be orchestrated together to fulfill a single task. 그러나 디자인이 너무 무겁게 이벤트에 의존하고 유지하기 위해, 특히 다양한 이벤트가 함께하는 경우는 하나의 임무를 완수하기 위해 조율된 게 쉽지가 될 수있다. In that case, it might be better to consider a shared service. 그런 경우엔, 공유 서비스를 고려하는 것이 좋을 것입니다.
    • Shared services . 공유 서비스를 제공합니다. A shared service is a class that can be accessed through a common interface. 공유 서비스는 일반적인 인터페이스를 통해 액세스할 수있는 클래스입니다. Typically, shared services are found in a shared assembly and provide system-wide services, such as authentication, logging, or configuration. 일반적으로 공유하는 서비스는 공유 어셈블리에서 발견하는 시스템을 제공하고 인증과 같은 - 넓은 서비스, 로깅, 또는 구성.
    • Shared resources . 공유 자원. If you do not want modules to directly communicate with each other, you can also have them communicate indirectly through a shared resource, such as a database or a set of Web services. 만약 당신이 직접 서로 통신 모듈을 원하지 않으면, 당신도 그들을 간접적으로 공유 리소스를 통해, 데이터베이스 또는 웹 서비스의 설정 등과 같은 의사 소통을 할 수있습니다.

Team Development Using Modules 팀 개발을 사용하여 모듈

Modules have explicit boundaries, typically by subsystem or feature. 모듈, 서브 시스템 또는 기능은 일반적으로 명시적인 한계가있다. Having these boundaries makes it easier for separate teams to develop modules. 이러한 경계가 쉽게 모듈을 개발할 수 있도록 별도의 팀이있다. On large applications, teams may be organized by cross-cutting capabilities in addition to being organized by a specific subsystem or feature. 대형 응용 프로그램에서 크로스로 팀을 구성할 수있습니다 - 절삭 기능 이외에 특정 서브 시스템 또는 기능에 의해 조직되고있다. For example, there may be a team assigned to shared components of the application, such as the shell or the common infrastructure module. 예를 들어, 한 팀을 거기 응용 프로그램의 공유 구성 요소, 셸 또는 공통 인프라 모듈 등 할당될 수있습니다.

The following are the different teams developing the Stock Trader Reference Implementation, as illustrated in Figure 3: 다음은 다른 팀들은 주식 트레 이더 참조 구현을 개발,으로 그림 3에서 보여주고있습니다 :

  • Infrastructure team . 인프라 팀. This team develops the core cross-cutting services used in the application and cross-module types and interfaces. 이 팀의 핵심 크로스 - 서비스는 응용 프로그램과 교차에 사용되는 - 모듈 유형과 인터페이스를 절단을 개발하고있습니다.
  • Pos i tions team . 포지션 내가 팀을 참조하십시오. This team maintains the Positions and Buy/Sell functionality. 이 팀의 순위를 유지 관리 및 주문 / 기능을 매도했다.
  • News team . 뉴스 팀. This team handles aggregating and displaying news. 이 팀은 합칩니다 핸들 및 뉴스를 표시합니다.
  • UI team . UI를 팀. This team manages the shell and contains a set of graphic designers who set the overall appearance of the application. 이 팀은 껍질을 관리하고 응용 프로그램의 전체적인 모양을 설정 그래픽 디자이너의 세트가 포함되어있습니다. They work across each of the development teams. 그들은 각 팀들의 전체 개발 작업.
  • Operation s team . 작전 팀. This team is responsible for managing and deploying modules to staging and production. 이 팀 관리 및 준비 및 생산에 모듈을 배포하기위한 책임이있다. They also manage the master module configuration files. 그들은 또한 모듈 구성 파일은 마스터를 관리할 수있습니다.

Figure 3 illustrates an example of a composite application's modules and associated teams. 그림 3은 응용 프로그램의 모듈과 관련된 종합 팀의 예를 보여줍니다.

Dd490825.ModuleDesignFig3 (엉 - 우리, MSDN.10)의 PNG.

RSS 기사받아오기

WEF 웹서비스를 사용하기 위한 기본설정

      <service behaviorConfiguration="SLApp.Web.WCFServiceBehavior"
        name="SLApp.Web.WCFService">
        <!--
        <endpoint address="" binding="wsHttpBinding" contract="SLApp.Web.IWCFService">
          -->
        <endpoint address="" binding="basicHttpBinding" contract="SLApp.Web.IWCFService">

          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>
</configuration>

WCF 웹서비스

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Activation;
using System.Xml.Linq;
namespace SLApp.Web
{
    // NOTE: If you change the class name "WCFService" here, you must also update the reference to "WCFService" in Web.config.
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class WCFService : IWCFService
    {
        public void DoWork()
        {
        }

        public List<WCFDataType.RSS2_0> GetNewsRSS() {

            List<WCFDataType.RSS2_0> rss = new List<SLApp.Web.WCFDataType.RSS2_0>();
            XDocument rssFeed = XDocument.Load("http://news.kbs.co.kr/rss/kbsrss_news.php?site=news");
            var posts = from item in rssFeed.Descendants("item")
                        select new
                        {
                            Title = item.Element("title").Value,
                            Published = DateTime.Parse(item.Element("pubDate").Value),
                            Description = item.Element("description").Value,
                            Link = item.Element("link").Value,
                            Author = item.Element("author").Value
                        };

            foreach (var s in posts)
            {
                rss.Add(new SLApp.Web.WCFDataType.RSS2_0(s.Title, s.Author, s.Published, s.Description, s.Link));
            }

            return rss;
        }

------------------------------------------------------------------------------------------

//또다른 방법

      public  void Stock()
        {
            WebClient client = new WebClient();
            string s = client.DownloadString(new Uri("주식정보"));
            s = s.Trim();
            XDocument doc = XDocument.Parse(s);
            IEnumerable<XElement> current = doc.Element("StockInfo")
                .Element("channel")
                .Element("Stock_Sise")
                .Elements();
            Response.Write(current.ElementAt(0).Value);
        }

실제 받아오는 정보

<?xml version="1.0" encoding="euc-kr" ?>

- <StockInfo>

- <channel>

- <Stock_Sise>

<StockName>삼성전자</StockName>

<CurrentPoint>17,2500</CurrentPoint>

<UpDownPoint>50</UpDownPoint>

<UpDownFlag>2</UpDownFlag>

<UpDownSign></UpDownSign>

</Stock_Sise>

</channel>

</StockInfo>

------------------------------------------------------------------------------------------

    }
}

실버라이트에서 공유할 데이터

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Activation;
using System.Xml.Linq;

namespace SLApp.Web.WCFDataType
{
    [DataContract]
    public class RSS2_0
    {

        public RSS2_0(string p_Title, string p_Author, DateTime p_PubDate, string p_Description, string p_Link)
        {
            this.Title = p_Title;
            this.Author = p_Author;
            this.PubDate =p_PubDate;
            this.Description = p_Description;
            this.Link = p_Link;
        }
        private string _Title;
        private string _Description;
        private string _Author;
        private DateTime _PubDate;
        private string _Link;

        [DataMember]
        public string Title
        {
            get { return _Title; }
            set { _Title = value; }
        }

        [DataMember]
        public string Description
        {
            get { return _Description; }
            set { _Description = value; }
        }

        [DataMember]
        public string Author
        {
            get { return _Author; }
            set { _Author = value; }
        }

        [DataMember]
        public DateTime PubDate
        {
            get { return _PubDate; }
            set { _PubDate = value; }
        }

        [DataMember]
        public string Link
        {
            get { return _Link; }
            set { _Link = value; }
        }

    }
}

실버라이트

using System.Xml.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Collections.ObjectModel;
namespace PCGEP.SLApp.Contents
{
    public partial class NoticeNewsRSS : UserControl
    {
        public NoticeNewsRSS()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(NoticeNewsRSS_Loaded);
        }

        void NoticeNewsRSS_Loaded(object sender, RoutedEventArgs e)
        {

            Binding defaultBinding = new System.ServiceModel.BasicHttpBinding();
            EndpointAddress defaultAddress = new System.ServiceModel.EndpointAddress(@"http://localhost:9917/WCFService.svc");

            WCFServiceReference.WCFServiceClient  wc = new PCGEP.SLApp.WCFServiceReference.WCFServiceClient(defaultBinding, defaultAddress);
            wc.GetNewsRSSCompleted += new EventHandler<PCGEP.SLApp.WCFServiceReference.GetNewsRSSCompletedEventArgs>(wc_GetNewsRSSCompleted);
            wc.GetNewsRSSAsync();

        }

        void wc_GetNewsRSSCompleted(object sender, PCGEP.SLApp.WCFServiceReference.GetNewsRSSCompletedEventArgs e)
        {
            this.rssListBox.ItemsSource = e.Result;
        }
    }
}

실버라이트 초간단 데이터 바인딩

1.ADO.NET Entity Data Model 을 만듭니다 (서버측)

image

2. 그런다음 데이터베이스에 접근할수 있는 ado.net Data Service 를 만들어줍니다. (서버측)

image

만들어진 Ado.Net Data Service

image

3.실버라이트 프로젝트에서 웹서비스를 참조합니다.

image

4.데이터그리드에 데이터를 바인딩 합니다.

아래는 간단한 소스

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Data.Services.Client;
using System.Collections.ObjectModel;
using System.ComponentModel;
using BM.ServiceReference1;
namespace BM.CodeTest
{
    public partial class Delete : UserControl
    {
        ServiceReference1.TEST_BOOKEntities svcContext;
        ObservableCollection<ServiceReference1.BOOK> ObsvBookCollrection;
        BooksDS bookDs;


        public Delete()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Delete_Loaded);
        }

        void Delete_Loaded(object sender, RoutedEventArgs e)
        {

            svcContext = new TEST_BOOKEntities(new Uri("BookWebDataService.svc", UriKind.Relative));
            ObsvBookCollrection = new ObservableCollection<BOOK>();
            bookDs = new BooksDS();
            DataServiceQuery<BOOK> query = (DataServiceQuery<BOOK>)
                (from c in svcContext.BOOK select c);
            query.BeginExecute(GetDataCallback, query);

        }

 

        #region 셀렉트

        void GetDataCallback(IAsyncResult result)
        {

            try
            {
                DataServiceQuery<BOOK> queryResult =
                               (DataServiceQuery<BOOK>)result.AsyncState;

                IEnumerable<BOOK> results =
                              queryResult.EndExecute(result);

                foreach (var item in results)
                {
                    bookDs.Add(item);
                }
                    grdList.ItemsSource = bookDs;
   
              
                

                MessageBox.Show("도서목록가져오기 완료");

            }
            catch (DataServiceRequestException ex)
            {

            }


        }
        #endregion


    }
}


5.테스트 끝~

// Custom class implements the IValueConverter interface.
public class DateToStringConverter : IValueConverter
{

    #region IValueConverter Members

    // Define the Convert method to change a DateTime object to a month string.
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // value is the data from the source object.
        DateTime thisdate = (DateTime)value;
        int monthnum = thisdate.Month;
        string month;
        switch (monthnum)
        {
            case 1:
                month = "January";
                break;
            case 2:
                month = "February";
                break;
            default:
                month = "Month not found";
                break;
        }
        // Return the value to pass to the target.
        return month;

    }

    // ConvertBack is not implemented for a OneWay binding.
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

 

 

사용법

  1. <Grid.Resources>  
  2.  
  3.     <DataTemplate x:Name="dataTemplate">  
  4.         <Grid>  
  5.             <Grid.ColumnDefinitions>  
  6.                 <ColumnDefinition />  
  7.                 <ColumnDefinition />  
  8.             </Grid.ColumnDefinitions>  
  9.             <TextBlock Grid.Column="0" Text="{Binding Month, Converter={StaticResource Converter1}}"/>  
  10.             <TextBlock Grid.Column="1" Text="{Binding Total}"/>      
  11.        </Grid>  
  12.     </DataTemplate>  
  13.  
  14. </Grid.Resources>  
  15.  
  16. <ItemsControl x:Name="IC1" ItemsSource="{Binding}" ItemTemplate="{StaticResource dataTemplate}"/> 

 

INotifyPropertyChanged 인터페이스는 속성 값이 변경되었음을 클라이언트(대개 바인딩하는 클라이언트)에 알리는 데 사용됩니다.

예를 들어, FirstName이라는 속성이 있는 Person 개체의 경우 제네릭 속성 변경 알림을 제공하기 위해 Person 형식은 INotifyPropertyChanged 인터페이스를 구현하고 FirstName이 변경될 때 PropertyChanged 이벤트를 발생시킵니다.

다음 코드 예제에서는 INotifyPropertyChanged 인터페이스를 구현하는 방법을 보여 줍니다.

//Add using statements
using System.ComponentModel;
using System.Windows.Data;


...


// Create a class that implements INotifyPropertyChanged
public class Person : INotifyPropertyChanged
{
    private string firstNameValue;
    public string FirstName{
        get { return firstNameValue; }
        set
        {
            firstNameValue=value;
            // Call NotifyPropertyChanged when the property is updated
            NotifyPropertyChanged("FirstName");
        }
    }

    // Declare the PropertyChanged event
    public event PropertyChangedEventHandler PropertyChanged;

    // NotifyPropertyChanged will raise the PropertyChanged event passing the
    // source property that is being updated.
    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }  
}

 

 

 

또다른 예제

// Create a class that implements INotifyPropertyChanged.
public class MyColors : INotifyPropertyChanged
{
    private SolidColorBrush _Brush1;

    // Declare the PropertyChanged event.
    public event PropertyChangedEventHandler PropertyChanged;

    // Create the property that will be the source of the binding.
    public SolidColorBrush Brush1
    {
        get { return _Brush1; }
        set
        {
            _Brush1 = value;
            // Call NotifyPropertyChanged when the source property is updated.
            NotifyPropertyChanged("Brush1");
        }
    }


    // NotifyPropertyChanged will raise the PropertyChanged event, passing 
    // the source property that is being updated.
    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

유저컨트롤

namespace BM.CodeTest
{
    public delegate void PagerChangeIndex( string pageIndex ); //델리게이트 선언
    public partial class DelegateTestControl : UserControl
    {
        public event PagerChangeIndex pagerChangeIndex;
        public DelegateTestControl()
        {
            InitializeComponent();
          
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                pagerChangeIndex("10");
            }
            catch (Exception ex) {

                MessageBox.Show("델리게이트 구현");
            }
        }
    }
}

 

 

페이지

namespace BM.CodeTest
{
    public partial class DelegateTest : UserControl
    {
        public DelegateTest()
        {
            InitializeComponent();
            this.Btn.pagerChangeIndex += new PagerChangeIndex(Btn_pagerChangeIndex); //델리게이트 이벤트 구현
            
        }

        void Btn_pagerChangeIndex(string pageIndex)
        {
            MessageBox.Show(pageIndex);  //   10이 표시됩니다.
        }


    }
}

1.데이터바인딩

바인딩 개체 만들기

namespace BusinessEntities
{
    public class Movies
    {
        public Movies(string Title) {

            this.Title = Title;
        
        }

        public string Title { set; get; }
    }
}

 

2.classMovies를 담을 ObservableCollection<BusinessEntities.Movies> 개체 생성

    public class AllMovies  :ObservableCollection<BusinessEntities.Movies>
    {
        public AllMovies() {

            this.Add(new BusinessEntities.Movies("제목"));
            this.Add(new BusinessEntities.Movies("제목"));
            this.Add(new BusinessEntities.Movies("제목"));
            this.Add(new BusinessEntities.Movies("제목"));
            this.Add(new BusinessEntities.Movies("제목"));

        }

        
    }

 

3.표시할 xaml (MovieList.xaml )만들기

   <ListBox x:Name="MyBooks" ItemsSource="{Binding Mode=OneWay}"  Margin="16,48,16,24" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" >
                      
                        <TextBlock Text="{Binding Title}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

 

4.바인딩하기 MovieList.xaml 비하인드 코드

	public partial class MovieList : UserControl
	{
        BusinessEntities.AllMovies am;
		public MovieList()
		{
			// 변수를 초기화하는 데 필요합니다.
			InitializeComponent();
            am = new BusinessEntities.AllMovies();
            MyBooks.DataContext = am;

		}




	}

 

------------------------------------------------------------------------------------------------

바인딩된 개체 알아내기

------------------------------------------------------------------------------------------------

이벤트 추가

<ListBox x:Name="MyBooks" ItemsSource="{Binding Mode=OneWay}"  Margin="16,48,16,24"   SelectionChanged="MyBooks_SelectionChanged">

비하인드 코드

//선택변경
private void MyBooks_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
     ListBox SelectMovies = sender as ListBox;
     BusinessEntities.Movies mv = SelectMovies.SelectedItem as BusinessEntities.Movies;
     MessageBox.Show(mv.Title); 

}

Resource Page Description
Example code and other resources used by the Silverlight SDK.
Silverlight Clock
A simple clock with hour hand, minute hand, and second hand created using Silverlight.
Run this sample | Download | View documentation


Custom ControlTemplate for CheckBox
A custom ControlTemplate that changes the visual structure and visual behavior of a CheckBox.
Download | View documentation


Deep Zoom Example
A Deep Zoom example created using Silverlight
Download | View documentation


Isolated Storage Example
Describes how to save and retrieve data as key-value pairs or files in isolated storage.
Download


Accessing Web Service Example
Builds a Silverlight-enabled Web service and accesses its methods from a Silverlight client.
Download


Ink Erase Sample
Implements the point erase and stroke erase for ink.
Download


Silverlight Video Player
Video player with all the basic features like progress/seek slider, full screen toggle, play/pause/resume, etc.
Download

 

 

http://code.msdn.microsoft.com/silverlightsdk

using System;
using System.ComponentModel;

namespace DGRowDetails
{
    public class Task : System.ComponentModel.INotifyPropertyChanged
    {
        // The Task class implements INotifyPropertyChanged so that 
        // the datagrid row will be notified of changes to the data
        // that are made in the row details section.

        // Private task data.
        private string m_Name;
        private DateTime m_DueDate;
        private bool m_Complete;
        private string m_Notes;

        // Define the public properties.
        public string Name
        {
            get { return this.m_Name; }
            set
            {
                if (value != this.m_Name)
                {
                    this.m_Name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }

        public DateTime DueDate
        {
            get { return this.m_DueDate; }
            set
            {
                if (value != this.m_DueDate)
                {
                    this.m_DueDate = value;
                    NotifyPropertyChanged("DueDate");
                }
            }
        }

        public bool Complete
        {
            get { return this.m_Complete; }
            set
            {
                if (value != this.m_Complete)
                {
                    this.m_Complete = value;
                    NotifyPropertyChanged("Complete");
                }
            }
        }

        public string Notes
        {
            get { return this.m_Notes; }
            set
            {
                if (value != this.m_Notes)
                {
                    this.m_Notes = value;
                    NotifyPropertyChanged("Notes");
                }
            }
        }

        // Implement INotifyPropertyChanged interface.
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}


Silverlight 2를 사용하여 데이터 중심 웹 응용 프로그램 만들기

출처 : http://msdn.microsoft.com/ko-kr/magazine/cc794279.aspx

Data Services

Silverlight 2를 사용하여 데이터 중심 웹 응용 프로그램 만들기

Shawn Wildermuth

코드 다운로드 위치: SLDataServices2008_09a.exe (234KB)
온라인으로 코드 찾아보기

이 기사는 Silverlight 2 및 ADO.NET 데이터 서비스 시험판 버전을 기준으로 합니다. 여기에 포함된 모든 정보는 변경될 수 있습니다.

이 기사에서는 다음 내용에 대해 설명합니다.

  • 데이터 원본에 Silverlight 연결
  • ADO.NET 데이터 서비스 사용
  • LINQ to SQL 사용
  • 서비스 디버깅

이 기사에서 사용하는 기술:
Silverlight 2 베타 2, LINQ, ADO.NET 데이터 서비스

목차

ADO.NET 데이터 서비스
서비스 만들기
데이터 쿼리 및 업데이트
Silverlight 2.0 클라이언트 라이브러리
관련 엔터티 로드
변경 관리
서비스를 통해 업데이트
서비스 디버깅
정리

Silverlight™에서 기간 업무(LOB) 및 기타 데이터 중심 응용 프로그램을 작성할 수 있지만 Silverlight에서의 데이터 작업이 항상 간단하지만은 않습니다. Silverlight에는 데이터를 소비하고 웹 서비스 및 XML을 지원하는 데 필요한 많은 도구가 포함되어 있지만 이러한 도구는 방화벽을 통한 데이터 액세스의 기본 요소일 뿐입니다.

일반적인 데이터 액세스 전략에서는 웹 서비스와 클라이언트 쪽 LINQ를 섞어 사용하는데, 이는 기존 웹 서비스 끝점을 활용하여 Silverlight 응용 프로그램을 구동할 경우에 가장 권장되는 방법입니다. 그러나 특별히 Silverlight에서 작동하도록 새로운 웹 서비스를 구축하는 경우에는 불필요한 비용이 수반됩니다.

일반적인 웹 서비스 계층의 경우 서버에 기존의 데이터 액세스 전략을 구현하고(사용자 지정 비즈니스 개체, LINQ to SQL, Entity Framework, NHibernate 등) 웹 서비스를 통해 데이터 개체를 노출합니다. 이 경우 웹 서비스는 기본 데이터 액세스 전략에 대한 게이트웨이일 뿐입니다.

그러나 완전한 데이터 연결을 활성화하려면 4개의 데이터 작업(Create, Read, Update 및 Delete)을 웹 서비스 메서드에 매핑해야 합니다. 예를 들어 Product 클래스를 지원하기 위한 간단한 서비스 계약은 그림 1과 비슷할 것입니다. 이 기사에서는 C#을 사용하지만 코드 다운로드에는 Visual Basic® 코드도 포함되어 있습니다.

그림 1 간단한 Product 웹 서비스에 대한 서비스 계약

코드 복사

[ServiceContract]
public interface ICustomerService
{
  [OperationContract]
  List<Product> GetAllProducts();

  [OperationContract]
  Product GetProduct(int productID);

  [OperationContract]
  List<Product> GetAllProductsWithCategories();

  [OperationContract]
  Product SaveProduct(Product productToSave);

  [OperationContract]
  void DeleteProduct(Product productToDelete);
}
한 
응용 프로그램의 전체 데이터 모델과 함께 작동하는 서비스 집합을 만들려면 상당한 시간이 걸릴 수 있습니다. 그리고 이 예에서 볼 수 있듯이 기능별 작업으로 인해 웹 서비스가 불필요하게 커질 수 있습니다. 즉, 시간이 지남에 따라 웹 서비스에는 새로운 요구 사항과 작업이 추가되고, 여기에는 핵심 비즈니스 영역에 속하지 않는 작업도 포함됩니다. 

예를 들어 그림 1에서는 기본적으로 Product를 가져오고 범주를 포함하는 데 사용되는 GetAllProductsWithCategories 작업을 볼 수 있습니다. 이 간단한 예에도 정렬, 필터링 및 페이징 메커니즘이 추가된다는 것쯤은 쉽게 짐작할 수 있습니다. 이러한 모든 메커니즘을 계속 수동으로 작성하지 않고도 쿼리, 정렬, 필터링과 같은 데이터 작업을 지원하는 간단한 방법이 있다면 좋을 것입니다. 바로 이 부분에서 ADO.NET 데이터 서비스가 진가를 발휘합니다.

ADO.NET 데이터 서비스

ADO.NET 데이터 서비스의 목표는 데이터 모델에 웹 액세스가 가능한 끝점을 제공하는 것입니다. 이 끝점을 사용하면 개발자는 이러한 작업을 위한 기능을 사용자 지정하여 구축하지 않고도 서버의 데이터를 필터링하고, 정렬하고, 표현하고, 페이징할 수 있습니다. 기본적으로 각 끝점은 LINQ 쿼리의 시작 지점입니다. 끝점에서 여러분이 실제로 찾는 데이터에 대한 쿼리를 수행할 수 있습니다.

그러나 ADO.NET 데이터 서비스를 많은 데이터 액세스 전략 중의 하나로 치부하지는 마십시오. ADO.NET 데이터 서비스는 데이터 액세스를 직접 수행하지 않습니다. 사실 ADO.NET 데이터 서비스는 직렬화, 쿼리 및 업데이트를 지원하는 데이터 액세스 기술 위에 위치합니다. 데이터 쿼리 및 업데이트 작업은 이 기본 데이터 액세스 계층에서 담당합니다. 그림 2는 일반적인 응용 프로그램 아키텍처에서 ADO.NET 데이터 서비스의 위치를 보여 줍니다.

그림 2 ADO.NET 데이터 서비스 계층 (더 크게 보려면 이미지를 클릭하십시오.)

ADO.NET 데이터 서비스는 데이터 액세스 기능에 의존하여 실제 데이터 액세스 작업을 수행하므로 이러한 작업의 형태를 지정할 방법이 필요합니다. ADO.NET 데이터 서비스에서 각 서비스는 LINQ 지원 공급자의 지원을 받아야 합니다. 사실 각 끝점은 하나의 IQueryable 끝점에 불과합니다. 따라서 ADO.NET 데이터 서비스는 IQueryable을 지원하는 모든 개체를 지원할 수 있습니다.

서비스 만들기

ADO.NET 데이터 서비스를 프로젝트에 추가하면 서비스를 나타내는 클래스와 함께 새로운 .svc 파일이 만들어집니다. 웹 서비스와는 달리 서비스 작업을 직접 구현하지 않고 DataService 클래스가 대부분의 주요 작업을 처리하도록 합니다. 서비스를 실행하려면 몇 가지 간단한 작업이 필요합니다. 첫째, DataService 클래스에 context 개체라고 하는 형식 매개 변수가 필요합니다. context 개체는 서비스로 노출될 데이터를 설명하는 클래스입니다. 서비스가 관계형 데이터베이스의 데이터를 노출하는 경우 이 클래스는 일반적으로 EntityFramework의 ObjectContext 또는 LINQ to SQL의 DataContext에서 파생됩니다.

코드 복사

// Use my NorthwindEntities context object 
// as the source of the Service's Data
public class Products : DataService<NorthwindEntities>

context 개체에 대한 기본 클래스 요구 사항은 없습니다. 사실 속성이 IQueryable 인터페이스를 구현한다는 조건만 충족하면 직접 만들 수 있습니다. ADO.NET 데이터 서비스는 이러한 속성을 끝점으로 노출합니다.

코드 복사

public class StateContext
{
  StateList _states = new StateList();
  public IQueryable<State> States 
  {
    get { return _states.AsQueryable<State>(); } 
  }
}

InitializeService 호출에서는 서비스에서 허용할 사용 권한 유형을 지정하는 데 사용할 수 있는 IDataServiceConfiguration 개체가 제공됩니다. ADO.NET 데이터 서비스는 그림 3에서 볼 수 있듯이 명사와 동사를 추상화하여 사용 권한을 지정합니다.

그림 3 액세스 규칙 설정


// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{
  // Only allow us to read or update Products Entities
  // not Delete or Create
  config.SetEntitySetAccessRule("Products", 
                                EntitySetRights.AllRead | 
                                EntitySetRights.WriteUpdate);

  // Only Allow Reading of Category and Supplier Entities
  config.SetEntitySetAccessRule("Categories", EntitySetRights.AllRead);
  config.SetEntitySetAccessRule("Suppliers", EntitySetRights.AllRead);

}

이 작업이 완료되면 서비스로 직접 이동할 수 있고, 이렇게 하면 각 끝점에 대한 Atom 피드 정보가 표시됩니다. ADO.NET 데이터 서비스를 디버깅하려면 Internet Explorer®에서 RSS 피드 보기를 비활성화하거나 다른 브라우저를 사용하여 XML 형식의 서비스를 보는 것이 좋습니다.

데이터 쿼리 및 업데이트

ADO.NET 데이터 서비스는 서비스를 SOAP 기반 서비스가 아닌 REST(Representational State Transfer) 기반 서비스로 노출합니다. 이는 SOAP 봉투 대신 서비스의 응답 페이로드에 요청 메타데이터가 아닌 데이터만 포함됨을 의미합니다. 모든 요청은 HTTP 동사(GET, PUT, POST 등)와 요청 URI를 조합하여 기술됩니다. 그림 4와 같이 Product, Category 및 Supplier를 기술하는 모델이 있다고 가정해 보겠습니다. ADO.NET 데이터 서비스에는 모델의 각 엔터티 집합당 하나씩 3개의 끝점이 있을 수 있습니다. 모델에 있는 엔터티 집합의 주소를 지정하는 URI는 서비스의 주소와 끝점의 이름입니다(http://localhost/{서비스 이름}/{끝점 이름} 또는 http://localhost/Product.svc/Products).

그림 4 샘플 데이터 모델 (더 크게 보려면 이미지를 클릭하십시오.)

이 URI 구문은 특정 엔터티 검색, 결과의 정렬, 필터링, 페이징, 표현 등 다양한 기능을 지원합니다.

ADO.NET 데이터 서비스는 이러한 URL 스타일 쿼리를 사용하여 서비스 소비자에게 데이터를 반환합니다. 현재는 JSON(JavaScript Object Notation)과 Atom 기반 XML의 두 가지 직렬화 형식만 지원되지만 향후 버전에서 확장될 가능성이 높습니다. JSON은 클라이언트 쪽 웹 코드에서 사용하기에 편리한 형식이고, Atom은 나중에 XML 파서의 지원이 필요한 XML 기반 형식입니다.

쿼리에 직렬화 형식을 지정할 필요가 없는 대신 ADO.NET 데이터 서비스는 표준 HTTP Accept 헤더를 사용하여 클라이언트로 반환할 형식을 결정합니다. XML을 사용할 수 있는 클라이언트(예: 브라우저)에서 요청을 실행하는 경우, 그리고 Accept 헤더를 통해 기본 형식 유형을 지정하지 않는 경우 반환되는 데이터의 기본 형식은 Atom입니다.

데이터 쿼리는 솔루션의 일부일 뿐입니다. 궁극적으로 솔루션은 쿼리와 업데이트를 모두 지원해야 합니다. 이러한 요구 사항을 모두 지원하기 위해 ADO.NET 데이터 서비스는 4개의 기본 데이터 액세스 작업을 4개의 기본 HTTP 동사에 매핑합니다(그림 5 참조).

그림 5 데이터 액세스 동사와 HTTP 동사

데이터 액세스 동사
HTTP 동사

Create
POST

Read
GET

Update
PUT

Delete
DELETE

ADO.NET 데이터 서비스는 이러한 동사를 사용하여 서비스 소비자가 다양한 유형에 대한 특수한 끝점을 만들지 않고도 모든 유형의 데이터 작업을 활용할 수 있도록 합니다. ADO.NET 데이터 서비스 내에서 데이터 업데이트가 작동하도록 하기 위한 유일한 요구 사항은 IUpdatable 인터페이스를 지원하는 기본 데이터 액세스 기술에 대한 것입니다. 이 인터페이스는 업데이트가 ADO.NET 데이터 서비스에서 데이터 원본으로 전달되는 방식을 정의하는 계약입니다.

현재는 Entity Framework가 이 인터페이스를 지원하는 유일한 데이터 액세스 기술이지만 앞으로는 대부분의 계층에서 이에 대한 지원이 통합될 것입니다(LINQ to SQL, LLBGenPro 및 NHibernate 포함). ADO.NET 데이터 서비스에 대한 실무적인 지식을 익혔으니 이제 Silverlight 2에서 사용할 수 있습니다.

Silverlight 2.0 클라이언트 라이브러리

ADO.NET 데이터 서비스를 사용하여 URI 구문을 통해 데이터를 업데이트하고 쿼리를 실행하고 XML을 직접 조작하려는 경우 원하는 기능을 얻을 수는 있지만 상당한 양의 코딩 작업이 필요합니다. 바로 이 부분에서 Silverlight용 ADO.NET 데이터 서비스 클라이언트 라이브러리를 활용할 수 있습니다. 명령줄 도구와 함께 이 라이브러리를 사용하면 Silverlight 응용 프로그램에서 직접 LINQ 쿼리를 실행할 수 있습니다. 그러면 클라이언트 라이브러리에서 이러한 쿼리를 HTTP 쿼리 또는 데이터 서비스에 대한 업데이트 요청으로 변환합니다.

시작하려면 먼저 몇 가지 코드를 생성해야 합니다. 이 코드 생성에서는 ADO.NET 데이터 서비스 서비스의 메타데이터를 읽고 서비스의 엔터티에 대한 데이터 전용 클래스와 서비스 자체를 나타내는 클래스를 생성합니다.

이 코드를 생성하려면 Microsoft® .NET Framework 3.5 폴더(일반적으로 c:\windows\Microsoft.NET\Framework\v3.5\)에 있는 DataSvcUtil.exe 도구를 사용합니다. 이 도구를 실행하고 서비스의 URI, 출력 코드 파일, 코드를 작성하는 데 사용할 언어 등을 지정할 수 있습니다.

 

DataSvcUtil.exe –uri:http://localhost/Product.svc –out:data.cs  
  –lang:CSharp

이렇게 하면 각 끝점에 대한 데이터 계약 클래스와 DataServiceContext 파생 클래스가 포함된 새 파일이 작성됩니다. DataServiceContext 클래스는 서비스 진입점으로 사용되며 쿼리 가능한 서비스 끝점을 노출합니다. 이 클래스를 Silverlight 프로젝트에 추가하고 System.Data.Services.Client.dll(Silverlight 2 베타 2 SDK에 속함)에 대한 참조를 추가하면 ADO.NET 데이터 서비스로 작업하는 데 필요한 모든 코드를 얻을 수 있습니다.

Silverlight 클라이언트 코드는 .NET 대상 코드에서 작성할 수 있는 다른 LINQ 기반 쿼리와 유사합니다. DataServiceContext 파생 클래스를 만들고 이에 대한 LINQ 쿼리를 실행합니다. 코드는 다음과 같습니다.


// Create the Service class specifying the 
// location of the ADO.NET Data Services 
NorthwindEntities ctx = 
  new NorthwindEntities(new Uri("Products.svc", UriKind.Relative));

// Create a LINQ Query to be issued to the service
var qry = from p in ctx.Products
               orderby p.ProductName
                select p;

이 쿼리를 실행하면 원하는 데이터를 가져오기 위한 웹 요청이 실행됩니다. 그러나 Silverlight는 동기 웹 요청을 허용하지 않는다는 점에서 여기에 나온 Silverlight 코드는 표준 LINQ 쿼리와 크게 다릅니다. 따라서 비동기적으로 실행하기 위해 다음과 같이 먼저 쿼리를 DataServiceQuery<T> 개체로 캐스팅한 다음 BeginExecute를 명시적으로 호출하여 비동기 실행을 시작해야 합니다.

 

// Cast to a DataServiceQuery<Product> 
// (since the query is returning Products)
DataServiceQuery<Product> productQuery =
  (DataServiceQuery<Product>)qry;

  // Execute the Query Asynchronously specifying 
  // a callback method
  productQuery.BeginExecute(new 
    AsyncCallback(OnLoadComplete),
    productQuery);

쿼리가 실행되면 작업의 성공 여부에 관계없이 AsyncCallback에 지정된 메서드가 실행됩니다. 이때 LINQ 쿼리를 열거하여 실제 결과를 처리할 수 있습니다. 일반적으로 AsyncCallback에 원래 쿼리를 포함하면 콜백 메서드에서 이를 검색하거나 클래스의 일부로 저장할 수 있습니다(그림 6 참조).

그림 6 컬렉션에 결과 추가

코드 복사

void OnLoadComplete(IAsyncResult result)
{
  // Get a reference to the Query
  DataServiceQuery<Product> productQuery =
    (DataServiceQuery<Product>)result.AsyncState;

  try
  {
    // Get the results and add them to the collection
    List<Product> products = productQuery.EndExecute(result).ToList();

  }
  catch (Exception ex)
  {
    if (HtmlPage.IsEnabled)
    {
      HtmlPage.Window.Alert("Failed to retrieve data: " + ex.ToString());
    }
  }

}

LINQ를 다루어 본 경험이 없다면 이 패턴이 상당히 생소할 것입니다. 현재로서는 비동기 패키지에서 LINQ를 실행하는 것(예: ThreadPool 및 BackgroundWorker) 외에는 비동기 LINQ에 대한 좋은 패턴이 없습니다. Silverlight에서는 모든 요청이 비동기적이어야 하므로 ADO.NET 데이터 서비스 클라이언트 라이브러리를 사용할 때 이 패턴이 필요합니다.

관련 엔터티 로드

ADO.NET 데이터 서비스는 관련 엔터티를 로드하는 방식을 선택할 수 있도록 합니다. 이전 예에서는 서버에서 제품을 로드했습니다. 각 제품은 해당 제품의 공급업체 및 범주와 관련되어 있습니다.

이전 LINQ 쿼리를 사용할 때는 제품만 검색했습니다. 공급업체 또는 범주 정보를 보여 주려고 했다면 이러한 정보에 즉시 액세스할 수 없었을 것입니다. 필요할 때 이러한 정보를 로드하거나 서버에 대한 원래 쿼리에서 명시적으로 검색해야 했을 것입니다. 두 방법은 각기 장점이 있지만 일반적으로 모든 개체에 대한 정보가 필요한 경우에는 명시적 로딩이 훨씬 더 효율적입니다. 몇 가지 엔터티에 대한 데이터만 로드하려는 경우에는 필요 시 검색이 더 좋습니다.

기본적으로 관계 속성(예: Product.Supplier)이 있을 때 속성을 명시적으로 로드하지 않으면 해당 속성은 Null이 됩니다. 필요 시 로딩을 지원하기 위해 DataServiceContext 클래스에는 동일한 비동기 모델을 따르는 BeginLoadProperty 메서드가 있습니다. 이 메서드에서 원본 엔터티, 속성 이름 및 콜백을 지정할 수 있습니다.

 

public void LoadSupplierAsync(Product theProduct)
{
  TheContext.BeginLoadProperty(theProduct, 
                               "Supplier", 
                               new AsyncCallback(SupplierLoadComplete), 
                               null);
  }

  public void SupplierLoadComplete(IAsyncResult result)
  {
    TheContext.EndLoadProperty(result);
  }
EndLoadProperty
가 호출되면 속성에 관련 엔터티가 적절히 로드된 것입니다. 원래 쿼리에서 이를 명시적으로 로드해야 하는 경우가 많습니다. 이를 용이하게 수행할 수 있도록 LINQ 공급자는 Expand 확장 메서드를 지원합니다. 이 메서드를 사용하면 쿼리가 실행될 때 로드할 속성 경로의 이름을 지정할 수 있습니다. Expand 확장 메서드는 LINQ 쿼리의 From 절에서 공급자에게 관련 엔터티를 로드하도록 지시하는 데 사용됩니다. 예를 들어 Category와 Supplier 모두에 대해 Expand 메서드를 사용하도록 원래 쿼리를 변경하는 경우 원래 쿼리가 실행되는 동안 개체가 이러한 관련 엔터티를 로드하게 됩니다.

코드 복사

var qry = 
  from p in TheContext.Products.Expand("Supplier").Expand("Category")
          orderby p.ProductName
          select p; 

ADO.NET 데이터 서비스에서 데이터를 읽기만 한다면 더 이상 수행할 작업이 없습니다. 쿼리를 만들고, 실행하고, 관련 엔터티를 로드하는 방법만 알면 됩니다. 그러나 데이터를 수정하려는 경우에는 작업이 더 남아 있습니다. 새로운 데이터를 Silverlight 컨트롤에 바인딩하고 원하는 대로 수정하십시오.

변경 관리

ADO.NET 데이터 서비스 클라이언트 라이브러리는 개체에 대한 변경 자동 모니터링을 지원하지 않습니다. 따라서 개체, 컬렉션 및 관계가 변경되면 개발자가 직접 DataServiceContext 개체에 이러한 변경에 대해 알려야 합니다. DataServiceContext 개체에 알리기 위한 API는 상당히 간단하며 그림 7에 설명되어 있습니다.

그림 7 DataServiceContext 변경 API

메서드
설명

AddObject
새로 만든 개체를 추가합니다.

UpdateObject
개체가 변경되었음을 표시합니다.

DeleteObject
개체를 삭제하도록 표시합니다.

AddLink
두 개체 간 링크를 추가합니다.

UpdateLink
두 개체 간 링크를 업데이트합니다.

DeleteLink
두 개체 간 링크를 삭제합니다.

따라서 개발자는 개체에 대한 변경을 모니터링하고 직접 코드를 작성하여 DataServiceContext 개체에 이를 알려야 합니다. 겉보기에는 자동 변경 관리 기능이 없다는 점이 실망스러울 수 있지만 이를 통해 라이브러리는 효율성과 작은 크기를 확보할 수 있습니다.

개체에 대한 변경을 모니터링하는 방법이 궁금할 것입니다. 해답은 생성된 코드에 있습니다. 생성된 각 데이터 계약 클래스에는 클래스 변경에서 데이터로 호출되는 부분 메서드가 있습니다. 부분 메서드에 대한 자세한 내용은 go.microsoft.com/fwlink/?LinkId=122979를 참조하십시오. 이러한 메서드가 구현되지 않는다면 클래스가 이러한 변경 알림에서 오버헤드를 유발하지 않습니다. 부분 메서드 메커니즘은 변경에 따른 변경 알림 구현을 지원하는 모든 데이터 계약에서 사용할 수 있습니다. DataServiceContract 클래스를 결합할 필요 없이 부분 메서드에서 DataServiceContract를 호출하기만 하면 됩니다.

다행히 Silverlight는 이미 변경 알림을 위한 인터페이스(INotifyPropertyChange)를 지원합니다. 데이터 변경의 영향을 받는 사용자에게 변경을 알리기 위한 구현에서 이 인터페이스를 활용하십시오. 예를 들어 데이터 계약 클래스(예에서는 Product 클래스)에서 INotifyPropertyChange를 구현하여 개체가 변경될 때 실행할 이벤트를 정의할 수 있습니다. 코드는 다음과 같습니다.

 

public partial class Product : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
}

이를 구현하면 속성이 변경될 때마다 이벤트가 발생되도록 할 수 있습니다. 생성된 데이터 계약 클래스가 호출하는 부분 메서드를 구현하여 이러한 이벤트가 발생하는 시기를 결정할 수 있습니다. 예를 들어 ProductName 속성이 변경될 때 구독자에게 알리려면 OnProductNameChanged 부분 메서드를 구현한 다음 PropertyChanged 이벤트를 발생시켜 이벤트 구독자가 변경된 속성을 식별할 수 있도록 ProductName을 전달하기만 하면 됩니다. 코드는 다음과 같습니다.

 

partial void OnProductNameChanged()
{
  if (PropertyChanged != null)
  {
    PropertyChanged(this, new PropertyChangedEventArgs("ProductName"));
  }  
}

모든 쓰기 가능 속성에 대해 이러한 부분 메서드를 구현하면 개체에 대한 변경을 간단하게 모니터링할 수 있습니다. 그런 다음 PropertyChanged 이벤트에 등록하고 개체가 변경될 때 DataServiceContext 개체에 알릴 수 있습니다.

코드 복사

// In the OnLoadComplete method
// Get the results and add them to the collection
List<Product> products = productQuery.EndExecute(result).ToList();

foreach (Product product in products)
{
  // Wireup Change Notification
  product.PropertyChanged += 
    new PropertyChangedEventHandler(product_PropertyChanged);
}

마지막으로 product_PropertyChanged 메서드를 구현하여 DataServiceContext 개체에 알릴 수 있습니다.

 

void product_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  Product product = (Product)sender;
  TheContext.UpdateObject(product);
}

마찬가지로 개체가 만들어지거나 삭제될 때도 그림 8에서와 같이 DataServiceContext에 알려야 합니다.

그림 8 알림 만들기 및 삭제

코드 복사

void addNewButton_Click(object sender, RoutedEventArgs e)
{
  Product theProduct = new Product();
  // ...
  TheContext.AddObject(theProduct);
}

void deleteButton_Click(object sender, RoutedEventArgs e)
{
  Product theProduct = (Product)theList.SelectItem;
  TheContext.DeleteObject(theProduct);
  theCollection.Remove(theProduct);
}

이러한 코드를 모두 작성하면 Silverlight UI에서 개체를 변경하고 데이터 바인딩과 변경 알림 코드를 사용하여 발생할 변경을 DataServiceContex에 알릴 수 있습니다. 그런데 서비스에 대한 실제 업데이트는 어떻게 수행할까요?

서비스를 통해 업데이트

DataServiceContext 개체가 데이터 변경에 대해 알게 되었으므로 이제 이러한 변경 내용을 서버에 전달할 방법이 필요합니다. 이를 지원하기 위해 DataServiceContext 클래스에는 앞서 설명한 쿼리와 동일한 비동기 모델을 따르는 BeginSaveChanges 메서드가 있습니다. BeginSaveChanges 메서드는 다음과 같이 DataServiceContext의 모든 변경 내용을 받아 서버로 전송합니다.

코드 복사

TheContext.BeginSaveChanges(SaveChangesOptions.None, 
                            new AsyncCallback(OnSaveAllComplete), 
                            null);

BeginSaveChanges를 실행할 때는 SaveChangesOptions라는 플래그가 지정된 열거형이 있습니다. 이 열거형을 사용하여 일괄 처리를 사용할지, 일부 개체가 저장에 실패하는 경우에도 계속할지를 지정할 수 있습니다. 일반적으로 항상 일괄 처리를 사용하는 것이 좋습니다. 사실 서버에 특정 참조 무결성 제약이 있는 경우 올바른 업데이트를 위해서는 일부 부모/자식 관계에 일괄 처리가 필요합니다.

저장이 완료되면 콜백이 실행됩니다. 오류 정보를 전달하는 메커니즘은 두 가지입니다. 첫째, 저장 실행 중에 발생한 예외가 있으면 콜백에서 "EndSaveChanges"를 호출할 때 이 예외가 발생합니다. 따라서 try/catch 블록을 사용하여 심각한 오류를 잡아야 합니다. 또한 EndSaveChanges의 반환 형식은 DataServiceResponse 개체입니다. DataServiceResponse에는 HasErrors 속성(그림 9 참조)이 있지만 Silverlight 2 베타 2 버전의 라이브러리에서는 불안정합니다.

그림 9 BeginSaveChanges 콜백

코드 복사

void OnSaveAllComplete(IAsyncResult result)
{
  bool succeeded = true;
  try
  {
    DataServiceResponse response = 
      (DataServiceResponse)TheContext.EndSaveChanges(result);

    foreach (OperationResponse opResponse in response)
    {
      if (opResponse.HasErrors)
      {
        succeeded = false;
      }
    }

  }
  catch (Exception ex)
  {
    succeeded = false;
  }

  // Alert the User
}

추가 정보

여기에 의존하는 대신 실제 OperationResponse 개체를 반복하여(DataServiceResponse는 OperationResponse 개체의 컬렉션임) 서버에서 반환된 각 응답에 오류가 있는지 확인할 수 있습니다. 이후 버전에서는 DataServiceResponse 클래스 자체의 HasErrors 속성을 사용할 수 있게 될 것입니다.

서비스 디버깅

서비스를 디버깅하는 동안 수행해야 할 중요한 작업 세 가지는 DataServiceContext 개체의 데이터 상태 보기, ADO.NET 데이터 서비스의 요청 보기, 그리고 마지막으로 서버 오류 잡기입니다.

먼저 DataServiceContext 개체의 엔터티 상태부터 살펴보겠습니다. DataServiceContext 클래스는 Entities와 Links라는 두 가지 유용한 컬렉션을 노출합니다. 이러한 컬렉션은 엔터티와 DataServiceContext에서 추적하는 엔터티 간 링크가 포함된 읽기 전용 컬렉션입니다. 디버깅할 때는 개체의 변경 표시 여부에 관계없이 디버거에서 이러한 컬렉션을 관찰하여 변경 추적 코드가 올바르게 작동하고 있는지 확인하는 것이 중요합니다.

또한 Silverlight 2 응용 프로그램의 실제 서버 요청도 확인해야 합니다. 가장 좋은 방법은 일종의 네트워크 프록시를 사용하는 것입니다. 필자는 이러한 프록시로 Fiddler2 (fiddler2.com)를 사용합니다. Fiddler2에 익숙하지 않은 독자를 위해 설명하자면, 이 프로그램은 전송 시 발생하는 일을 확인하기 위한 웹 트래픽 관찰 도구입니다.

ADO.NET 데이터 서비스의 경우 실제로 수행되는 통신을 확인하여 Silverlight 응용 프로그램에서 송수신되는 트래픽을 살펴보아야 합니다. 자세한 내용은 필자의 블로그(wildermuth.com/2008/06/07/Debugging_ADO_NET_Data_Services_with_Fiddler2)를 참조하십시오.

마지막으로 최신 .NET Framework 3.5 SP1에서 서버 쪽 오류는 클라이언트로 잘 전달되지 않습니다. 사실 서버에서 발생하는 대부분의 오류는 서버 내에서만 처리됩니다. 서버 오류를 디버깅하기 위한 가장 좋은 방법은 디버그 메뉴의 예외 옵션(디버그 -> 예외…)을 사용하여 모든 .NET 예외를 확인하고 넘어가도록 디버거를 구성하는 것입니다. 이 옵션을 선택하면 서비스에서 일으키는 예외를 볼 수 있습니다. 다만 처음 발생하는 다른 모든 예외에 대해 "계속"을 클릭해야 합니다.

정리

이 기사에서 필자는 ADO.NET 데이터 서비스가 Silverlight 2와 서버 기반 모델 사이에서 어떻게 게이트웨이 역할을 하는지 살펴보았습니다. 이제 수동으로 디자인된 웹 서비스에 의지하지 않고 ADO.NET 데이터 서비스를 사용하여 서버의 데이터를 읽고 쓰는 방법을 알게 되었을 것입니다. 이 기사에서 볼 수 있듯이 Silverlight, ADO.NET 데이터 서비스 및 LINQ를 조합하여 사용하면 Web 2.0 기술의 모든 이점을 갖춘 강력한 데이터 기반 웹 응용 프로그램을 작성할 수 있습니다. 이러한 기술에 대한 자세한 내용은 "추가 정보" 보충 기사를 참조하십시오.

Shawn Wildermuth는 Microsoft MVP(C#)이며 Wildermuth Consulting Services의 설립자이고, 여러 권의 책과 수많은 기사를 집필했습니다. 또한 Shawn은 현재 미국 전역에서 Silverlight 투어를 진행하면서 Silverlight 2를 강의하고 있습니다. 문의 사항이 있으면 shawn@wildermuthconsulting.com으로 연락하십시오.

+ Recent posts