HomeDigital EditionSearch Dotnet Cd
ASP.NET C# Certification Exams The CLI Data Access Editorials Extending .NET Fundamentals Interoperability Interviews Migrate Mobile .NET Mono .NET Interface Object-Oriented Programming Open Source Optimization Product/Book Reviews Security Source Code UML Visual Studio .NET

There are significant benefits to using third-party components, and many programmers have come to depend on components to handle common tasks that would require either too much time or specialization for them to implement themselves. However, many programmers fail to realize that there are component options available for almost every need. One of the most overlooked ­ and least discussed ­ benefits of using a component is the performance and scalability inherent in a quality component. While at first this may seem counterintuitive, as another layer is being placed between your code and a task, the architecture and implementation of most established third-party components makes the trade-off worthwhile.

There are four primary benefits to using a third-party component. First, the component is often written by an organization or a group of individuals within an organization that specializes in a niche technology. For example, while most developers may occasionally run across FTP or Telnet requirements in their applications, experienced component developers have lived and breathed all aspects of those protocols for years. Second, because many established component developers have been around since Visual Basic first appeared, they have seen a vast array of application requirements and have engineered those requirements into the components. Often this means recognizing problems that a developer may face long after deployment. Because these products have matured over so many versions, a user never has to worry about Server X responding in a noncompliant way and crashing the component. The engineers saw that happen in 1994 and wrote in the code to handle that situation. The third benefit is in the area of testing. Unless you are a specialist in the field, testing home-grown FTP code against a wide variety of servers will be difficult unless you have substantial time and resources. Finally, the established component companies work closely with Microsoft. This means the developers spend time at Redmond testing, asking questions, and tweaking their code to best take advantage of the Microsoft development environment. Best of all, when there are architectural questions, they can correspond directly with the actual developer who wrote the code for Microsoft.

What does all of this have to do with scalability, the Internet, and components? Adding Internet functionality to business applications has become a common requirement. This could mean running an automated report from an AS400 server using Telnet, transferring a file from point A to point B at a given time every day; checking a mail server for a particular message and then taking action based on the contents of that message; or building a system for IPC (Inter-Process Communications) based on a proprietary protocol or a protocol common to a particular industry. Components are perfect solutions, as they will save time and resources while adding performance and scalability. In this article I will demonstrate the advantages of components and offer some insights on how their internal design impacts the end-user experience. While I will focus on two Dart Communications products, PowerTCP FTP for .NET and PowerTCP Mail for .NET, the general lessons of component usage can be applied to most established component vendors.

The Fundamentals
Before getting into the code, let's address a few fundamental questions. The first is how does the internal architecture of these products enhance performance? For the most part, component users should get an efficient implementation out of the box. Performance starts with design. A component should not rely on a Windows API unless absolutely necessary. For example, an FTP component should not rely on Windows Internet Explorer to handle FTP, but rather implement the code itself, or if creating an SMTP component, then it should not depend on Microsoft's SMTP service, but instead write the protocol code from the ground up. A reliance on these APIs adds an unnecessary layer; they create inefficiencies because they have code that is executed that may have nothing to do with the task at hand. Also, using these APIs creates a dependence on their installation and configuration, which can be a risky, or at best, unknown proposition. There are exceptions to those rules, especially when working with certain security APIs, where compatibility with other Windows processes or technology is required because of their proprietary nature or implementation. In general, a component should not be a simple wrapper on a Microsoft API.

Architecture
Another aspect of performance is the general architecture of the code. The simplest approach is to architect the component on a single thread, but then all the code is forced to operate in the same time-slice, and if the application is heavy on other functionality and user-interface interactions, this suddenly becomes a large detriment and can grind the application to a halt. Because of this, the core functionality of a component should operate on a separate worker thread, greatly increasing performance. From the user-interface perspective, this gives the user the option of implementing the component in a blocking or nonblocking fashion.

What this means is that the user's code will function in a synchronous or asynchronous manner with the component. When a blocking method, e.g., ftp.Get, is called, the next line of code in the user's application will not be executed until the function returns with the file or files, because the user-interface thread has been put to sleep while the operation is in progress on the worker thread. (Two levels of blocking are available, one which will occasionally wake up the user-interface thread to process queued events, and another that will keep the user-interface thread asleep until the worker thread has completed its tasks.)

Using a component in a blocking manner is the easiest way to write code, especially when there is no concern about other simultaneous code execution, and the other functionality of the application is dependent on the completion of the file transfer. The more complicated and efficient approach is to use the code in a nonblocking manner. This means that as soon as ftp.BeginGet is called, the next line of the application code will be executed while the FTP transfer is occurring on another thread. All user-interface events and any other code that needs to be executed continue to run while the file transfer is occurring. The notification of completion comes in the form of an EndGet event. Although the event itself occurs on a separate thread, any required communication with the user-interface thread, such as updating a progress bar, is marshaled automatically from the subordinate thread. Listing 1 demonstrates the differences and similarities of the two different approaches. Listing 2 demonstrates the usage of the Pop component from Mail for .NET to get all of the messages from a mail server account. Any time-consuming method should be both blocking and nonblocking.

Performance Is Not Enough
Performance alone is not enough for most applications, as they need to scale with changing requirements. While scalability with respect to greater load is accomplished through efficient design, such as support for nonblocking methods, scalability also refers to the inevitable expansion of requirements based on the evolution of the project. While project designs attempt to be comprehensive, areas that are not within the technology scope of a developer or designer often receive minimal planning. For example, while a prototype of an application may need to transfer a simple file, the actual project suddenly needs to be able to manage multiple files in multiple directories, and then check the directory structure of the target FTP server for specific information. Then the client tells you that their location requires a proxy. For a mail project, instead of sending simple text, a message must be constructed dynamically and include an attachment, plus be constructed in HTML that uses images. Suddenly what was easily handled by a couple of function calls to an API requires major rework on the lower level, or large amounts of rework to the data that is sent to the API. A component should foresee common challenges or be flexible enough that a complex implementation is still available.

A Specific Solution to a Complex Problem
Component vendors handle this type of problem in two ways. First, if there is a complex problem that requires a very specific solution, such as building an HTML mail message, the approach often requires a specific method or property to handle the situation. For example, the MessageStream object, which represents any e-mail message in Mail for .NET uses an overloaded contructor with an HTML file that can be passed as a constructor. Listing 3 demonstrates creating a MessageStream object with an HTML file and sending that file using SMTP. All the complexities of finding the HTML resources used to construct the HTML file, such as images, and creating the proper MIME parts from them, is handled automatically, without the component user even having to understand the rules of complex message structures. An example for FTP would be the need to get a listing from an FTP server so that some specific information can be retrieved about a file. Instead of having to write large amounts of parsing code, making a call to a List method returns a Listing collection that contains every listed item as a ListEntry object with properties representing the listed entry, as demonstrated in Listing 4.

Abstraction
The second approach to scalability is through abstraction. Visual Basic 6, by its nature, places limitations on powerful object-oriented solutions. With .NET, this limitation has been lifted, and components are perfectly positioned to leverage that capability. By using components that are built on abstract technology, users become empowered to solve problems by flexing the power of object-oriented design. For example, all PowerTCP .NET products are built on a foundation of streams. Users can choose to use the stream-derived classes in PowerTCP products or create their own. With Mail for .NET, the SMTP component will consume a stream-based object as one of the overloads for the Send method. This could be the MessageStream that is part of Mail for .NET or a user-defined stream that conforms to whatever specification is necessary for the user. In fact, the MessageStream is just a container for a number of separate streams that make up the whole message, all under the control of the user. A simpler example involves FTP. Listing 5 demonstrates a MemoryStream that contains some data. A user may want to take that data and place it as a file on an FTP server. Using one of the overloads for the Put method, this is easily done.

Adding stability and efficient performance while preserving the ability to expertly expand into niche technologies is a greatly overlooked aspect of components. By leveraging the capabilities of .NET ­ and through good design ­ components can provide significant performance improvements to an application, plus enable developers to handle ever-increasing requirements through the life cycle of their application.

About The Author
Alex Gladshtein is the product manager and director of the Product Group at Dart Communications. Dart is the author of PowerTCP components, designed to assist developers with the protocols used in Internet communications. Alex holds undergraduate and graduate degrees from the University of Michigan, and when not obsessing about components, he enjoys spending time with his wonderful wife and cheering on the Michigan Wolverines. alex.gladshtein@dart.com

	



Listing 1: Blocking vs a non-blocking call to retrieve files


Blocking [C#]
//Setting up the basic server specifications for login.  Defaults to
//Port 21 although the ServerPort property can be used if required.
ftp1.Server = "MyFTPServer";
ftp1.Username = "MyName";
ftp1.Password = "MyPass";


//Instantiate an array of file objects for pointing to the incoming files.
Dart.PowerTCP.Ftp.FtpFile[] files;
//Retrieve the files using the Get overload that specifies the remote
//directory, the search pattern, the local directory, and whether to
//retrieve all the files and subdirectories recursively
files = ftp1.Get("/home/directory1", "*", "C:\\Files", false);


//Iterate through the files array and report the status
foreach(Dart.PowerTCP.Ftp.FtpFile file in files)
   if (file.Exception == null)
      Debug.WriteLine(file.RemoteFileName +
	  " successfully retrieved");
Blocking [Visual Basic]
'Setting up the basic server specifications for login.  Defaults to
'Port 21 although the ServerPort property can be used if required.
Ftp1.Server = "MyFTPServer"
Ftp1.Username = "MyName"
Ftp1.Password = "MyPass"


'Instantiate an array of file objects for pointing to the incoming 'files.
Dim files() As Dart.PowerTCP.Ftp.FtpFile
'Retrieve the files using the Get overload that specifies the remote
'directory, the search pattern, the local directory, and whether to
'retrieve all the files and subdirectories recursively
files = Ftp1.Get("/home/directory1", "*", "C:\Files", False)


'Iterate through the files array and report the status
Dim file as Dart.PowerTCP.Ftp.FtpFile
For Each file In files
   if (file.Exception == Nothing)
      Debug.WriteLine(file.RemoteFileName +
	  " successfully retrieved.")
Next
Nonblocking [C#]
//Setting up the basic server specifications for login.  Defaults to
//Port 21 although the ServerPort property can be used if required.
ftp1.Server = "MyFTPServer";
ftp1.Username = "MyName";
ftp1.Password = "MyPass";


//Retrieve the files using the Get overload that specifies the remote
//directory, the search pattern, the local directory, whether to
//retrieve all the files and subdirectories recursively, and a state object
//that is anything that should be accessed in the EndGet event.


ftp1.BeginGet("/home/directory1", "*", "C:\\Files\\", false, null);


//The EndGet event that returns when the files finished transferring.
private void ftp1_EndGet(object sender, Dart.PowerTCP.Ftp.FileEventArgs e)
{
    if(e.Exception == null)
    {
       //Iterate through the files array and report the status
       foreach(Dart.PowerTCP.Ftp.FtpFile file in e.Files)
          Debug.WriteLine("Successfully got " +
file.LocalFileName);
    }
    else
        Debug.WriteLine("Error " +
e.Exception.Message);
}


Nonblocking [Visual Basic]
'Setting up the basic server specifications for login.  Defaults to
'Port 21 although the ServerPort property can be used if required.
Ftp1.Server = "MyFTPServer"
Ftp1.Username = "MyName"
Ftp1.Password = "MyPass"


'Retrieve the files using the Get overload that specifies the remote
'directory, the search pattern, the local directory, whether to
'retrieve all the files and subdirectories recursively, and a state object
'that is anything that should be accessed in the EndGet event.
Ftp1.BeginGet("/home/directory1", "*", "C:\Files\", False, Nothing)


Private Sub Ftp1_EndGet(ByVal sender As Object, ByVal e As
Dart.PowerTCP.Ftp.FileEventArgs) Handles Ftp1.EndPut


   If e.Exception Is Nothing Then
      Dim file As Dart.PowerTCP.Ftp.FtpFile


      'Iterate through the files array and report the status.
      For Each file In e.Files
         Debug.WriteLine("Successfully got " +
file.LocalFileName)
      Next
   
   Else
      Debug.WriteLine("Error " + e.Exception.Message);
   End If


End Sub




Listing 2: Blocking vs a nonblocking call to retrieve messages


Blocking [C#]
//Be sure the Pop component is configured to automatically retrieve
messages.
pop1.AutoGet = MessageSection.Complete;
// Login to the POP server.
pop1.Login("mail.myserver.com", "test", "pass");
//Display the number of messages retrieved to the user.
Debug.WriteLine(pop1.Messages.Length + " messages retrieved.");


// Iterate through the messages
foreach(PopMessage msg in pop1.Messages)
{
   // Display information about each Message
   Debug.Write("Message " + msg.Id);
   Debug.Write(" has " +
   msg.Message.Attachments.Count + " attachments.");
}


Blocking [Visual Basic]
' Be sure the Pop component is configured to automatically retrieve
messages.
Pop1.AutoGet = Complete
' Login to the POP server.
Pop1.Login("mail.myserver.com", "test", "pass")
' Display the number of messages retrieved to the user.
Debug.WriteLine(Pop1.Messages.Length & " messages retrieved.")
Dim Msg As PopMessage
For Each Msg In Pop1.Messages
   ' Display information about each Message
   Debug.Write("Message " + Msg.Id)
   Debug.Write(" has " + Msg.Message.Attachments.Count + " attachments.")
Next
Nonblocking [C#]
// Be sure the Pop component is configured to automatically retrieve
messages.
pop1.AutoGet = MessageSection.Complete;
// Login to the POP server.
pop1.BeginLogin("mail.myserver.com", "test", "pass", null);


//When the EndLogin event is raised, take some action (in this example,
the number of
//attachments for each message is simply displayed).
private void pop1_EndLogin(object sender, System.EventArgs e)
{
   // Iterate through the messages
   foreach(PopMessage msg in pop1.Messages)
   {
      // Display information about each Message
      Debug.Write("Message " + msg.Id);
      Debug.Write(" has " + msg.Attachments.Length +
	  "attachments.");
   }
}


Nonblocking [Visual Basic]
' Be sure the Pop component is configured to automatically retrieve
messages.
Pop1.AutoGet = Complete
' Login to the POP server.
Pop1.BeginLogin("mail.myserver.com", "test", "pass", Nothing)


' When the EndLogin event is raised, take some action
(in this example, the number of
' attachments for each message are simply displayed).
Private Sub Pop1_EndLogin(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Pop1.EndLogin
   Dim Msg As PopMessage
   For Each Msg In Pop1.Messages
      ' Display info about each Message
      Debug.Write("Message " + Msg.Id)
      Debug.Write(" has " + Msg.Attachments.Length +
	  "attachments.")
   Next
End Sub



Listing 3: Building and sending an HTML e-mail message


[C#]
// Create a MessageStream object, passing in the HTML file
MessageStream msg = new MessageStream("C:\\html\\index.htm");


// Set "To", "From", and "Subject"
msg.To.Add(new MailAddress("you@yourserver.com"));
msg.From = new MailAddress("me@myserver.com");
msg.Subject = "HTML Message";


// First set the server.
smtp1.Server = "mail.test.com";
// Then send.
smtp1.Send(msg);
[Visual Basic]
' Create a MessageStream object, passing in the HTML file
Dim msg As New MessageStream("C:\html\index.htm")


' Set "To", "From", and "Subject"
msg.To.Add(New MailAddress("you@yourserver.com"))
msg.From = New MailAddress("me@myserver.com")
msg.Subject = "HTML Message"


' First set the server.
Smtp1.Server = "mail.test.com"
' Then send.
Smtp1.Send(msg)


Listing 4: Returning a Listing from an FTP server and
 iterating through the items (blocking version only)

[C#]
ftp1.Server = "MyFTPServer";
ftp1.Username = "MyName";
ftp1.Password = "MyPass";

Dart.PowerTCP.Ftp.Listing listing;

//Get the list using the "*" wildcard and return a full listing
rather than just a named //listing.
This data can easily be used with the ListView control.
listing = ftp1.List("*", true);

// Iterate through each ListEntry
   foreach(Dart.PowerTCP.Ftp.ListEntry le in listing)
   {
      /*
      Display generic fields (fields that are available whether the
      listing returned is UNIX or DOS)
      */

      // Display file path and name
      Debug.WriteLine(le.Path + le.Name);

      // Display size
      Debug.WriteLine(le.Size);

      // Display timestap
      Debug.WriteLine(le.TimeStamp);

    }

[Visual Basic]
Ftp1.Server = "MyFTPServer"
Ftp1.Username = "MyName"
Ftp1.Password = "MyPass"

Dim listing As Dart.PowerTCP.Ftp.Listing

' Get the list using the "*" wildcard and return a full listing
 rather than just a named ' listing.
 This data can easily be used with the ListView control.
listing = Ftp1.List("*", true)

' Iterate through each ListEntry
   Dim le As Dart.PowerTCP.Ftp.ListEntry
   For Each le In listing
  
      ' Display generic fields (fields that are available whether the
      ' listing returned is UNIX or DOS)

      ' Display file path and name
      Debug.WriteLine(le.Path + le.Name)

      ' Display size
      Debug.WriteLine(le.Size)

      ' Display timestamp
      Debug.WriteLine(le.TimeStamp)
   Next

Listing 5: Moving data from a MemoryStream directly to a
 remote FTP server (blocking version only)

[C#]
ftp1.Server = "MyFTPServer";
ftp1.Username = "MyName";
ftp1.Password = "MyPass";

Dart.PowerTCP.Ftp.FtpFile file;

System.IO.MemoryStream stream1 = new System.IO.MemoryStream();

byte[] data = System.Text.Encoding.ASCII.GetBytes("Some data");
stream1.Write(data, 0, data.Length);

//Place the stream data as a file called filefromstream.txt to the remote server.
file = ftp1.Put(stream1, "filefromstream.txt");

Debug.WriteLine("Stream now on server as " + file.RemoteFileName);
Debug.WriteLine("Data transferred " + file.Count);



[Visual Basic]
Ftp1.Server = "MyFTPServer"
Ftp1.Username = "MyName"
Ftp1.Password = "Mypass"

Dim file As Dart.PowerTCP.Ftp.FtpFile

Dim stream1 As New System.IO.MemoryStream()

Dim data() As Byte
data = System.Text.Encoding.ASCII.GetBytes("Some data")
stream1.Write(data, 0, data.Length)

' Place the stream data as a file called filefromstream.txt to the remote server.
file = Ftp1.Put(stream1, "filefromstream.txt")

Debug.WriteLine("Stream now on server as " + file.RemoteFileName)
Debug.WriteLine("Data transferred " + file.Count)

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.

  E-mail: info@sys-con.com