Study/Study2008. 3. 4. 18:22

테스트는 응용 프로그램이 디자인 요구 사항에 따라 동작하고 성능 기대치를 충족시키는지 검사하는 프로세스입니다. 이보다 더 중요한 테스트의 기능은 응용 프로그램이 고객의 기대치를 충족시키는지 확인하는 것입니다.

단원 내용

테스트 개요
테스트 프로세스와 테스트 방법에 대한 개요를 제공합니다.
유닛 테스트
응용 프로그램의 모듈 섹션을 테스트하는 프로세스에 대해 설명합니다.
통합 테스트
두 개 이상의 섹션이 조합된 응용 프로그램을 테스트하는 프로세스에 대해 설명합니다.
역행 검사
구현이 변경된 후 응용 프로그램을 다시 테스트하는 프로세스에 대해 설명합니다.
Visual Studio Analyzer
Visual Studio Analyzer는 분산 응용 프로그램을 점검하고 디버깅하는 데 사용할 수 있는 성능 분석 도구입니다.
Microsoft Application Center Test 1.0, Visual Studio .NET Edition
Microsoft Application Center Test는 웹 서버에 스트레스 테스트를 수행하고 성능 및 확장성 문제를 분석하기 위해 고안되었습니다.

관련 단원

가용성 테스트
응용 프로그램의 가용성을 테스트하는 방법에 대해 간략하게 설명합니다.
관리 효율성 테스트
응용 프로그램의 관리 효율성을 테스트하는 방법에 대해 간략하게 설명합니다.
성능 테스트
응용 프로그램의 성능을 테스트하는 방법에 대해 간략하게 설명합니다.
안정성 테스트
응용 프로그램의 안정성을 테스트하는 방법에 대해 간략하게 설명합니다.
확장성 테스트
응용 프로그램의 확장성을 테스트하는 방법에 대해 간략하게 설명합니다.
보안성 테스트
응용 프로그램의 보안을 테스트하는 방법에 대해 간략하게 설명합니다.
전역화 및 지역화 테스트
응용 프로그램의 전역화 및 지역화를 테스트하는 방법에 대해 간략하게 설명합니다.
디버깅
Visual Studio에서 코드를 디버깅하는 방법에 대해 간략하게 설명합니다.
대화 상자 테스트
대화 상자 편집기 내에서 대화 상자의 런타임 동작을 시뮬레이션하는 방법에 대해 설명합니다.
성능 조정 개요
성능 조정 프로세스에 대해 간략하게 설명합니다.
Testing Process
테스트의 필요성과 여러 가지 테스트 유형에 대해 설명합니다.

출처 : http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/vsent7/html/vxconUnitTesting.asp
Posted by 굥쓰
Study/WebService2008. 2. 20. 01:31

Download source and demo project - 10.3 Kb

Sample Image

Introduction

The purpose of this article is to demonstrate how to expose a method from an ASP.NET Web Service or WinFX Service that uses the asynchronous pattern internally, to increase the scalability of the service. Specifically, how to prevent the threads that are being used to service requests from blocking while background I/O operations take place.

Please note, this article does not deal with applications simply making asynchronous calls to Web Services, as this has been amply covered already. However, if you are familiar with that, you'll notice plenty of similarities between the client-side and the server-side implementation.

Contents

Requirements

I've written this using Visual Studio 2005, .NET 2.0, and the WinFX Feb CTP, on a Windows XP machine. The ASP.NET Web Service code should run on .NET 1.0 and .NET 1.1, although I haven't tested this.

Background

Before we start, let's just recap what normally happens when a consumer makes a request against an ASP.NET Web Service. The consumer issues a SOAP request to IIS which will hand the request over to the ASMX handler. If it hasn't already done so, the ASMX handler will build/reflect over the assemblies in the bin folder, looking for the methods decorated with the WebMethod attribute. The ASMX handler can then match a SOAP request with a specific method, and hence deserialises the request into the input parameters of that method. When that method returns, the thread running it is returned to the pool, and the associated HttpContext is released.

Writing the code - ASP.NET Web Service

Before we start writing any code to exploit the asynchronous features of the hosting platform, we need to create a synchronous method that performs some kind of I/O. I've chosen file-based I/O, but the principles apply equally to network I/O, such as a call to a backend ASP.NET Web Service or database. Below is the code for a method that opens a large file and counts the number of zero bytes inside it. I've called it SyncWork. The large file I've chosen is the TrueType Arial font because it's installed on most XP machines and weighs in at 22 MB, but any large file will do.

[WebMethod]
public Int32 SyncWork() 
{
    FileStream fs = 
      new FileStream(@"c:\windows\fonts\arialuni.ttf", 
      FileMode.Open);

    try
    {
        Byte[] buffer = new Byte[fs.Length];
        fs.Read(buffer, 0, (Int32) fs.Length);

        Int32 count = 0;
        foreach (Byte b in buffer)
        {
            if (b == 0) count++;
        }
        return count;
    }
    finally
    {
        fs.Close();
    }
}

Looking at SyncWork, we can identify that the call to FileStream.Read is going to involve reading some data from the hard disk. This is going to take some time to complete, and while we're waiting, our thread will be blocked and hence not performing any useful work. It would be better if this thread could be returned to the thread pool managed by our host (in this case IIS, or specifically the ASMX handler) so that more requests could be served. This is a scenario where implementing server-side asynchronous methods can help to make our service more scalable.

I'm going to create a new method on the service called AsyncWork which will perform the same function for the consumer as SyncWork but it will behave asynchronously. Although AsyncWork will appear to the consumer as a single method, we have to write two separate methods, named BeginAsyncWork and EndAsyncWork, respectively. The required signatures for these methods are shown below:

[WebMethod]
public IAsyncResult BeginAsyncWork(AsyncCallback callback, Object state)
{
    ...
}

[WebMethod]
public Int32 EndAsyncWork(IAsyncResult ar)
{
    ...
}

Notice that the BeginAsyncWork method has two additional input parameters, appended to the end of the parameter list. In this case, they are the only two input parameters. The first parameter refers to a callback method that should be called when the background I/O work is complete. The state object is anything that the host would like us to associate with this asynchronous work so that it can identify it later. In my experience, this has always been null, but there's nothing to suggest it won't be used in the future so it's important to treat it properly. The BeginAsyncWork method must return an IAsyncResult. This same IAsyncResult will be passed as the sole parameter to the EndAsyncWork method when the background I/O operation is complete. The EndAsyncWork method will have the same return type as the SyncWork method, in this case an Int32.

Crucially, the EndAsyncWork method may be called on a different instance of our Service class than the call to BeginAsyncWork. This is because the thread on which the BeginAsyncWork code ran will be returned to the thread pool as soon as it is finished so that it can be used to service another request - the whole point of this exercise in fact! A thread won't be selected to run the EndAsyncWork code until the ASMX handler receives notification that the background I/O operation is complete. All of this grabbing and releasing of threads means that we can't share information between the BeginAsyncWork and EndAsyncWork methods using member variables on our Service class. We'll need to create a separate class to hold state information, and give it to the ASMX handler to look after for us. Since the ASMX handler is going to get an object that implements IAsyncResult from BeginAsyncWork, and it's going to pass this object to EndAsyncWork, it makes sense for our state to be contained in that object. So in short, we'll store our state information in the object that implements IAsyncResult. The sequence diagram below shows the messages that are sent between the various objects during the process:

The consumer issues an AsyncWork request, which causes the ASMX handler to call BeginAsyncWork, which creates a state object and starts the background I/O operation via the FileStream's BeginRead method. The service1 object is finished so it can be garbage collected and its thread returned to the pool. When the background I/O operation finishes, it calls the ReadOperationComplete method on the serviceState object which in turn invokes the ASMX handler's callback method. The ASMX handler responds to this notification by calling the EndAysncWork method which finishes the job. Notice in the diagram above that the two Service classes, service1 and service2, have short life-times with respect to the whole operation (as shown in yellow).

A state class needs to maintain several pieces of information. It must know about the callback and state that was passed into the BeginAsyncWork method. It must reference the variables shared between the BeginAsyncWork and EndAsyncWork methods, which in this case boils down to a FileStream, an IAsyncResult returned from the FileStream, and a Byte array. It must implement the IAsyncResult interface so that it can be returned to the ASMX handler. Finally, it must provide a method which the background operation (FileStream, in this case) can call to tell us that it's finished. The ServiceState class below satisfies these requirements:

public class ServiceState : IAsyncResult
{
    // callback and state object for the host
    public AsyncCallback HostCallback;
    public Object HostState;

    // information needed for the background I/O call
    public Byte[] Buffer;
    public FileStream Stream;
    public IAsyncResult ReadOperationAsyncResult;

    // implementation of the IAsyncResult interface for the host
    public object AsyncState { get { return HostState; } }
    public WaitHandle AsyncWaitHandle { get { 
           return ReadOperationAsyncResult.AsyncWaitHandle; } }
    public Boolean CompletedSynchronously { get { 
           return ReadOperationAsyncResult.CompletedSynchronously; } }
    public Boolean IsCompleted { get { 
           return ReadOperationAsyncResult.IsCompleted; } }

    // our callback method that will be
    // notified when the background I/O is done
    public void ReadOperationComplete(IAsyncResult ar)
    {
        ServiceState serviceState = ar.AsyncState as ServiceState;
        serviceState.HostCallback(serviceState);
    }
}

Notice that this implementation of the IAsyncResult interface is achieved by simply wrapping most of the properties of the ReadOperationAsyncResult member. This member is provided by the FileStream object when calling BeginRead, and provides information about that specific background operation. Since this is our only background operation, we can just wrap it. If we had multiple background operations, we would need to implement IAsyncResult ourselves with properties that reflected the complete picture. For example, if only one of two background operations had completed, then the IsCompleted property would need to return false. The AsyncState property returns the HostState object. This is because if the host was to access the AsyncState property, then it would expect to get the same object that it provided in the initial call to BeginAsyncWork.

So, let's actually write the BeginAsyncWork method. We begin by constructing the object that will contain our state information, a ServiceState object in this case. I've stored the callback and the state. I've then created the FileStream, and subsequently the Byte array, that I need for the operation, and attached these to the state also. I then start the background I/O operation by providing the input parameters for both the read operations (a Byte array, start point, number of bytes) and for the asynchronous facility (a callback and my state object). My state object, serviceState, is then returned as this implements the IAsyncResult interface and allows the host to keep track of the background operation.

[WebMethod]
public IAsyncResult BeginAsyncWork(AsyncCallback callback, Object state)
{
    ServiceState serviceState = new ServiceState();
    serviceState.HostCallback = callback;
    serviceState.HostState = state;
    serviceState.Stream = new 
      FileStream(@"c:\windows\fonts\arialuni.ttf", FileMode.Open);

    try
    {
        serviceState.Buffer = new Byte[serviceState.Stream.Length];
        serviceState.ReadOperationAsyncResult = 
             serviceState.Stream.BeginRead(serviceState.Buffer, 0, 
            (Int32)serviceState.Stream.Length, 
             new AsyncCallback(serviceState.ReadOperationComplete), serviceState);
        return serviceState;
    }
    catch (IOException)
    {
        serviceState.Stream.Close();
        throw;
    }
}

When the background I/O operation is complete, it will call back to the ServiceState.ReadOperationComplete method. Taking a look at that method again below, you can see that I'm extracting the serviceState object that I provided, and I'm using it to access the HostCallback. By invoking this method, I'm letting the ASMX handler know that the job is done and that the EndAsyncWork method should now be called.

public void ReadOperationComplete(IAsyncResult ar)
{
    ServiceState serviceState = ar.AsyncState as ServiceState;
    serviceState.HostCallback(serviceState);
}

The ASMX handler will call EndAsyncWork, and this is where the original request made by one of the consumers of our service should be fulfilled. The method below shows how this is done. First, I extract the serviceState, and then call the FileStream.EndRead method to complete the Read operation. Finally, the serviceState object is accessed again in order to work on the Byte buffer that has been populated so as to determine the number of zero bytes. This value is returned from the method, and hence to the consumer, and this request has now been fulfilled.

[WebMethod]
public Int32 EndAsyncWork(IAsyncResult ar)
{
    ServiceState serviceState = (ServiceState)ar;
    try
    {
        serviceState.Stream.EndRead(serviceState.ReadOperationAsyncResult);
    }
    finally
    {
        serviceState.Stream.Close();
    }

    Int32 count = 0;
    foreach (Byte b in serviceState.Buffer)
    {
        if (b == 0) count++;
    }
    return count;
}

Writing the code - WinFX Service

To get this working under WinFX requires a few very minor modifications. Rather than creating a new project and a new class, I'm going to convert the existing class because it's quicker. I'm using the Feb CTP build for this. First, we need to define a contract for our service, which is done by simply defining an interface decorated with the ServiceContract attribute. The interface for our service is shown below. I've called it IWinfxService.

[ServiceContract()]
public interface IWinfxService
{
    [OperationContract]
    Int32 SyncWork();

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginAsyncWork(AsyncCallback callback, Object state);

    Int32 EndAsyncWork(IAsyncResult ar);
}

The first method in our contract is the SyncWork method. As you might expect, it has the same signature as the SyncWork method defined on the Service class, but it is also decorated with the OperationContract attribute. For methods that the WinFX host (in this case, still IIS) is to treat asynchronously, we need to set the AsyncPattern property of the OperationContract to true. As a result of doing this, we no longer need to decorate the EndAsyncWork method with any attributes but it does need to be present in the interface.

Next, we need to indicate that our Service class implements this interface. We do this by appending IWinfxService to the class declaration.

public class Service : System.Web.Services.WebService, IWinfxService
{
    ...
}

Next, we need an entry point for our WinFX host, which in this case is IIS. To do this, we create a Service.svc file in the root of the website and put in the line of text shown below. This line instructs the WinFX host to look for a class called Service in a file called ~/App_Code/Service.cs. If you create your WinFX services using the 'Add New Item' dialog, then this will normally be created for you, but here we're converting an ASP.NET Web Service so we have to create it manually.

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

Finally, we need to put a few extra lines into the Web.Config file. Again, if you had used Visual Studio and the 'Add New Item' dialog to create your WinFX service, then this would be done for you. The section that needs to be added to the Configuration node is shown below. Please bear in mind that this is one possible configuration - you can change the binding used for the service, and you would certainly want to change the behaviour such that it didn't return detailed faults to the consumer.

<system.serviceModel>
    <services>
        <service name="Service" behaviorConfiguration="returnFaults">
            <endpoint contract="IWinfxService" binding="wsHttpBinding"/>
        </service>
    </services>
    <behaviors>
        <behavior name="returnFaults" 
                 returnUnknownExceptionsAsFaults="true" />
    </behaviors>
</system.serviceModel>

Now, we just need to test it. First, check that the WinFX service builds OK by setting the website as the startup project and running it. When the IE window pops up, navigate to the service.svc page. Highlight the full url, which will be something like http://localhost:1234/WebSite2/Service.svc, and press Ctrl-C. Next, create a new console application. Then, right-click on the project and select 'Add Service Reference'. In the dialog that appears, paste the URL into the top box and type 'localhost' in the bottom box, and click OK. This sets up a reference to your service. In the Feb CTP of WinFX, there's no support for browsing to a WinFX service in your solution but this will almost certainly improve in newer builds.

Finally, replace the auto-generated Program class with the one below to test your WinFX service. You can use break-points to satisfy yourself that the service is using the asynchronous pattern internally.

class Program
{
    static void Main(string[] args)
    {
        localhost.WinfxServiceProxy proxy = 
           new localhost.WinfxServiceProxy();
        Console.WriteLine(proxy.AsyncWork());
        Console.ReadKey();
    }
}

If you have any problems running the ConsoleApplication test harness included in the download, then it's probably because, on your computer, the website has been assigned a different port number. One way to fix this is to just remove and re-add the Service Reference using the instructions explained two paragraphs above.

Summary

I've shown how to create a service method using the asynchronous pattern to increase the scalability of services that need to perform background I/O operations whether that be implemented using an ASP.NET Web Service or a WinFX service.

History

  • No changes made so far.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Karl M. Hulme


I live with my wife in Bournemouth in the UK. Her shining brilliance consistently inspires me to be the person I know I should be. I enjoy writing music, playing the piano, watching movies, philisophical discussion and eating jaffa cakes.

In my spare time I work for Regency as ICT Manager, dividing my time between IT strategy, software architecture, development, infrastructure, networking and crawling around under desks.
Occupation: Web Developer
Location: United Kingdom United Kingdom
출처 : CodeProject.com (http://www.codeproject.com/KB/cpp/asyncws.aspx)
Posted by 굥쓰
Study/WebService2008. 2. 20. 01:28
by Peter A. Bromberg, Ph.D.

Peter Bromberg
"Watching non-programmers trying to run software companies is like watching someone who doesn't know how to surf trying to surf." -- Joel Spolsky

We've been getting a few of this genre of questions recently on our forums, and I decided it would be a good time to put something together. And since we seem to be getting a lot of VB.NET'ers, I've broken with tradition and will post code in both VB.NET and C#, as well as in the downloadable solution.

First let's ask and answer a few basic questions:

Q: What does "Asynchronous" really mean?

A: Literally, "not synchronous"; not at the same time. Asynchronous is a programming term that means a type of two-way communication that occurs with a time delay, allowing participants to respond at their own convenience. In other words, we can make a method call and go about our work without delay, and when the results are ready, we are notifed appropriately and are able to act upon them.


Bug Tracking Saves Time
Save hundreds of development hours each month. Click to see how...

GoDiagram Components
Add diagrams to improve your user interface, allowing users to more easily visualize and manipulate their data.

Demo Builder - Create Flash Presentations
Create interactive Flash movies that allow you to show how applications and systems work.  Download a FREE trial now.

Q: When would I want to use asynchronous web service methods?

A: Whenever you want! Particularly, if the method call may take some time and you do not want to have your user interface "frozen" while waiting for the results. Asynchronous WebMethod calls are as efficient, and sometimes more efficient than their synchronous siblings.

In ASP.NET, starting with ASP.NET 1.0, asynchronous versions of WebMethods are wired into the WebService infrastructure automatically. On the server side, you need do nothing except create your WebMethod, and the WSDL contract that .NET uses to generate a proxy class to make calls on service methods will automatically include asynchronous versions of each method. You can identify these because they begin with "Begin"<WebMethodName> and "End"<WebMethodName>.

Q: How does all this work?

A: Asynchronous methods in webservices work the same way as asynchronicity in any other API - the inbound method call is intercepted and acted upon. However, the results are sent via what is referred to as a "Callback". A callback is a method on the client which is "rung up" or called back, with the results of the call. What this means is that a method call to a BeginXXX WebMethod returns immediately, it is said to be "non blocking". This allows your UI to remain free so the user continues to have full use of its features while waiting for the result. When the result arrives, it is the separate callback method that receives and processes it.

Now let's take a look at some simple ASP.NET 1.1 VB.NET code for a WebMethod that accepts a string, and packages it up, returning a DataSet object as the return type:


    <WebMethod()> _

    Public Function GetDataSet(ByVal value As String) As DataSet

        Dim ds As New DataSet

        Dim dt As New DataTable

        dt.Columns.Add("Test")

        Dim row As DataRow

        row = dt.NewRow

        row("Test") = value

        dt.Rows.Add(row)

        ds.Tables.Add(dt)

        Return ds

    End Function

The above is quite simple and should require no explanation. However, if we take a look at the proxy code that is generated by creating a WebReference to this service, we see:

Those two artful red arrows show the Asynchronous version set of methods that ASP.NET automatically generated for us to match our synchronous "GetDataSet" method.

Consuming Asynchronous Methods

Now comes the fun part - how to use these new guys!

As with any WebService method call, we need an instance of our proxy class. But that's where things change. Consider the following sample VB.NET code from a Windows Forms application that consumes our "GetDataSet" method:

 ' This is our button click handler. It creates the service proxy instance, and calls the BEGINXXX method:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim ws As New peter.Service1

        Dim ar As IAsyncResult = ws.BeginGetDataSet("Testing 1234", New AsyncCallback(AddressOf WsCallback), ws)

    End Sub


    ' This is our Callback method. It RECEIVES the callback in the form of an IAsyncResult object we can act on.

    Private Sub WsCallback(ByVal ar As IAsyncResult)

        Dim cb As peter.Service1 = ar.AsyncState

        Dim ds As DataSet = DirectCast(cb.EndGetDataSet(ar), DataSet)

        Me.Label1.Text = ds.Tables(0).Rows(0)("Test")

    End Sub

Note that the "BeginGetDataSet" method call has a distinctive signature - the string input parameter is passed first (there could be more than one parameter) , then we create a new AsyncCallback object that "points" to the WsCallback callback (the "receiver") method. This Button1_Click method returns immediately - there is no blocking of the thread. Note also that in this case I decided to pass the actual "ws" proxy class instance itself as the last parameter, which is the AsyncState parameter, of type Object.

In the WsCallback method, we receive the IAsyncResult and I immediately get back my original proxy class instance by casting it out of the IAsyncResult object's AsyncState property. Then, all I need to do is call the asynchronous "EndGetDataSet" method, passing in the AsyncResult object, and cast this to the return type of DataSet.

At this point I have a valid DataSet object and I can do whatever I "am supposed to do" with it - update a DataGrid, whatever. In this simplified example I simply get out my one column value from my single DataTable row and update the Label with it.

This infrastructure has a number of variations, including the use of delegates. Also note that the return value of the original ws.BeginGetDataSet method call is also an IAsyncResult object, and this can be used as well.

While there are a number of variations on this theme, the above pattern and signatures are useful in the vast majority of instances.

Also, I think it is important to bear in mind that in ASP.NET 2.0, we have some interesting enhancements, most notably that there is now an Event-driven WebService infrastructure, which makes for a much cleaner use pattern on the client / consumer side. In addition, it provides the ability to work with servers that enable HTTP (Gzip) compression to conserve bandwidth.

The C# Consumer pattern looks like this:

private void button1_Click(object sender, System.EventArgs e)

        {

            peter.Service1 ws = new CSharpTester.peter.Service1();

            ws.BeginGetDataSet("Testing 1234",new AsyncCallback(WsCallback),ws);

        }



        private void WsCallback( IAsyncResult ar)

        {

            peter.Service1 ws =  (peter.Service1)ar.AsyncState;

            DataSet ds = ws.EndGetDataSet(ar);

            this.label1.Text=(string)ds.Tables[0].Rows[0]["test"];

        }

The downloadable Visual Studio.NET 2003 solution below has a VB.NET Service page, and both C# and VB.NET versions of a Winforms consumer application.

Download the Visual Studio.NET 2003 Solution that accompanies this article

Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here.  --> Social Link this page<--NOTE: Post QUESTIONS on FORUMS!


Posted by 굥쓰