Introduction
Windows Workflow Foundation, with its set of activities, provides an easy and visual way to build applications. But, what if the available activities are not suitable for our needs and we don't want to use a CodeActivity? Let's create a custom activity; below, you can find a very basic one for file operations over FTP.
Using the code
The FTP activity supports upload/delete/list of files from an FTP server. You can either add the project George.WWF.Activities.FTP to your solution, or add the activity to your toolbox (Tools-> Choose toolbox items) in order to use it.
Once dropped on the design windows, the activity will warn the user that the required properties are not set:
Validation is managed by the FTPActivityValidator
class that inherits from ActivityValidator
.
The custom class FTPActivityValidator
can now be used to validate the activity class:
[ActivityValidator(typeof(FTPActivityValidator))]
public partial class FTPActivity : System.Workflow.ComponentModel.Activity
We can now decide how we want to bind the properties; we can do it at design time:
or programmatically; maybe the values can come from a config file:
private void initFTPActivity_ExecuteCode(object sender, EventArgs e)
{
this.ftpActivity1_FTPServer1 = "10.20.100.65";
this.ftpActivity1_FTPUserName1 = "Giorgio";
...
}
The activity is easily customizable, so support to other operations can be added extending the FTP core classes:
Here is the code for the FTP Manager:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
namespace George.WWF.Activities.FTP
{
public enum OperationType
{
Upload = 0,
Delete = 1 ,
List = 2
}
public class FtpManager
{
#region Class members
OperationType operation;
FtpEntity ftpEntity;
FtpWebRequest ftpWebRequest;
Stream ftpStream;
#endregion
#region Members modifiers
public OperationType Operation
{
get { return operation; }
}
#endregion
public FtpManager(FtpEntity ftpEntity)
{
this.ftpEntity = ftpEntity;
}
public bool CreateFtpFile(string file, string destinationFolder)
{
FtpWebRequest request = null;
try
{
FileInfo fileInfo = new FileInfo(file);
request = (FtpWebRequest)WebRequest.Create(string.Format("{0}/{1}/{2}",
ftpEntity.FtpServer, destinationFolder, fileInfo.Name));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(ftpEntity.Username,
ftpEntity.Password);
request.UseBinary=true;
request.KeepAlive=false;
request.ContentLength = fileInfo.Length;
int buffLength = 512;
byte[] buff = new byte[buffLength];
int contentLen;
FileStream fs = fileInfo.OpenRead();
try
{
Stream strm = request.GetRequestStream();
contentLen = fs.Read(buff, 0, buffLength);
while (contentLen != 0)
{
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
strm.Close();
fs.Close();
strm.Dispose();
fs.Dispose();
}
catch(Exception ex)
{
return false;
}
}
catch
{
return false;
}
finally
{
}
return true;
}
public bool DeleteFtpFiles(string folder)
{
try
{
string[] directoryFiles = ListFtpFiles(folder);
for (int i = 0; i < directoryFiles.Length; i++)
{
if (!string.IsNullOrEmpty(directoryFiles[i]))
DeleteFtpFile(directoryFiles[i], folder);
}
}
catch
{
return false;
}
return true;
}
public string[] ListFtpFiles(string folder)
{
ftpWebRequest = (FtpWebRequest)WebRequest.Create(
new Uri(string.Format("{0}/{1}", ftpEntity.FtpServer, folder)));
this.operation = OperationType.List;
ChooseOperationType();
SettingFtpManager();
FtpWebResponse ftpWebResponse =
(FtpWebResponse)ftpWebRequest.GetResponse();
ftpStream = null;
ftpStream = ftpWebResponse.GetResponseStream();
StreamReader reader = new StreamReader(ftpStream);
string[] directoryFiles = Regex.Split(
(reader.ReadToEnd().Replace(string.Format("{0}/",folder),
string.Empty)), Environment.NewLine);
Console.WriteLine("Directory List Complete, status {0}",
ftpWebResponse.StatusDescription);
reader.Close();
ftpWebResponse.Close();
return directoryFiles;
}
private bool DeleteFtpFile(string fileName, string folder)
{
ftpWebRequest = (FtpWebRequest)WebRequest.Create(
new Uri(string.Format("{0}/{1}/{2}",
ftpEntity.FtpServer, folder, fileName)));
this.operation = OperationType.Delete;
ChooseOperationType();
SettingFtpManager();
FtpWebResponse ftpWebResponse =
(FtpWebResponse)ftpWebRequest.GetResponse();
Console.WriteLine("Delete status: {0}",
ftpWebResponse.StatusDescription);
ftpWebResponse.Close();
return true;
}
private void SettingFtpManager()
{
ftpWebRequest.Credentials =
new NetworkCredential(ftpEntity.Username, ftpEntity.Password);
ftpWebRequest.KeepAlive = false;
ftpWebRequest.UseBinary = true;
}
private void ChooseOperationType()
{
switch(operation)
{
case OperationType.Upload:
ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile;
break;
case OperationType.Delete:
ftpWebRequest.Method = WebRequestMethods.Ftp.DeleteFile;
break;
case OperationType.List:
ftpWebRequest.Method = WebRequestMethods.Ftp.ListDirectory;
break;
}
}
}
}
Here is the code for FTPEntity
:
using System;
using System.Collections.Generic;
using System.Text;
namespace George.WWF.Activities.FTP
{
public class FtpEntity
{
string ftpServer, username, password;
public string FtpServer
{
get { return ftpServer; }
}
public string Username
{
get { return username; }
}
public string Password
{
get { return password; }
}
public FtpEntity(string ftpServer, string username, string password)
{
this.ftpServer = FormatFtpServer(ftpServer);
this.username = username;
this.password = password;
}
private string FormatFtpServer(string ftpServer)
{
if(!ftpServer.StartsWith("ftp://"))
ftpServer = "ftp://" + ftpServer;
return ftpServer;
}
}
}
Points of interest
The support to design-time is a feature that can make a big difference during development and add extra value to classic class libraries. Windows Workflow Foundation provides great instruments to build applications but lack in controls (most of them are not free); this article provides the first steps to build your own custom activity.