Now, I’m joining with a huge and complex intranet project (a collaboration between ASP classic, ASP.NET 1.1, ASP.NET 2.0, and now ASP.NET 4.0) to creating document workflow management for intranet. I built with ASP.NET 4.0 with C# as foundation programming language.
Existing applications contains :
- FoxPro application for complex distributed custom desktop application. Installed in more than 15 branch office in Indonesia.
- ASP classic application using VBScript as core application for user management, core services, and a lot of custom application for running their business process..
- ASP.NET 1.1 application using Visual Basic .NET for purchasing and HRD application
- ASP.NET 2.0 application using Visual Basic .NET for outsourcing and payroll
and now, ASP.NET 4.0 application using C# for document workflow management (I created it). This is interesting project because of the various environment and how to collaborate and integrate with their existing apps. Some of experiences that I have found when integrating ASP.NET 4.0 into their existing environment is very interesting.
My job are about:
- Integrating and customizing their existing ASP.NET application thus (if possible) migrating from ASP.NET 1.1/2.0 into ASP.NET 4.0.
- Creating service layer to centralize all functionality into one door.
- Code refactoring for existing apps built with ASP.NET 1.1/2.0 (haiyaah…) and they want to migrate to .NET 4.0
- Upgrading application performance
- Integrating logging for every critical process
- and many…
Fyuuh… this is the most challenging project in my life. They give me an access for their source code and permit me to do anything.
I will share some of my experiences when dealing with that kind of project.
a) Cookies sharing. Their core intranet using ASP classic for authenticating user based on SQL Server 2000 database. Then the engine will create some of cookies. Their existing ASP.NET 1.1 and 2.0 application using client side JavaScript for retrieving cookies and then passing to ASP.NET application. I don’t know why their programmer do the tricky way like that. :) Because the easiest way it just using this code :
HttpCookie cookie = Request.Cookies[“cookieName”];
or to make it easy, just make a function in an helper class like this:
public static string GetCookieValue(string cookieName)
{
HttpCookieCollection cookies = HttpContext.Current.Request.Cookies;
string cookieValue = string.Empty;
for (int n = 0; n < cookies.Count; n++)
{
HttpCookie cookie = cookies
;
if (HttpUtility.UrlDecode(cookie.Name) == cookieName)
{
cookieValue = HttpUtility.UrlDecode(cookie.Value);
}
}
return cookieValue.Trim();
}
and access it by calling :
string userId = Helper.GetCookieValue(“userid”);
No need to do tricky way by using JavaScript again. Cookie is a small information that sent by browser into client machine and can be accessed easily by ASP.NET by using HttpCookie class.
b) They don’t use CSS for styling the design! Whew… this is horrible! Even the ASP.NET server control has their own functionality in styling, but don’t use it in production! It’s better to use CSS instead. I have to refactoring ALL custom styles and put it into one single CSS for consistency. Doing this for every files is really painful. So I create a tool for extracting style tag in every server control by using HtmlAgilityPack component then grouping it by server control type. For example, if the style is in <asp:Label … /> tag, then the tool will provide grouping :
LABEL
font-family: Arial; font-size: 12pt; color: Blue
TEXTBOX
font-family: Arial; font-size: 12pt; border: 1px solid Gray;
etc…etc…etc…
This is useful for me to identify every server control in every pages and put it into single CSS file and then change the style tag into class tag.
c) Their application using ALOT of appSettings! More than 30 tags in appSettings. And the worst is their application read and write the value in appSettings which mean they have to touch web.config every time! This is very bad in performance since touching web.config will make ASP.NET worker process recycling again.
The solution is moving ALL appSettings configuration tags into external files by using this technique:
<appSettings configSource=”customAppSettings.config” />
The customAppSettings.config files contains existing appSettings tags. This kind of technique would prevent ASP.NET application touching web.config again and again and also boost the performance.
d) Their existing ASP.NET application using Page and ALWAYS page. No base page, no web user control, and always based on Page. For checking authenticating, they copied the same code between page again and again. And guess, there are more than 500 pages!! Walaahh… No reusability. This is a big refactoring…!
After analyzing, the solution is by create a base class which contains shared functionality between pages (authentication checking, authorization checking, language detecting, currency detecting, etc etc) into one library. And make it available to others by referencing the library and recompile. Yes, there is no other way to do except recompile. :( Using IoC or dependency injection is no use here because of their application is unstructured. :(
d) Their application sending mail a lot. And use Persist component for ASP classic and ASP.NET. But the code is the same as before. Copied in every page again and again. Another refactoring again … :( Their process is synchronous, so every time a user click Send button, then the browser will ‘hang’ for a second (or sometimes for a minute, especially if the mail server is down) because of waiting for sending email process.
After doing meeting with their executive, they want me to make a mechanism for sending email as ‘fire and forget’. So no need to trap the error message. Just send the email, and just like that (notification only).
Solution: I create a custom function to utilize asynchronous. This feature exist in ASP.NET 2.0, so the existing ASP.NET 1.1 apps can’t utilize this function. In my helper class, I wrote the code like this:
public static void SendEmailNotification(string to, string template, string subject)
{
MailMessage message = new MailMessage();
message.From = new MailAddress(ConfigurationManager.AppSettings["EmailNotifier"].ToString()); // no-reply@domain.com
message.To.Add(new MailAddress(to));
message.Subject = subject;
message.Body = template;
message.IsBodyHtml = true;
using (new SynchronizationContextSwitcher())
{
var smtpClient = new SmtpClient();
smtpClient.SendCompleted += (sender, e) =>
{
// do nothing
};
smtpClient.SendAsync(message, null);
}
}
Sending email asynchronously STILL waiting for UI thread for updating, so we have to use ANOTHER thread (not ASP.NET thread). So, I utilize a class which named “SynchronizationContextSwitcher” as you can see above.
This class is useful to change the process context. So, the sending email process would not use ASP.NET thread.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;
namespace docflow
{
public class SynchronizationContextSwitcher : IDisposable
{
private ExecutionContext _executionContext;
private readonly SynchronizationContext _oldContext;
private readonly SynchronizationContext _newContext;
public SynchronizationContextSwitcher()
: this(new SynchronizationContext())
{
}
public SynchronizationContextSwitcher(SynchronizationContext context)
{
_newContext = context;
_executionContext = Thread.CurrentThread.ExecutionContext;
_oldContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(context);
}
public void Dispose()
{
if (null != _executionContext)
{
if (_executionContext != Thread.CurrentThread.ExecutionContext)
throw new InvalidOperationException("Dispose called on wrong thread.");
if (_newContext != SynchronizationContext.Current)
throw new InvalidOperationException("The SynchronizationContext has changed.");
SynchronizationContext.SetSynchronizationContext(_oldContext);
_executionContext = null;
}
}
}
}
You can use it in your project to make email sending become ‘fire and forget’. But just do it IF and ONLY IF that suited your requirements.
Stay tune for the next update from me.
Hopefully helpful for you.
Cheers…