Sunday, July 20, 2008

Design Patterns

A Design pattern is a general reusable solution to a commonly occurring problem in software design. Each pattern has an intention that try to solve a common problem. You need to know the intention of each pattern also the problem they solve then if you come up with that issue you need to apply that pattern to solve the problem. I have seen so many programmers have no idea about design pattern if you are part of those people I should say you are not classified as senior programmer. I believe a senior programmer need to know these patterns. I could find a big gap in terms of design patterns knowledge between Java programmers and C# programmers. Java programmers know design patterns very well however; C# programmers hardly know them and use them. I had a lot of co-workers writing horrible codes just because they do not know how to solve a common problem. I wish I had a chance to convince them apart from technologies like ASP.NET, WCF, WF, and etc you need much more deep understanding about coding and design patterns otherwise you may know amazing technology but you do not have enough capacity to use them correctly!

This is why I decided to start writing about design patterns and give you some idea about them and also share my experiences over some of these patterns.

I hope you can find this useful. Please follow my post about design patterns and help me to correct myself also share your experiences with me.

A sample of Multi Thread programming in Asp.net

Download The Source Code

Please See the clip

Understanding the issue

I was assigned to implement a part of web application that was responsible for calling a java based application. There were some concerns about this part of application as follows:

1. The java based application was supposed to create a file that I had to process that file and show to user

2. A user could run the process from a page but after running he was not able to run it again (since the process took 3 min)

3. When application was running no one else could run application but they were supposed to see that application was run by someone else.

4. Page should be updated every 10 seconds to check whether the process is finished or not

5. When process is finished a file was generated I saved the file output in a temporary table (for simplicity I will save this data in a static variable)

To simulate this let's say we have an application called ExternalApp (a console application) to create a file but creating that file may take 3 min.

class Program

{

static void Main(string[] args)

{

if (args.Length == 1)

{

System.Threading.Thread.Sleep(180000);

StreamWriter w = new StreamWriter(args[0],false);

for(int i=0;i<10;i++)

w.WriteLine(" A Sample Data :"+ i);

w.Close();

}

}

}

Now we should create a web application to call this application by using Multi thread programming.

First I create a library to run the external application in a separate method. We need to check whether the process of running is finished or not. I created two classes one was Director another one was Builder.

Take a look at Director Class:

public class Director

{

Builder builder;

string userName;

public static List<string> Result = new List<string>();

public AlgorithemStatus Constract(Builder builder,string user)

{

this.userName = user;

if (HttpContext.Current.Application["athread"] == null)

{

this.builder = builder;

ThreadStart ts = new ThreadStart(RunThread);

Thread t = new Thread(ts);

t.Start();

HttpContext.Current.Application["athread"] = t;

return AlgorithemStatus.AlgorithemRunSuccessful;

}

else

return AlgorithemStatus.AlgorithemWasRunning;

}

public static bool IsAlgorithemRunning

{

get

{

if (HttpContext.Current.Application["athread"] == null)

return false;

else

{

Thread t = HttpContext.Current.Application["athread"] as Thread;

if (!t.IsAlive)

{

HttpContext.Current.Application.Remove("athread");

return false;

}

else

return true;

}

}

}

private void RunThread()

{

try

{

builder.PrepareHistoryFolder(userName);

builder.RunExternalAll();

Result = builder.LoadResult();

}

catch (Exception ex)

{

System.IO.StreamWriter w = new System.IO.StreamWriter(System.Configuration.ConfigurationManager.AppSettings["HistoryPath"] + "\\err.log", true);

w.WriteLine("Date Time : " + DateTime.Now);

w.WriteLine(ex.ToString());

w.Close();

}

}

}

In This class construct create a new thread and run RunThread method in it. IsAlgorithemRunning Property Check whether the running process has been started. Finally RunThread method calls different method in builder class and retrieve the result.

Check the Builder Class:

using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

namespace AlgorithemManagment

{

public class Builder

{

private string pathResultExteranlApp;

private string pathExternalApp;

private StreamWriter statswriter;

internal void PrepareHistoryFolder(string userName)

{

//Check for History folder first

string path = System.Configuration.ConfigurationManager.AppSettings["HistoryPath"];

if (!Directory.Exists(path))

Directory.CreateDirectory(path);

string foldername = string.Format("{0:d4}_{1:d2}_{2:d2}_{3:d2}_{4:d2}_{5:d2}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);

string HistoryPaht = path + "\\" + foldername;

Directory.CreateDirectory(HistoryPaht);

pathResultExteranlApp = HistoryPaht + "\\Result.CSV";

pathExternalApp = System.Configuration.ConfigurationManager.AppSettings["ExteranlAppPath"];

string pathstats = HistoryPaht + "\\stats.log";

statswriter = new StreamWriter(pathstats, true);

statswriter.WriteLine("User who runned the Algorithem: " + userName);

statswriter.WriteLine("Starting DateTime: " + DateTime.Now.ToString());

}

internal void RunExternalAll()

{

System.Diagnostics.Process prc = new System.Diagnostics.Process();

prc.StartInfo.FileName = pathExternalApp;

prc.StartInfo.Arguments = pathResultExteranlApp;

if (prc.Start())

statswriter.WriteLine("Allocatoin application has just started at " + DateTime.Now.ToString());

prc.WaitForExit();

}

internal List<string> LoadResult()

{

List<string> strs = new List<string>();

StreamReader r = File.OpenText(pathResultExteranlApp);

string data = r.ReadLine();

while (data != null)

{

strs.Add(data);

data = r.ReadLine();

}

statswriter.WriteLine("Temptable was filled successfully DateTime:" + DateTime.Now.ToString());

statswriter.Close();

return strs;

}

}

}

PrepareHistoryFolder creates a history folder to log and also save the result. RunExternalApp runs the External application and wait until it result is generated. LoadResult read the file generated by external app and process it.

Check the Page code:

protected void Page_Load(object sender, EventArgs e)

{

if (Director.IsAlgorithemRunning)

{

PrepareDisableMode();

}

else

{

PrepareEnableMode();

}

}

private void PrepareEnableMode()

{

ButtonRunAlgorithm.Enabled = true;

imgwait.Visible = false;

DataListResult.Visible = true;

if (Director.Result.Count > 0)

{

DataListResult.DataSource = Director.Result;

DataListResult.DataBind();

}

}

private void PrepareDisableMode()

{

ButtonRunAlgorithm.Enabled = false;

imgwait.Visible = true;

ClientScript.RegisterStartupScript(typeof(string), "mykey", "<script>RefreshPageinSecond();</script>");

DataListResult.Visible = false;

}

protected void ButtonRunAlgorithm_Click(object sender, EventArgs e)

{

PrepareDisableMode();

AlgorithemManagment.Director d = new AlgorithemManagment.Director();

AlgorithemManagment.Builder b = new AlgorithemManagment.Builder();

d.Constract(b,"The User Name");

}

When page is load if algorithm is running we are disabling the button and showing an image indicating we are middle of the process. If algorithm is not running we enable the button and also show the result. When button is clicked we disable button and show the image and also run the algorithm.

Please see the clip to understand the code better.
Please download the source code and also go to web.config file and update the ExteranlAppPath which is the address of ExternalApp exe file and update the HistoryPath which is the address of history folder to log information.