Wednesday, September 26, 2007

ASP.NET Impersonate Application Pool Account To Access Database

In an ASP.NET web application, we use a fixed account to connect to SQL server. Although we could encrypt the user name and password in the connection string, admin still suggested to use Windows Authentication so that there's no any user credential information stored in the file system.

One simple way to achieve that is create an application pool for the web app with a service account, and grant the permission for that service account inside the SQL server, then wrap all ADO.NET code inside a System.Web.Hosting.HostingEnvironment.Impersonate() block:
    using (System.Web.Hosting.HostingEnvironment.Impersonate())
{
using (SqlConnection connection = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(spStoredProcedureName, connection);
//... other ADO.NET code
}
}

Thursday, August 02, 2007

Array.ConvertAll and List.ConvertAll

Just noticed this handy method:
using System;
using System.Collections.Generic;

class Test
{
static void Main()
{
int[] numbers = new int[] { 65, 66, 67 };
char[] chars = Array.ConvertAll(numbers, delegate(int number)
{ return (char)number; });

//Print "ABC"
Array.ForEach(chars, delegate(char ch) { Console.Write(ch); });

Console.WriteLine();
List<int> numberList = new List<int>(numbers);
List<char> charList = numberList.ConvertAll(delegate(int number)
{ return (char)number; });

//Print "ABC"
charList.ForEach(delegate(char ch) { Console.Write(ch); });

Console.Read();
}
}

Saturday, July 07, 2007

Singleton Pattern - A Logger Example

The idea of a singleton is to have a class that has only one instance and a global point of access to the instance. Following code is a simple singleton C# implementation:
public class Singleton
{
private static Singleton instance = new Singleton();

private Singleton() { }

public static Singleton GetInstance()
{
return instance;
}
}
The above implementation creates a new static instance during the class initialization. The singleton instance is retrieved by a static GetInstance method. An alternative is expose the singleton object by read-only property without a setter. The code needs a little bit change if a lazy creation is required:
public class Singleton
{
private static volatile Singleton instance;
private static object syncObj = new Object();

private Singleton() {}

public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncObj)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
SyncLock in above code is for thread-safe consideration. In addition, the singleton class could be sealed if you don't like people to inherit from it and break the singleton principle.

Singleton pattern is very useful in some use cases. Here's a real life example. I needed a file logger for a simple web service application used in a small company. Enterprise library, logging application block, and even log4Net are just too heavy for such small application. In addition, the client is rejective to third-party tools.

I decided to write a file logger by myself. A stream writer was used, but I don't want multiple instances of stream writers being created and used at the same time. The IO is expensive and multiple accesses to a single file is not safe. Singleton came to play and only one instance of logger and one stream writer is created:
using System;
using System.IO;
using System.Configuration;
using System.Diagnostics;

class Program
{
static void Main(string[] args)
{
Logger.WriteLog("Jus a test.");
Logger.WriteLog(LogSeverity.Info, "Another test.");
Console.Read();
}
}

// A helper static class to get access the logger
public static class Logger
{
private static readonly FileLogger _logger = FileLogger.GetInstance();

public static void WriteLog(String log)
{
_logger.Write(log);
}
public static void WriteLog(LogSeverity logLevel, String log)
{
_logger.Write(logLevel, log);
}
}

// Enumeration for log severity
public enum LogSeverity { Error, Warning, Info, Debug };

// FileLogger class with Singleton pattern
internal sealed class FileLogger : IDisposable
{
private static FileLogger _instance = null; // Singleton object
private static object _syncLock = new object(); // Sync lock

internal StreamWriter _writer; // Stream writer writting to the log file
internal string _path = "C:\\Logs"; // Default folder
internal string _absolutePath; // C:\Logs\MachineName.log
internal string _fileName; // MachineName.log
internal int _logFileSize = 2; // New log file when its size bigger than 2 MB
internal LogSeverity _defaultSeverity = LogSeverity.Error; // Default severity

// Private constructor
private FileLogger()
{
GetConfigSettings();

_fileName = string.IsNullOrEmpty(Environment.MachineName) ?
"LogFile.log" : Environment.MachineName + ".log";
_absolutePath = _path + "\\" + _fileName;

if (!Directory.Exists(_path))
{
Directory.CreateDirectory(_path);
}

_writer = new StreamWriter(_absolutePath, true);
}

// Public static method to get Singleton object
public static FileLogger GetInstance()
{
if (_instance == null)
{
lock (_syncLock)
{
if (_instance == null)
{
_instance = new FileLogger();
}
}
}
return _instance;
}

// Check if the log file exceeds the maximum size
private void CheckFileSize()
{
if (_writer.BaseStream.Length >= (_logFileSize * 1024 * 1024))
{
this.Dispose();
string backupFile = string.Format("{0}\\{1}-{2}.log",
_path, Environment.MachineName, DateTime.Now.ToString("yyyyMMdd-HHmmsss"));
try
{
while (File.Exists(backupFile))
{
backupFile = backupFile.Replace(".log", "_1.log");
}
File.Move(_absolutePath, backupFile);
this.Dispose();
_writer = new StreamWriter(_absolutePath, true);
}
catch (Exception ex)
{
WriteToEventLog(ex.Message);
}
}
}

// Get default setting from configuraiton file
private void GetConfigSettings()
{
string appSetting = ConfigurationManager.AppSettings["FileLogger_Path"];
if (!string.IsNullOrEmpty(appSetting))
{
_path = appSetting;
}
int maxFileSize;
appSetting = ConfigurationManager.AppSettings["FileLogger_MaxFileSizeInMB"];
if (!string.IsNullOrEmpty(appSetting) && int.TryParse(appSetting, out maxFileSize))
{
_logFileSize = maxFileSize;
}
appSetting = ConfigurationManager.AppSettings["FileLogger_DefaultSeverity"];
if (!string.IsNullOrEmpty(appSetting))
{
try
{
_defaultSeverity = (LogSeverity)Enum.Parse(typeof(LogSeverity), appSetting);
}
catch (Exception ex)
{
WriteToEventLog(ex.Message);
}
}
}

// Clean up the stream writer
public void Dispose()
{
lock (_syncLock)
{
if (_writer != null)
{
_writer.Dispose();
}
}
}

// Write with default log severity
public void Write(string msg)
{
Write(_defaultSeverity, msg);
}

// Write message to log file
public void Write(LogSeverity logSeverity, string msg)
{
lock (_syncLock)
{
try
{
if (_writer.BaseStream == null)
{
_writer = new StreamWriter(_absolutePath, true);
}
CheckFileSize();
_writer.WriteLine("[{0}] at {1} -- {2}",
logSeverity.ToString(), DateTime.Now.ToString(), msg);
_writer.Flush();
}
catch (Exception ex)
{
WriteToEventLog(ex.Message);
}
}
}

private void WriteToEventLog(string message)
{
string source = "Logger";
string log = "Application";

try
{
if (!EventLog.SourceExists(source))
EventLog.CreateEventSource(source, log);

EventLog.WriteEntry(source, message, EventLogEntryType.Error);
}
catch { }
}
}

Thursday, May 10, 2007

.NET Interface Serialization Issue

Interface can not be serialized or de-serialized. That's because interface is just a contract not detailed implementation. However, in some cases, we do want to expose interface so objects are not restricted by a specific type.

Look at a simple example:
    public interface IPriceable
{
double GetPrice();
}
[Serializable]
public class ProductA : IPriceable
{
public double GetPrice()
{
return 123.345;
}
}

[Serializable]
public class ProductB : IPriceable
{
public double GetPrice()
{
return 234.567;
}
}

[Serializable]
public class PromotionPackage
{
public List<IPriceable> Items = new List<IPriceable>();
double GetPromotePrice()
{
//return PromotionCaculator.GetPrice(_items);
return 567.89; ;
}
}
The PromotionPackage contains a list of IPriceable objects (ProductA, ProductB etc.). Everything is okay except that an exception will be thrown when we serialize the PromotionPackage object:
{"Cannot serialize member PromotionPackage.Items of type IPriceable because it is an interface."}
A workaround would be wrapping the interface to an abstract class:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public class Program
{
public interface IPriceable
{
double GetPrice();
}

[XmlInclude(typeof(ProductA))]
[XmlInclude(typeof(ProductB))]
public abstract class ProductBase : IPriceable
{
public abstract double GetPrice();
}

[Serializable]
public class ProductA : ProductBase
{
public override double GetPrice()
{
return 123.345;
}
}

[Serializable]
public class ProductB : ProductBase
{
public override double GetPrice()
{
return 234.567;
}
}

[Serializable]
public class PromotionPackage
{
public List<ProductBase> Items = new List<ProductBase>();
double GetPromotePrice()
{
//return PromotionCaculator.GetPrice(_items);
return 567.89; ;
}
}

static void Main(string[] args)
{
PromotionPackage package = new PromotionPackage();
package.Items.Add(new ProductA());
package.Items.Add(new ProductB());

string xmlString = string.Empty;
XmlSerializer serializer = new XmlSerializer(typeof(PromotionPackage));

// Serialize
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, package);
xmlString = sw.ToString();
}

// De-serialize
PromotionPackage newPackage = null;
using (StringReader sr = new StringReader(xmlString))
{
XmlTextReader reader = new XmlTextReader(sr);
newPackage = (PromotionPackage)serializer.Deserialize(reader);
}

Console.WriteLine("Done.");
Console.Read();
}
}

Saturday, May 05, 2007

A Managed Way To Serialize & Deserialze DateTime field

public class MyEvent
{
public string Name;

[XmlIgnore]
public DateTime Time;
[XmlElement("Time")]
public string TimeString
{
get { return Time.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture); }
set { Time = DateTime.ParseExact(value, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture); }
}

public MyEvent() { }
public MyEvent(string name, DateTime time)
{
Name = name;
Time = time;
}

static void Main(string[] args)
{
MyEvent evt = new MyEvent("event1", new DateTime(2007, 5, 5));

XmlSerializer serializer = new XmlSerializer(typeof(MyEvent));
TextWriter writer = new StreamWriter(@"c:\event.xml");
serializer.Serialize(writer, evt);
writer.Close();
}
}
Here TimeString is exposed as a public property. Can we make it private? The answer is yes, but it requires the DataContract new in .NET 3.0:
[DataContract]
public class MyEvent
{
[DataMember]
public string Name;

public DateTime Time;

[DataMember(Name="Time") ]
private string TimeString
{
get { return Time.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture); }
set { Time = DateTime.ParseExact(value, "yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture); }
}

public MyEvent(string name, DateTime time)
{
Name = name;
Time = time;
}

static void Main(string[] args)
{
MyEvent evt = new MyEvent("event1", new DateTime(2007, 5, 5));

DataContractSerializer serializer = new DataContractSerializer(typeof(MyEvent));
XmlWriter writer = XmlWriter.Create(@"c:\event.xml");
serializer.WriteObject(writer, evt);
writer.Close();
}
}

Monday, April 16, 2007

.NET Class Library Get Configuration File

A class library is used by different kind of applications, like a test console, a WinForm or Web applications. And the class library needs to read a configuration file at run time. How to do that? The common way is copy the configuration file to the folder where the class library assembly is sitting. Then the configuration file can be located by reflection at run time. But this doesn't work in ASP.NET web site environment because of the .NET dynamic compilation process. One resolution is to handle the path differently in web environment. Let's say if We have a convention of copying the configuration file to the root folder of web site, then the code to retrieve the full path of configuration file is like:
    public static string GetConfigurationFile(string fileName)
{
string asmLocation = Assembly.GetExecutingAssembly().Location;
string asmFolder = asmLocation.Substring(0, asmLocation.LastIndexOf(@"\"));
string fileFullPath = asmFolder + @"\" + fileName;

// Fix the temporary folder issue when running in web environment
if (fileFullPath.Contains(@"\Microsoft.NET\Framework"))
{
fileFullPath = System.Web.Hosting.HostingEnvironment.MapPath("/") + fileName;
}

return fileFullPath;
}
(Updated 2008/2) Another easy way to get configuration file is using AppDomain.CurrentDomain.SetupInformation.ConfigurationFile property which maps to app.config for Cosole/WinForm applications, and maps to web.config in ASP.NET.

Tuesday, April 03, 2007

StringDictionary, NameValueCollection, Dictionary<TKey, TValue> and KeyedCollection<TKey, TItem>

How to store key/value data in .NET?

DictionaryEntry in .NET 1.0 and generic KeyValuePair<TKey, TValue> in .NET 2.0 both store key/value pair inside a struct (not class). How about many KeyValue pairs? One solution is to put Key/value objects inside a collection like Generic List. But there's a major problem of such use: search by key is expensive and you need to loop through the whole collection. HashTable is fast in search but it's limited to string/object pair, and it's not strongly typed.

Are there any out-of-box strongly-typed collections for key/value objects?

If the key and value both are strings, StringDictionary and NameValueCollection are options. They have been available since .NET 1.0 and both are under System.Collections.Specialized name space. The difference is that NameValueCollection can have duplicate keys (multiple values for the same key are allowed).

In .NET 2.0 we have generic Dictionary<TKey, TValue> that is efficient and easy to use. More importantly the key and value objects can be of any types, and are strongly typed. But one issue of generic dictionary is that it can not be serialized directly. Paul Welter provided a SerializableDictionary solution for the problem but it's just a workaround. The other drawback of generic dictionary is that index accessing is not supported.

Generic KeyedCollection<TKey, TItem> under System.Collections.ObjectModel name space does not have those two issues on generic dictionary. KeyedCollection<TKey, TItem> supports index and key search, just like a hybrid version of generic List and Dictionary. But KeyedCollection<TKey, TItem> is just an abstract class, and you have to inherit from it and implement at least one abstract method to tell what's the key in each object inside the collection:
protected abstract TKey GetKeyForItem(TItem item);
Following is a demo of how to use KeyedCollection, where the EmployeeCollection uses employee number as a key:
using System;
using System.Text;
using System.Collections.ObjectModel;

class Program
{
static void Main()
{
EmployeeCollection ec = new EmployeeCollection();
ec.Add(new Employee("123456", "Rob", 40));
ec.Add(new Employee("234567", "Steve", 45));

bool contained = ec.Contains("123456");
ec["123456"].Age = 44;
ec[1].Age = 45;

Console.Read();
}
public class Employee
{
public Employee(string empNumber, string name, int age)
{
EmployeeNumber = empNumber;
Name = name;
Age = age;
}
public string EmployeeNumber;
public string Name;
public int Age;
// other properties and memebers
}

public class EmployeeCollection : KeyedCollection<string, Employee>
{
protected override string GetKeyForItem(Employee item)
{
return item.EmployeeNumber;
}
}
}

Thursday, March 29, 2007

Thread Safety In .NET Collections

Most .NET collections are not thread-safe. Take generic list as an example, the MSDN description is as following:

Thread Safety

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

A List<T> can support multiple readers concurrently, as long as the collection is not modified. Enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with one or more write accesses, the only way to ensure thread safety is to lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

So we need to implement read/write locks in multi-threaded environment when the list objects are modified. That's a lot of extra work and is prone to deadlock in a complicate system.

Is there any easy way on this? Can we build a new collection and switch the list reference to the new constructed collection? Following code demos such approach:
using System;
using System.Collections.Generic;
using System.Threading;

class Test
{
public class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

private static List<Person> _personList = new List<Person>();
static void Main()
{
// Build initial list
for (int i = 0; i < 10; i++)
{
_personList.Add(new Person("Name" + i.ToString(), i));
}

new Thread(new ThreadStart(LoopthroughList)).Start();

//Following thread will throw InvalidOperationException:
//Exception: Collection was modified; enumeration operation may not execute.
//new Thread(new ThreadStart(UpdateListUnsafely)).Start();

// Following thread won't throw exception
new Thread(new ThreadStart(UpdateListSafely)).Start();

Console.Read();
}

static void LoopthroughList()
{
Console.WriteLine("First loop");
foreach (Person person in _personList)
{
Console.WriteLine("ListHashCode:{0} ListCount:{1} Name:{2} Age:{3}",
_personList.GetHashCode(), _personList.Count, person.Name, person.Age);
Thread.Sleep(100);
}
Console.WriteLine("Second loop");
foreach (Person person in _personList)
{
Console.WriteLine("ListHashCode:{0} ListCount:{1} Name:{2} Age:{3}",
_personList.GetHashCode(), _personList.Count, person.Name, person.Age);
}
}

static void UpdateListUnsafely()
{
Thread.Sleep(500);
_personList.RemoveAt(_personList.Count - 1);
_personList.Add(new Person("A New Person", 20));
}

static void UpdateListSafely()
{
Thread.Sleep(500);
List<Person> newList = new List<Person>(_personList);
newList.RemoveAt(newList.Count - 1);
newList.RemoveAt(newList.Count - 1);
newList[newList.Count - 1].Name = "Modified Name";
newList.Add(new Person("A New Person", 20));
_personList = newList;
Console.WriteLine("List updated! ListCount:{0}", _personList.Count);
}
}
Result:
The result looks promising. We don't see any error when switching the reference (theoretically we should lock this operation). Is such implementation really thread-safe now? Maybe yes maybe no. The problem is that I couldn't find an official answer from Microsoft.

Thursday, March 15, 2007

ASP.NET Find Control Recursively

Sometimes we need to get a control instance inside a page. Looking up each control recursively is the easiest way:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoginInfoUserControl loginControl = FindControl<LoginInfoUserControl>(this.Page, "ucLogin");
}

public static T FindControl<T>(Control root, string controlID) where T : class
{
if (root is T && root.ID == controlID)
{
return root as T;
}
foreach (Control control in root.Controls)
{
T foundControl = FindControl<T>(control, controlID);
if (foundControl != null)
{
return foundControl;
}
}
return null;
}
}

Wednesday, March 07, 2007

HttpWebRequest With Cookie Context

I was doing some load test on an ASP.NET page. The page uses Cache to store some profile data, and the Cache is associated with the client Cookie value. The load test tools I have don't have such dynamic cookie assignment functionality. So I created a simple load test tool for this. The key point is to assign the CookieContainer property of the HttpWebRequest object:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;

namespace HttpRequestTest
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost/MyDashBoard.aspx";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
CookieContainer cookieContainer = new CookieContainer();
Cookie cookie = new Cookie("cookieKey", "cookieValue");
cookie.Path= "/";
cookie.Domain = "localhost";
cookieContainer.Add(cookie);
request.CookieContainer = cookieContainer;

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseData = response.GetResponseStream();
StreamReader reader = new StreamReader(responseData);
string responseFromServer = reader.ReadToEnd();

Console.Read();

}
}
}