Wednesday, September 03, 2008

ASP.NET Cache Info Page

This is an ASP.NET page to show Cache usage of current w3wp process:



CacheInfo.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CacheInfo.aspx.cs" Inherits="CacheInfo" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Cache Info/Test Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<div>
<p style="text-align: center">
<asp:Label ID="Label4" runat="server" Text="Cache Info" Font-Size="Large" Font-Bold="true"></asp:Label></p>
<p>
<asp:Label ID="lblSysInfo" runat="server" EnableViewState="false"></asp:Label>
<asp:Label ID="lblInfo" runat="server" EnableViewState="false"></asp:Label>
</p>
<p>
<asp:Label ID="lblCache" runat="server" EnableViewState="false"></asp:Label><br />
<asp:GridView ID="gvCache" runat="server" AutoGenerateColumns="False" BorderWidth="1px"
BackColor="White" CellPadding="4" BorderStyle="Solid" BorderColor="#3366CC" Font-Size="Small"
AlternatingRowStyle-ForeColor="ActiveCaption" EnableViewState="false">
<HeaderStyle ForeColor="White" BackColor="#003399" HorizontalAlign="Left"></HeaderStyle>
<Columns>
<asp:TemplateField ItemStyle-Width="20" ItemStyle-Font-Size="X-Small">
<ItemTemplate>
<%# Container.DataItemIndex + 1 %>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Key" DataField="Key" ReadOnly="true" ItemStyle-Width="420px"></asp:BoundField>
<asp:BoundField HeaderText="Type" DataField="Type" ReadOnly="true" ItemStyle-Width="180px"></asp:BoundField>
<asp:BoundField HeaderText="Note" DataField="Note" ReadOnly="true" ItemStyle-Width="180px"></asp:BoundField>
</Columns>
<EmptyDataTemplate>
<asp:Label ID="lblEmptyMessage" runat="server" Text="No feature found."></asp:Label>
</EmptyDataTemplate>
</asp:GridView>
</p>
<p>
<asp:Label ID="Label3" runat="server" Text="Add cache with size in MB"></asp:Label>
<asp:DropDownList ID="ddlCacheSize" runat="server" AutoPostBack="false">
<asp:ListItem>50</asp:ListItem>
<asp:ListItem>100</asp:ListItem>
<asp:ListItem>200</asp:ListItem>
<asp:ListItem>300</asp:ListItem>
<asp:ListItem>400</asp:ListItem>
<asp:ListItem>500</asp:ListItem>
<asp:ListItem>600</asp:ListItem>
<asp:ListItem>700</asp:ListItem>
<asp:ListItem>800</asp:ListItem>
<asp:ListItem>900</asp:ListItem>
<asp:ListItem>1000</asp:ListItem>
<asp:ListItem>2000</asp:ListItem>
<asp:ListItem>3000</asp:ListItem>
<asp:ListItem>4000</asp:ListItem>
<asp:ListItem>5000</asp:ListItem>
<asp:ListItem>6000</asp:ListItem>
<asp:ListItem>7000</asp:ListItem>
<asp:ListItem>8000</asp:ListItem>
<asp:ListItem>9000</asp:ListItem>
<asp:ListItem>10000</asp:ListItem>
</asp:DropDownList>&nbsp;&nbsp;
<asp:Label ID="Label1" runat="server" Text="Cache Expire in seconds"></asp:Label>
<asp:DropDownList ID="ddlExpir" runat="server" AutoPostBack="false">
<asp:ListItem>10</asp:ListItem>
<asp:ListItem>20</asp:ListItem>
<asp:ListItem>30</asp:ListItem>
<asp:ListItem>40</asp:ListItem>
<asp:ListItem>50</asp:ListItem>
<asp:ListItem>60</asp:ListItem>
<asp:ListItem>70</asp:ListItem>
<asp:ListItem>80</asp:ListItem>
<asp:ListItem>90</asp:ListItem>
<asp:ListItem>100</asp:ListItem>
<asp:ListItem>110</asp:ListItem>
<asp:ListItem>120</asp:ListItem>
<asp:ListItem>180</asp:ListItem>
</asp:DropDownList>&nbsp;&nbsp;
<asp:Button ID="btnAddCache" runat="server" Text="Add Test Cache" OnClick="btnAddCache_click" />
</p>
</div>
</div>
</form>
</body>
</html>

CacheInfo.aspx.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.Caching;
using System.Text;
using System.Data;

public partial class CacheInfo : System.Web.UI.Page
{
protected class CacheInfoItem
{
public string Key { get; set; }
public string Type { get; set; }
public string Note { get; set; }
public CacheInfoItem(string key, string type)
{
Key = key;
Type = type;
}
}

protected class CacheData
{
public Byte[] Data { get; set; }
public CacheData(int totalBytes)
{
Data = new Byte[totalBytes];
for (int i = 0; i < totalBytes; i += 123)
{
Data[i] = (byte)(i % 256);
}
}
}

protected string TestCacheKey = "Cache_Test_Key";

protected void btnAddCache_click(object sender, EventArgs e)
{
try
{
int totalBytes = int.Parse(ddlCacheSize.SelectedValue) * 1024 * 1024;
CacheData data = new CacheData(totalBytes);
int seconds = int.Parse(ddlExpir.SelectedValue);
HttpRuntime.Cache.Insert(TestCacheKey, data, null, DateTime.Now.AddSeconds(seconds), Cache.NoSlidingExpiration);
lblInfo.Text = "Successfully added test data to cache (expire in " + seconds + " seconds).";
}
catch (Exception ex)
{
lblInfo.Text = "Insert Cache error: " + ex.Message;
}
}

protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
List<CacheInfoItem> cacheItems = new List<CacheInfoItem>();

// Populate System Info
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
long totalMemBytes = currentProcess.WorkingSet64;
DateTime startTime = currentProcess.StartTime;
Cache cache = HttpRuntime.Cache;
StringBuilder sbInfo = new StringBuilder();
sbInfo.Append(Server.MachineName + " " + currentProcess.MachineName + " ");

sbInfo.Append(currentProcess.ProcessName + " start time: " + startTime.ToString("yyy-MM-dd HH:mm") + " ");
sbInfo.Append("Process memory usage (MB): " + totalMemBytes / (1024 * 1024) + "<br>");
lblSysInfo.Text = sbInfo.ToString();

// Populate Cache Info
sbInfo = new StringBuilder();
if (cache != null)
{
foreach (DictionaryEntry item in cache)
{
try
{
Type type = item.Value.GetType();
string typeInfo = type.ToString();

CacheInfoItem ci = new CacheInfoItem(item.Key.ToString(), typeInfo);
cacheItems.Add(ci);

if (type == Type.GetType("System.DateTime"))
{
ci.Note = "Time: " + Convert.ToDateTime(HttpRuntime.Cache[item.Key.ToString()]).ToString("yyy-MM-dd HH:mm");
}
else if (type == Type.GetType("System.String"))
{
ci.Note = "Value: " + cache[item.Key.ToString()].ToString();
}
else
{
Object obj = cache[item.Key.ToString()];
if (obj != null)
{
if (obj is IList)
{
ci.Note = "Item Count: " + ((IList)obj).Count.ToString();
}
else if (obj is DataSet && (DataSet)obj != null && ((DataSet)obj).Tables[0] != null)
{
ci.Note = "Item Count: " + ((DataSet)obj).Tables[0].Rows.Count.ToString();
}
else if (obj is DataTable && (DataTable)obj != null)
{
ci.Note = "Item Count: " + ((DataTable)obj).Rows.Count.ToString();
}
}
}
}
catch (Exception ex)
{
sbInfo.Append("Get cache info error for " + item.Key + ":" + ex.Message + "<br>");
throw;
}
}
}
lblInfo.Text += sbInfo.ToString();

lblCache.Text = "Total Cache item count: " + cacheItems.Count.ToString();
cacheItems.Sort((i, j) => i.Key.CompareTo(j.Key));
gvCache.DataSource = cacheItems;
gvCache.DataBind();
}
}

Tuesday, August 26, 2008

LINQ SelectMany Usage

Select and SelectMany are two projection operators in LINQ. Select simply goes through a sequence of elements (IEnumerable), and for each input element it exactly projects one output element. On the contrast, SelectMany can projects 0, 1 or multiple elements for each input elements. It works something like this, for each input element, I can do something based on the element context or just fixed logic, then produces some new elements to return.

For instance, each instructor offers many courses in the school, Instructors.SelectMany(i => i.Courses) means for each instructor in the Instructors list, return his/her Courses, so you get all courses for all instructors.

A SelectMany overloading method accepts the second parameter which gives us more power to control the output. Let’s look at a statement:

Instructors.SelectMany(i => i.Courses, (i, c) => new { InstructorName = i.Name, CourseName = c } ).

The first parameter "i => i.Courses" will get all Courses for each instructor, and transform it to the second parameter, so inside "(i, c)", "i" is the original input element just like what we have in "i => i.Courses", and "c" here is each element sent from "i.Courses". What the statement does is go through each instructor’s courses, and return the instructor’s name and course name with anonymous type of object.

Following code example demos a few usages of SelectMany, including how you can join a sequence to another sequence, and do you your customized logic inside the lambda function:
using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    class Instructor
    {
        public string Name { get; set; }
        public string DeptID { get; set; }
        public string[] Courses { get; set; }
    }

    class Department
    {
        public string ID { get; set; }
        public string Name { get; set; }
    }

    static void Main()
    {
        var departments = new List<Department>()
            {
                new Department() { ID = "EE", Name = "Electrical Engineering" },
                new Department() { ID = "CS", Name = "Computer Science" },
                new Department() { ID = "ME", Name = "Mechanical Engineering" }
            };
        var instructors = new List<Instructor>()
            {
                new Instructor() { Name = "EE1", DeptID = "EE", Courses = new string [] { "EE101", "EE102", "EE103" } },
                new Instructor() { Name = "EE2", DeptID = "EE", Courses = new string [] { "EE201", "EE202", "EE203" } },
                new Instructor() { Name = "CS1", DeptID = "CS", Courses = new string [] { "CS101", "CS102", "CS103" } },
                new Instructor() { Name = "CS2", DeptID = "CS", Courses = new string [] { "CS201", "CS202", "CS203" } },
                new Instructor() { Name = "ME1", DeptID = "ME", Courses = new string [] { "ME101", "ME102", "ME103" } },
                new Instructor() { Name = "ME2", DeptID = "ME", Courses = new string [] { "ME201", "ME202", "ME203" } },
            };
        var CSInstructors = instructors.Where(i => i.DeptID == "CS").Select(i => "CS Instructor: " + i.Name).ToArray();
        // CSInstructors: { "CS Instructor: CS1", "CS Instructor: CS2" }
  
        var CSCourses = instructors.Where(i => i.DeptID == "CS").SelectMany(i => i.Courses).ToArray();
        // CSCourses.Count = 6
        // CSCourses: { "CS101", "CS102", "CS103", "CS201", "CS202", "CS203" }

        var CSInstructorCourses = instructors.Where(i => i.DeptID == "CS").
            SelectMany(i => i.Courses, (i, c) => i.Name + ":" + c).ToArray();
        // CSInstructorCourses.Count = 6
        // CSInstructorCourses: { "CS1:CS101", "CS1:CS102", "CS1:CS103", "CS2:CS201", "CS2:CS202", "CS2:CS203" } 

        var instructorDepartment = instructors.SelectMany(i => departments.Where(d => d.ID == i.DeptID),
            (i, d) => i.Name + ":" + d.Name).ToArray(); 
        // instructorDepartment.Count = 6
        // instructorDepartment: { "EE1:Electrical Engineering", "EE2:Electrical Engineering", "CS1:Computer Science" ... }

        var departmentCourses = instructors.SelectMany(
            (i, index) => i.Courses.Select(c => new { Index = index, CourseName = c}),
            (i, c) => new { Course = c.CourseName, Instructor = i.Name, 
                DeptName= departments.Find(d => d.ID == i.DeptID).Name, Index = c.Index, }).ToArray();
        /* departmentCourses.Count = 18
         * departmentCourses: 
            { Course = "EE101", Instructor = "EE1", DeptName = "Electrical Engineering", Index = 0 }
            { Course = "EE102", Instructor = "EE1", DeptName = "Electrical Engineering", Index = 0 }
            { Course = "EE103", Instructor = "EE1", DeptName = "Electrical Engineering", Index = 0 }
            { Course = "EE201", Instructor = "EE2", DeptName = "Electrical Engineering", Index = 1 }
            { Course = "EE201", Instructor = "EE2", DeptName = "Electrical Engineering", Index = 1 }
            ...
            { Course = "ME202", Instructor = "ME2", DeptName = "Mechanical Engineering", Index = 5 }
            { Course = "ME203", Instructor = "ME2", DeptName = "Mechanical Engineering", Index = 5 }
         */

        var courses101 = instructors.SelectMany(
            i => {
                List<string> filterCourses = new List<string>();
                foreach(var course in i.Courses)
                {
                    if (course.EndsWith("101"))
                        filterCourses.Add(course);
                }
                return filterCourses;
            }, 
            (i, c) => {
                var dept = departments.Find(d => d.ID == i.DeptID);
                var deptName = (dept == null) ? "N/A" : dept.Name;
                return new { Course = c, Instructor = i.Name, DeptName = deptName };
            }).ToArray();
        /* courses101.Count = 3
         * courses101:
            { Course = "EE101", Instructor = "EE1", DeptName = "Electrical Engineering" }
            { Course = "CS101", Instructor = "CS1", DeptName = "Computer Science" }
            { Course = "ME101", Instructor = "ME1", DeptName = "Mechanical Engineering" }
         */

        Console.Read();
    }
}

Wednesday, July 16, 2008

The Cost Of Linq And IQueryable

The Language Integrated Query (Linq) is part of the .NET 3.5. We can easily do Linq queries against IEnumerable objects using very concise syntax by passing a delegate or the new Lambda expression. You can even create your own Linq Provider and implement the IQueryable interface, building something like Linq to Google or Linq to MySql.

It's super great, right? Basically yes, but remember a lot of new features from Microsoft have a cost. In my previous post we discussed the Linq deferred execution issue. Today let's do some tests to see how expensive the Linq queries are. Below is the simple test code. We build a simple key/Value collection (array). Because all collections including array have implemented IEnumerable interface, we can query the objects inside the array with Linq. Furthermore, we can convert the array object to be IQueryable and do query over IQueryable.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

class Program
{
    // A simple class presenting key/value pair
    class KeyValue
    {
        public KeyValue(string key, int value)
        {
            Key = key;
            Value = value;
        }
        public string Key { get; set; }
        public int Value { get; set; }
    }

    // Simple linear search 
    static KeyValue LinearSearch(KeyValue[] keyValues, string key)
    {
        foreach (KeyValue item in keyValues)
        {
            if (item.Key == key)
                return item;
        }
        return null;
    }

    /// <summary>
    /// Compare search performance with three methods:
    /// 1. Manually go through each object inside collection
    /// 2. Linq search by IEmuerable<T>.Where(lambda_expression);
    /// 3. Convert to IQueryable and then IQueryable<T>.Where(lambda_expression)
    /// </summary>
    /// <param name="keyValues">The array to be search on</param>
    /// <param name="key">Search object by KeyValue.Key</param>
    /// <param name="repeat">Number for repeating the search</param>
    static void SearchTest(KeyValue[] keyValues, string key, int repeat)
    {
        Stopwatch watch = new Stopwatch();

        // Test linear search
        watch.Start();
        for (int i = 0; i < repeat; i++)
            LinearSearch(keyValues, key);
        watch.Stop();
        Console.WriteLine("Linear search over {0} objects (repeating {1} times) takes {2} ms",
            keyValues.Count(), repeat, watch.ElapsedMilliseconds);
        watch.Reset(); watch.Start();

        // Test Linq search
        for (int i = 0; i < repeat; i++)
            keyValues.FirstOrDefault(item => item.Key == key);
        watch.Stop();
        Console.WriteLine("Linq search over {0} objects (repeating {1} times) takes {2} ms",
            keyValues.Count(), repeat, watch.ElapsedMilliseconds);

        // Test IQueryable search
        IQueryable<KeyValue> queryables = keyValues.AsQueryable();
        watch.Reset(); watch.Start();
        for (int i = 0; i < repeat; i++)
            queryables.FirstOrDefault(item => item.Key == key);
        watch.Stop();
        Console.WriteLine("Linq IQueryable search over {0} objects (repeating {1} times) takes {2} ms",
            keyValues.Count(), repeat, watch.ElapsedMilliseconds);
        Console.WriteLine();
    }


    static void Main()
    {
        // Build test data
        KeyValue[] keyValues1 =
            Enumerable.Range(0, 1000).Select(i => new KeyValue(i.ToString(), i)).ToArray();
        KeyValue[] keyValues2 =
            Enumerable.Range(0, 2000).Select(i => new KeyValue(i.ToString(), i)).ToArray();
        KeyValue[] keyValues3 =
            Enumerable.Range(0, 3000).Select(i => new KeyValue(i.ToString(), i)).ToArray();

        // Start Test
        SearchTest(keyValues1, "5000", 1000);
        SearchTest(keyValues2, "10000", 1000);
        SearchTest(keyValues3, "20000", 1000);

        Console.Read();
    }
}
Result:


As expected Linq to objects is slightly slower than the regular linear search, and we can assume Linq uses linear search internally. But IQueryable search is much slower than others. Why that happened? Not like Linq to object where Lambda expression is passing as a function delegate, Lambda expression passed to the IQueryable is converted to Expression tree which is a quite expensive.

The interesting thing is that explicit delegate passed to IQueryable won't be converted to Expression tree. In following code there's no noticeable performance difference between the two queries.
    KeyValue keyValueObj = keyValues.FirstOrDefault(
delegate(KeyValue obj) { return obj.Key == key; });
IQueryable<KeyValue> queryables = keyValues.AsQueryable();
keyValueObj = queryables.FirstOrDefault(
delegate(KeyValue obj) { return obj.Key == key; });
Conclusion: Linq to objects are working fine for .NET collections, but be cautious to make a collection as IQueryable (collection.AsQueryable() method) if you are using Lambda expression inside the IQueryable queries, because there's performance degradation for it.

Saturday, June 28, 2008

.NET Linq Deferred Execution

The new Linq (Language Integrated Query) feature shipped with .NET 3.5 is fantastic. With Linq now we can write more concise and meaningful queries. Like anonymous method pitfall we discussed in previous post, Linq has a similar behavior called deferred execution; it could be problematic if you are not aware of that. Let's look at following code:
using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
static void Main()
{
// Test Data
string[] names = new string [] { "NameA", "NameB"};
string _name = "NameA";

// Query by delegate
IEnumerable<string> searchName1 = names.Where(
delegate(string name)
{
return name == _name;
});
// Query by Lambda expression
IEnumerable<string> searchName2 = names.Where(name => name == _name);

// Rename the search keyword
_name = "NameB";

// Redo the queries
IEnumerable<string> searchName3 = names.Where(
delegate(string name)
{
return name == _name;
});
IEnumerable<string> searchName4 = names.Where(name => name == _name);

Console.WriteLine("{0} \t {1} \t {2} \t {3}",
searchName1.First(), searchName2.First(), searchName3.First(), searchName4.First());

Console.Read();
}
}
What's the result? you may think it must be "NameA NameA NameB NameB". But you will get "NameB NameB NameB NameB" instead if you run the console application.

Why's that? Because Linq's Where search is by default a deferred execution function. For example, the statement
string searchName2 = names.Where(name => name == _name);
is telling the compiler that we have a Lambda expression attached to the Where search. But it's not invoked until we are actually reading data from the search result. So searchName1, searchName2, searchName3 and searchName4 in our case are the same because they all compare the same value when their condition is examined.

How to avoid this issue? Reading data immediately after the query such as looping through the data inside the IEnumerable collection. The other way is use ToList() or ToArray() methods to force immediate execution of a Linq query.

Following Linq methods have deferred execution behavior:
Except, Take, TakeWhile, Skip, SkipWhile, Where
While the others don't have such behavior, and will be executed immediately:
Any, Average, Contains, Count, First, FirstOrDefault, Last, LastOrDefault, Single,
SingleOrDefault, Sum, Max, Min, ToList, ToArray, ToDictionary, ToLookup
How to remember all this? The tip is to look at the return type of the method. It would be deferred execution if the return type is IEnumerable<TSource>. Why? Yield return is used by those methods. That's the root cause of the deferred execution.

Thursday, June 05, 2008

C# Anonymous Delegate Pitfall

What is output of following code?
using System;
using System.Collections.Generic;
using System.Text;

namespace AnonymousDelegate
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("AnonymousDelegate1:");
AnonymousDelegate1();
Console.WriteLine();

Console.WriteLine("AnonymousDelegate2:");
AnonymousDelegate2();
Console.WriteLine();

Console.WriteLine("AnonymousDelegate3:");
AnonymousDelegate3();
Console.Read();
}

static void AnonymousDelegate1()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(delegate() { Console.WriteLine(i); });
}
foreach (Action action in actions)
{
action();
}
}

static void AnonymousDelegate2()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
int temp = i;
actions.Add(delegate() { Console.WriteLine(temp); });
}
foreach (Action action in actions)
{
action();
}
}

static void AnonymousDelegate3()
{
List<Action> actions = new List<Action>();
int temp;
for (int i = 0; i < 3; i++)
{
temp = i;
actions.Add(delegate() { Console.WriteLine(temp); });
}
foreach (Action action in actions)
{
action();
}
}
}
}
The result may surprise you:
AnonymousDelegate1:
3
3
3

AnonymousDelegate2:
0
1
2

AnonymousDelegate3:
2
2
2
Why is inconsistent for each test? That stems from the way how .NET handle anonymous delegate or anonymous method.

.NET compiler will create a delegate and a static method for an anonymous delegate, just like a traditional delegate. There's no anonymous concept in intermediate language (IL) in any .NET assembly.

The thing becomes more interesting when a variable used inside anonymous delegate/method is declared outside the anonymous delegate/method, which is called closure scenario. .NET compiler will wrap the anonymous method and the variable to a sealed class: anonymous method becomes a class method, variable becomes a public member. We can examine this by looking inside the IL code (click to see bigger picture):



As we can see there're 3 inner classes (<>c__DisplayClass2/5/9 in red boxes) generated for 3 anonymous methods. All three classes have the same structure:
private sealed class ComiplerGeneratedClass
{
public int i;
public void ActionMethod()
{
Console.WriteLine(this.i);
}
}
That makes sense but why having different results? Let's check the IL code for AnonymousDelegate1:



We can see that only one AnonymousClass object is created, and AnonymousClass.i is used for looping condition (for loop) check.

But when we look at the IL code for method of AnonymousDelegate2, we found AnonymousClass is created 3 times inside the for loop. To be more readable, we translate the IL code back to C# code for all three test methods:
      private sealed class AnonymousClass // Generated by compiler
{
public int i;
public void ActionMethod()
{
Console.WriteLine(this.i);
}
}

static void AnonymousDelegate1()
{
AnonymousClass anonymousClass = new AnonymousClass();
List<Action> actions = new List<Action>();
anonymousClass.i = 0;
for (; anonymousClass.i < 3; anonymousClass.i++)
{
Action action = new Action(anonymousClass.ActionMethod);
actions.Add(action);
}
foreach (Action action in actions)
{
action.Invoke();
}
}

static void AnonymousDelegate2()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
AnonymousClass anonymousClass = new AnonymousClass();
anonymousClass.i = i;
Action action = new Action(anonymousClass.ActionMethod);
actions.Add(action);
}
foreach (Action action in actions)
{
action.Invoke();
}
}

static void AnonymousDelegate3()
{
AnonymousClass anonymousClass = new AnonymousClass();
List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
anonymousClass.i = i;
Action action = new Action(anonymousClass.ActionMethod);
actions.Add(action);
}
foreach (Action action in actions)
{
action.Invoke();
}
}
Now the answer is clear. .NET compiler generates different code depending on the scale of closure variable. It looks very confusing at the beginning and it's easy to write buggy code if we don't understand this.

We have to put it in mind that a real class is created, and a public member is used to store a closure variable for anonymous methods with closure. If not sure how the closure variable is handled, use minimum scale of local variable and pass it to the anonymous method, like what AnonymousDelegate2 does; that ensures each anonymous method using independent variable, but be aware of consuming more resource in such case because more anonymous classes are generated at run time.

Tuesday, March 25, 2008

Remove Files Read-only Attribute In Windows

A Unix machine that's not in our control is running a daily process of copying files in folders to a Windows server under our control by rsync. But some of the files have read-only permission in the target Windows machine, and these read-only files in Windows cause some problem in a separate file-handling process.

To remove a file read-only permission in Windows we can use attrib command:
attrib -r filename
About DOS command will remove all attributes from the file. But attrib can only handle single file. In Unix/Linux we can simply call "chomd -R 777 folder" to recursively change the permission of all files in the folder, but not that easy in Windows.

To do this unix-simple-task in Windows, I created a console application:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.IO;
using System.Security.AccessControl;

namespace Bell.Sympatico.Utilities.ReadOnlyRemover
{
/// <summary>
/// This console application is to remove file's read only attribute
/// The user who runs the application needs the modify permission for the files
/// Application configuration file name: ReadOnlyRemover.exe.config
///
/// Usage:
/// 1. Process all files in a folder and its subfolders.
/// Specify the folder (RootFolder entry) in App.config configuration file
/// C:\ApplicationFolder>ReadOnlyRemover.exe /all
/// 2. Process feed and/or log files.
/// Specify the file name for Feed files (FeedFile entry in configuration file) and/or
/// Specify the file name for Log files (LogFile entry in configuration file)
/// C:\ApplicationFolder>ReadOnlyRemover.exe
/// 3. Process all files in a folder and its subfolders. But the folder name is
/// provided in the arguments. The folder name can be a share folder in network.
/// You need the assign read/write permission for the network shared folder in
/// such case.
/// C:\ApplicationFolder>ReadOnlyRemover.exe /all c:\test
/// C:\ApplicationFolder>ReadOnlyRemover.exe /all \\NetworkShared\Feed1
///
/// A application log file will be created or appended during the process.
/// You can define the application log file name in configuration file (AppLogFile entry)
/// </summary>
class ReadOnlyRemover
{
static StreamWriter sw = null; // Write the logging in a file.
static bool doLog = true; // Do we need to do the logging during the processing?

/// <summary>
/// The entry point of the application
/// </summary>
/// <param name="args">Arguments</param>
static void Main(string[] args)
{
bool scanAll = false; // A flag if check all files inside a folder
if (args.Length > 0 && (args[0].ToLower() == "/all" || args[0].ToLower() == "all"))
{
scanAll = true;
}

string rootFolder = ConfigurationManager.AppSettings["RootFolder"];
if (string.IsNullOrEmpty(rootFolder) || !Directory.Exists(rootFolder))
{
rootFolder = "C:/Feed/"; // Default folder name
}
else if (!rootFolder.EndsWith("/") && !rootFolder.EndsWith("\\"))
{
rootFolder = rootFolder + "/";
}

if (scanAll && args.Length > 1)
{
string argument = args[1];
if (Directory.Exists(argument))
{
rootFolder = argument;
}
else
{
Console.WriteLine("Specified folder invalid!");
return;
}
}

string appLogFile = ConfigurationManager.AppSettings["AppLogFile"];
if (string.IsNullOrEmpty(appLogFile))
{
appLogFile = "ReadOnlyRemover.log.txt"; // Default application log name
}

try
{
sw = new StreamWriter(appLogFile, true);
sw.WriteLine("Process starts at " + DateTime.Now.ToString("yyyy/MM/dd HH:mm"));
sw.Flush();
}
catch (Exception ex)
{
doLog = false; // Do not write the log in the file in such case
Console.WriteLine("Could not create or open application log file. Error: " + ex.Message);
Console.WriteLine("Continue to process without logging.");
}

if (scanAll)
{
UpdateFolderRecursive(rootFolder);
}
else // Process the files that are listed in feedFile and/or logFile
{
bool hasFeedFile = true;
string feedFile = ConfigurationManager.AppSettings["FeedFile"];
if (string.IsNullOrEmpty(feedFile) || !File.Exists(feedFile))
{
Console.WriteLine("Miss Feed file!");
sw.WriteLine("Miss Feed file!");
hasFeedFile = false;
}
List<string> feedNameList = null;
if (hasFeedFile)
{
feedNameList = GetFileNames(feedFile, rootFolder);
UpdateFilesAttribute(feedNameList);
}

bool hasLogFile = true;
string logFile = ConfigurationManager.AppSettings["LogFile"];
if (string.IsNullOrEmpty(logFile) || !File.Exists(logFile))
{
Console.WriteLine("Miss Log file!");
sw.WriteLine("Miss LOg file!");
hasLogFile = false;
}
List<string> logNameList = null;
if (hasLogFile)
{
logNameList = GetFileNames(logFile, rootFolder);
UpdateFilesAttribute(logNameList);
}
}
if (doLog && sw != null)
{
sw.Close();
}
}

/// <summary>
/// Read a file storing all file names that need to be processed
/// Put all file names into a list
/// </summary>
/// <param name="fileName">File Name</param>
/// <param name="rootFolder">The root folder that holds the file</param>
private static List<string> GetFileNames(string fileName, string rootFolder)
{
List<string> fileNameList = new List<string>();
if (File.Exists(fileName))
{
try
{
using (StreamReader sr = new StreamReader(fileName))
{
while (sr.Peek() >= 0)
{
fileNameList.Add(rootFolder + sr.ReadLine());
}
}
}
catch (Exception ex)
{
string logString = DateTime.Now.ToString("yyyy/MM/dd HH:mm") + " - Error in reading file " + fileName + ": " + ex.Message;
if (doLog)
{
sw.WriteLine(logString);
sw.Flush();
}
else
{
Console.WriteLine(logString);
}
}
}
return fileNameList;

}

/// <summary>
/// Go throught all files and remove the read only attribute
/// </summary>
/// <param name="fileNames">File names</param>
private static void UpdateFilesAttribute(List<string> fileNames)
{
foreach (string file in fileNames)
{
if (File.Exists(file))
{
RemoveFileReadonlyAttribute(file);
}
else
{
/*
string logString = DateTime.Now.ToString("yyyy/MM/dd HH:mm") + " - Not a valid file: " + file;
if (doLog)
{
sw.WriteLine(logString);
sw.Flush();
}
else
{
Console.WriteLine(logString);
}
*/

}
}
}

/// <summary>
/// Recursively check all files in the folder and its subfolders
/// Remove read only for all files
/// </summary>
/// <param name="path">Folder Name</param>
private static void UpdateFolderRecursive(string path)
{
if (Directory.Exists(path))
{
string[] files = Directory.GetFileSystemEntries(path);
foreach (string entry in files)
{
if (Directory.Exists(entry))
{
// It's a folder, dig into it's subfolders
UpdateFolderRecursive(entry);
}
else if (File.Exists(entry))
{
// It's a file, check the attribute
RemoveFileReadonlyAttribute(entry);
}
}
}
}

/// <summary>
/// Check if the file is read only. if so remove the read only attribute.
/// </summary>
/// <param name="fileName">file name</param>
private static void RemoveFileReadonlyAttribute(string fileName)
{
if (!File.Exists(fileName))
return;

FileInfo fi = new FileInfo(fileName);
if ((fi.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
try
{
// Remove the read only attribute
fi.Attributes -= FileAttributes.ReadOnly;
StringBuilder sb = new StringBuilder();
sb.Append(DateTime.Now.ToString("yyyy/MM/dd HH:mm"));
sb.Append(" - Read only attribute of file ");
sb.Append(fileName.Replace("\\", "/"));
sb.Append(" has been removed.");
if (doLog)
{
sw.WriteLine(sb.ToString());
sw.Flush();
}
else
{
Console.WriteLine(sb.ToString());
}
}
catch (Exception ex)
{
string logString = DateTime.Now.ToString("yyyy/MM/dd HH:mm") + " - Error in updating " + fileName + " attribute: " + ex.Message;
if (doLog)
{
sw.WriteLine(logString);
sw.Flush();
}
else
{
Console.WriteLine(logString);
}
}
}
}

/// <summary>
/// Add File Security for a file
/// AddFileSecurity(@"c:\Feed\abc.txt", "Everyone", FileSystemRights.FullControl, AccessControlType.Allow);
/// </summary>
/// <param name="filep">File Path</param>
/// <param name="account">Acount for the permission</param>
/// <param name="right">User right</param>
/// <param name="controlType">Allow or Deny</param>
public static void AddFileSecurity(string file, string account, FileSystemRights right, AccessControlType controlType)
{
FileSecurity fileSec = File.GetAccessControl(file);
fileSec.AddAccessRule(new FileSystemAccessRule(account, right, controlType));
File.SetAccessControl(file, fileSec);
}
}
}

Thursday, February 21, 2008

.NET Threading - AutoResetEvent And ManualResetEvent

In .NET EventWait handles are used for signaling for threads' communication. Signaling is when one thread waits until it receives notification from another. AutoResetEvent and ManualResetEvent are two EventWait handles available in .NET 2.0. AutoRsetEvent allows a thread to unblock once when it receives a signal from another and then it automatically resets. ManualResetEvent won't do reset by itself unless you do it manually. Following code sample demos such concept:
using System;
using System.Threading;

class Test
{
static void Main()
{
AutoResetEvent autoReset = new AutoResetEvent(false);
ManualResetEvent manualReset = new ManualResetEvent(false);
WaitHandle[] events = new WaitHandle[2] { autoReset, manualReset };

//Start 3 threads with AutoResetEvent
for (int i = 0; i < 3; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Run), autoReset);
}
Thread.Sleep(1000);
autoReset.Set();
Thread.Sleep(1000);
Console.WriteLine();

//Start 3 threads with ManualResetEvent
for (int i = 0; i < 3; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Run), manualReset);
}
Thread.Sleep(1000);
manualReset.Set();
Thread.Sleep(1000);

Console.WriteLine("\nMain thread completed...");
Console.ReadLine();
}

static void Run(object obj)
{
EventWaitHandle hander = obj as EventWaitHandle;
Console.WriteLine("Thread {0} is started and blocked...", Thread.CurrentThread.ManagedThreadId);
hander.WaitOne();
Console.WriteLine("Thread {0} is unblocked...", Thread.CurrentThread.ManagedThreadId);
}
}
Result:

Saturday, February 02, 2008

.NET 2.0 Configuration AppSettings In Custom Section

App.config or Web.config:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="MyCustomSection" type="CustomSettingManager, ConsoleApplication1"/>
</configSections>
<MyCustomSection>
<appSettings>
<add key="Key1" value="Value1"/>
<add key="Key2" value="Value2"/>
</appSettings>
</MyCustomSection>
</configuration>
Code behind:
using System;
using System.Configuration;

class Test
{
static void Main()
{
Console.WriteLine("Key:{0} Value:{1}", "key1", CustomSettingManager.GetCustomSetting("key1"));
Console.Read();
}
}

public class CustomSettingElement : ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get { return this["key"] as string; }
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get { return this["value"] as string; }
}
}

public class CustomSettingCollection : ConfigurationElementCollection
{
public CustomSettingElement this[int index]
{
get
{
return base.BaseGet(index) as CustomSettingElement;
}
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}

protected override ConfigurationElement CreateNewElement()
{
return new CustomSettingElement();
}

protected override object GetElementKey(ConfigurationElement element)
{
return ((CustomSettingElement)element).Key;
}
}

public class CustomSettingManager : ConfigurationSection
{
private const string sectionName = "MyCustomSection";
private const string collectionName = "appSettings";
private static CustomSettingManager configSection =
ConfigurationManager.GetSection(sectionName) as CustomSettingManager;

public static string GetCustomSetting(string key)
{
string value = string.Empty;
if (configSection != null && configSection[collectionName] != null)
{
CustomSettingCollection appSettings =
configSection[collectionName] as CustomSettingCollection;
foreach (CustomSettingElement item in appSettings)
{
if (string.Compare(item.Key, key, true) == 0)
{
value = item.Value;
}
}
}
return value;
}

[ConfigurationProperty(collectionName)]
public CustomSettingCollection AppSettings
{
get
{
return this[collectionName] as CustomSettingCollection;
}
}
}

Friday, January 18, 2008

Access Private Fields And Invoke Private Methods In .NET

using System;
using System.Reflection;

class Test
{
private int _number;
private int Number
{
get { return _number; }
set { _number = value; }
}

private int PrivateAdd(int a, int b)
{
return a + b;
}

private static int StaticAdd(int a, int b)
{
return a + b;
}

static void Main()
{
//Instance method
Test test = new Test();
MethodInfo privateMethod = test.GetType().GetMethod("PrivateAdd",
BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = new object[] { 1, 2 };
int result = (int)privateMethod.Invoke(test, parameters);

//Static method
MethodInfo staticMethod = typeof(Test).GetMethod("StaticAdd",
BindingFlags.NonPublic | BindingFlags.Static);
result = (int)staticMethod.Invoke(null, parameters);

//Private field
FieldInfo privateField = test.GetType().GetField("_number",
BindingFlags.NonPublic | BindingFlags.Instance);
privateField.SetValue(test, 123);
result = (int)privateField.GetValue(test);

//Private property
PropertyInfo privateProperty = test.GetType().GetProperty("Number",
BindingFlags.NonPublic | BindingFlags.Instance);
privateProperty.SetValue(test, 456, null);
result = (int)privateProperty.GetValue(test, null);

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