Tuesday, December 20, 2005

Using CrystalReport to Export DataTable With Different Format

We can export data inside a DataTable to an Excel spreadsheet by defining the Response.ContentType:
Response.ContentType = "application/vnd.ms-excel";
string separator = "\t";
foreach (DataColumn dc in dataTable.Columns)
{
Response.Write(separator + dc.ColumnName );
}
Response.Write("\n");
foreach (DataRow dr in dataTable.Rows)
{
for (int i = 0; i < dataTable.Columns.Count; i++)
{
Response.Write(separator + dr[i].ToString());
}
Response.Write("\n");
}
What if we need more format such as PDF? You can export data to a few other popular formats by using Crystal's ReportDocument if you have Crystal Reports available:
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
using System.IO;

private void ExportReport()
{
    MemoryStream oStream = new MemoryStream();
    string filename = string.Empty;
    try
    {
        ReportDocument crReport = new ReportDocument();
        string reportFile = "CrystalReport1.rpt";
        string reportPath = string.Format("{0}/{1}", Server.MapPath("."), reportFile);
        crReport.Load(reportPath);

        ReportDataSetTableAdapters.UtilityTableAdapter ad = new ReportDataSetTableAdapters.UtilityTableAdapter();
        ReportDataSet.UtilityDataTable ut = ad.GetAllData();
        crReport.SetDataSource((DataTable)ut);

        switch (SelectedType)
        {
         case "1": //Rich Text (RTF)
             oStream = (MemoryStream)crReport.ExportToStream(ExportFormatType.RichText);
             Response.ContentType = "application/rtf";
             filename = "data.rtf";
             break;
         case "2": //PDF 
             oStream = (MemoryStream)crReport.ExportToStream(ExportFormatType.PortableDocFormat);
             Response.ContentType = "application/pdf";
             filename = "data.pdf";
             //in case you want to export it as an attachment use the line below
             //crReport.ExportToHttpResponse(ExportFormatType.PortableDocFormat, Response, true, "Your Exported File Name"); 
             break;
         case "3": //MS Word (DOC)
             oStream = (MemoryStream)crReport.ExportToStream(ExportFormatType.WordForWindows);
             Response.ContentType = "application/doc";
             filename = "data.doc";
             break;
         case "4": //MS Excel (XLS)
             oStream = (MemoryStream)crReport.ExportToStream(ExportFormatType.Excel);
             Response.ContentType = "application/vnd.ms-excel";
             filename = "data.xls";
             break;
         default: //PDF 
             oStream = (MemoryStream)crReport.ExportToStream(ExportFormatType.PortableDocFormat);
             Response.ContentType = "application/pdf";
             filename = "data.pdf";
             break;
        }
        //write report to the Response stream
        byte[] dataByte = oStream.ToArray();
        Response.Charset = "";
        Response.AddHeader("content-disposition", "fileattachment;filename=" + filename);
        Response.ContentType = "application/octet-stream";
        Response.BinaryWrite(dataByte);
        Response.Flush();
        Response.Clear();
        Response.End();
    }
    catch (Exception ex)
    {
        ErrorLog.Write(ex);
    }
    finally
    {
        //clear stream
        oStream.Flush();
        oStream.Close();
        oStream.Dispose();
    }
}

Just a side note if you can not import Crystal merge module into your VS2003, then you need to register the dll again:
regsvr32 "C:\Program Files\Common Files\Microsoft Shared\MSI Tools\mergemod.dll"

Friday, December 09, 2005

Check Windows Group Users & Clean-up Network Logon

Often used commands inside Windows domain:

Show users in a local group:
>net localgroup administrators

Show users in a domain group:
>net group All_IT_Staff /domain

Delete credential for a network share
>net use \\shareServerName /del

Delete all credentials for all shares:
>net use * /del

Open Credential manager:
>control keymgr.dll
>rundll32.exe keymgr.dll, KRShowKeyMgr

Monday, November 28, 2005

.NET Volatile Field

The MSDN description for the volatile modifier:
"For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock-statement."

What does it mean? In a multi-CPU and multi-threaded environment, a value could be temporarily stored in registers and multiple levels of cache, and the value change by a thread in one CPU may not be seen immediately by another thread run in another CPU. This could lead to some unpredictable issues.

We can guarantee to get a refresh value by declaring a volatile field:
using System;
using System.Threading;
class Test
{
public static bool finished = false;
private static object syncLock = new object();
static void Main()
{
new Thread(new ThreadStart(Thread1)).Start();
new Thread(new ThreadStart(Thread2)).Start();
Console.Read();
}
static void Thread1()
{
while (true)
{
if (finished)
{
Console.WriteLine("Finished.");
break;
}
}
}
static void Thread2()
{
lock (syncLock)
{
finished = true;
}
}
}
In above code snippet, Thread1 will immediately see the latest "finished" value updated by a Thread2, and guarantee to finish and break the infinite loop. Without volatile declared, Thread1 could (not always) never be ended in some systems.

One important note is that volatile field doesn't mean it's thread-safe. Locking mechanism is still required in some scenarios. With a locked field, the value of that field is guarantee to be the latest one, and volatile is unnecessary in such case.

(Updated 2006/9):
My original understanding may not be correct. Some people argue that volatile only means the sequence of accessing a volatile member won't reordered, it doesn't mean that a value assignment will be immediately visible to all processors. However I could not verify this without a real environment.

Wednesday, November 09, 2005

Visual Studio 2005 & SQL Server 2005 Launch Event in Toronto

Microsoft was launching VS2005, SQL Server 2005, BizTalk Sever 2006 Server as well as .NET 2.0 in Toronto Congress center yesterday. We attended the developer sessions and saw some really good stuff from the presenters. Even better, every attendee was given complimentary software. That includes full version of VS 2005 professional edition, SQL server 2005 standard edition and BizTalk Server 2006 standard edition. Microsoft is quite generous this time.

Tuesday, October 18, 2005

Web-based Real-time Information Tracking

I am working on a project required web-based real-time status tracking on a big automobile plant. Supervisors need to check if some events such as “incidents happen using a browser. The browser needs to update the information in a few seconds based on the setting. The status information such as “incidents” are stored in a database with information of “start time”, “end time”, “duration”, etc. We decided to use Dundas Charts to generate the information images, and use JavaScript to populate the real-time images. We thought about two approaches for dynamic loading Dundas images on the fly.

1. Image generated in the server
  • Advantage: browser friendly without any client side requirement
  • Disadvantage: increase the workload in server and more traffic in the network if images are big
2. Image generated in the client
  • Advantage: less overhead in server and network
  • Disadvantage: require ActiveX enabled in the browser and more overhead in the client
The first method uses JavaScript to update the images regularly. To avoid the page flicker, and the page only reloads the image instead of the whole page (change the image source inside JavaScript):

Consider the image generated in the server may take a bit time (retrieving data from database and building chart image), we could use image buffering in the server: the page image at client only loaded when it’s ready at server. We can buffer image in memory or in files:

The second method also uses JavaScript, but the JavaScript gets the real-time data from server instead of binary images. To do this, we create XMLDOM object inside JavaScript. The data is retrieved from server by XMLHTTP object, and filled into ActiveX object (Dundas chart), then we redraw the chart, as shown in following figure:

There is a sampling rate issue for plotting data in a chart when the unit of X-value in chart is different from that of the data from database. A simple solution for that is to plot the data when there is an incident inside that time slot. As depicted in below figure, three incidents are all plotted between the time shot 2 and shot 3:


(Nov. 2005 update) The first approach (generating images from the server) has been implemented with memory buffering mechanism. The client is quite satisfied what we delivered.

Friday, August 19, 2005

Autonomus System

We are working on a pilot project called BEAT (Best-Effect Autonomous Technology) that will be used in buildings' remote management. The project is quite challenging. We face so many concerns. The complexity of the application would grow significantly if every concern needs to be well addressed. It seems very hard to implement a practical and effective autonomous system in the real world. But hold on a second, is autonomous system well set in every automobile?:-)

Three of us in our team spent almost a whole day on discussing a module of our autonomous system, and the whiteboard pictures of our discussion:

Thursday, June 16, 2005

.NET Remoting vs Java RMI

I found .NET Remoting very close to Java RMI in terms of concept and usage. I did more research on .NET Remoting and happened to see a benchmark comparison between .NET Remoting and Java RMI. It shows .NET Remoting is much slower than Java RMI. Here's the conclusion from the article (http://www.cs.ru.nl/~marko/onderwijs/oss/RMI_and_Remoting.pdf):

"
The most obvious and expected result of these measurements is that the middleware for both Java and .Net causes a performance decrease compared to local system calls. This is still true when disregarding the networking
cost, because there is an extra layer of functionality within middleware.

RMI had the best performance in the 3 performed tests. It is up to 20
times faster than .Net Remoting on object invocation. This was more or less expected, as Microsoft warns for a bad performance in this specific test. The result was unexpected in the method invocation and data invocation tests. On method invocation RMI outperformed Remoting by a factor 7 and on data invocation by a factor 2.
"

The tests were conducted on .NET 1.1 and Java 1.4.2. I checked .NET 2.0 Beta documentation; new IPC channel and security enhancements are highlighted, and I couldn't see any information about Remoting's performance improvement in .NET 2.0.

Tuesday, May 03, 2005

.NET 2.0 Generics

I am testing out the new .NET 2.0 Generics feature, and found it's very cool.

Generics allow you to define type-safe classes without compromising type safety, performance, or productivity (From MSDN). That's really true especially on productivity. With Generics you are no longer need to do the type casting. Goodbye ArrayList because we have generic List!
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
public interface ITimeable
{
DateTime GetTime();
}

public class MyEvent : ITimeable
{
private string _name;
private DateTime _time;
public MyEvent(string name, DateTime time)
{
_name = name;
_time = time;
}
public DateTime GetTime()
{
return _time;
}
public override string ToString()
{
return _name;
}
}

public class Helper
{
public static List<T> GenericSortByTime<T>(List<T> items) where T : ITimeable
{
items.Sort(delegate(T item1, T item2)
{
return item1.GetTime().CompareTo(item2.GetTime());
});
return items;
}

public static List<T> GenericSortByName<T>(List<T> items) where T : class
{
items.Sort(delegate(T item1, T item2)
{
return item1.ToString().CompareTo(item2.ToString());
});
return items;
}
}

class Program
{
static void Main(string[] args)
{
DateTime now = DateTime.Now;
List<MyEvent> events = new List<MyEvent>();
events.Add(new MyEvent("1", now.AddDays(1)));
events.Add(new MyEvent("3", now.AddDays(2)));
events.Add(new MyEvent("2", now.AddDays(3)));
events.Add(new MyEvent("a", now.AddDays(-1)));
events.Add(new MyEvent("b", now.AddDays(-2)));
Console.WriteLine("Original list:");
foreach (MyEvent myEvent in events)
Console.WriteLine(myEvent.ToString() + ":" + myEvent.GetTime().ToString());
events = Helper.GenericSortByTime(events);
Console.WriteLine("Sort by time:");
foreach (MyEvent myEvent in events)
Console.WriteLine(myEvent.ToString() + ":" + myEvent.GetTime().ToString());
events = Helper.GenericSortByName(events);
Console.WriteLine("Sort by name:");
foreach (MyEvent myEvent in events)
Console.WriteLine(myEvent.ToString() + ":" + myEvent.GetTime().ToString());
Console.Read();
}
}
}

Result:

Monday, April 18, 2005

.NET 2.0 Beta 2

.NET 2.0 Beta 2 and Visual Studio 2005 Beta 2 were just released last week. I tested it and did see a lot of improvements comparing previous version: full 64-bit support, enhanced intelli-sense, generics, partial class, typed data table, and a lot of new controls...

But one thing confused me is that VS2005 is still built by C++. Why not C#/VB.NET? It's more convincing to people that .NET is robust and mature enough for enterprise applications, if Microsoft is building their own tools using .NET.

Monday, March 07, 2005

Email Sending in .NET

First you need a SMTP server (relay server) available for your .NET application to send out emails. SMTP server can be a Linux Sendmail demon or IIS SMTP service.

To send an email in .NET 1.1, you need a reference to System.Web.dll and use the System.Web.Mail namespace:

MailMessage emailMsg = new MailMessage();
emailMsg.From =
"sender@mydomain.com";
emailMsg.To =
"receiver@mydomain.com";
emailMsg.Subject =
"Subject";
emailMsg.Body =
"Body";
SmtpMail
.SmtpServer = "mysmtpserver.com";
SmtpMail
.Send(emailMsg);

Be aware that under the hood .NET dispatches email message to a COM library CDO.Message included in cdosys.dll, which come with Windows 2003 server (in $Windows$/system32 folder). So .NET email function is just a wrapper function, and unmanaged (native) code is involved during the email sending.

If SMTP server requires authenitcation, you need to add following setting for MailMessage:

emailMsg.Fields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] = 1;
emailMsg.Fields[
"http://schemas.microsoft.com/cdo/configuration/sendusername"] = "myLogin";

emailMsg.Fields[
"http://schemas.microsoft.com/cdo/configuration/sendpassword"] = "myPassword";

If you want the a HTML format of email, and add an attachment to it:

emailMsg.BodyFormat = MailFormat.Html;
MailAttachment
emailAttchement = new MailAttachment(fileName);
emailMsg.Attachments.Add(emailAttchement);

Tuesday, February 22, 2005

ASP.NET Session Cookies and Authentication Cookies

In previous blog I briefly mentioned the Session cookie and the Authentication Cookie. How do they work?

First let's look at the Session in ASP.NET. ASP.NET Session is a state management mechanism that maintains session data inside the server for a unique user interacting with the server for a period of time. For example, you can pull a user profile data from database during user's first visit and store it into Session, then for the next requests from the same user you can retrieve the profile data directly from Session without bothering database:

User currentUser = Session["UserProfile"] as User;
if (currentUser == null)
{

currentUser = UserService.GetUserById(id);
Session[
"UserProfile"] = currentUser;
}


The Session data are stored in memory by default. But you have option to store Session data in a dedicated machine, in database, or use your you own session provider. This is important in a web farm environment.

How do ASP.NET web server identify a user and retrieve its Session data? ASP.NET uses a 120 bit identifier (Session ID) to track each session. When the user first talks to the server, the Session cookie is empty and the server will create a new Session ID and set to Session cookie which will be kept in user's browser store (non-persistent cookie). When the user talks the server again, ASP.NET looks on to Session ID and retrieves corresponding data. You can see the Session cookie using a traffic monitor tool such as Fiddler :

Cookie: ASP.NET_SessionId=ezdpwa55i5xo41rbp5bfbv55;

Now let's discuss the Authentication and Authentication cookie. Authentication is a mechanism to obtain a user's identity by validating its credential against the authority such as Active Directory or LDAP. By default ASP.NET supports Windows Authentication, Passpart Authentication, and Forms Authentication. In short, Windows Authentication is managed by IIS, Passport Authentication is handled by a web-based and centralized store control by Microsoft, and Forms Authentication is the only one that is able to check user credentials stored in a file or in a custom database. Authentication cookie is used in all three scenarios, but we only cover Forms Authentication cookie here.

With Forms Authentication, usually you create a login form with the logic to validate a user against credential store. If authentication fails then the user will be treated as anonymous user and granted limited permssion. If authentication succeeds, the server will create a Forms Authentication ticket and add the ticket in Authenticaiton cookie. This is usually done by:

FormsAuthentication.SetAuthCookie(userName, false);

Or

FormsAuthentication.RedirectFromLoginPage(userName, false);


The second parameter in above two methods sets the Authentication cookie to be non-persistent or persistent.

For security consideration, the Forms Authentication ticket is encrypted and signed using server's Machine.config configuration before saving to Authentication cookie. The default encryption algorithm is 3DES. You will see the real Authentication cookie like:

Cookie: .ASPXAUTH=5D49D0CFCC287EE624F53A0FFC51F436C098E90D6345ACC06BF249661725E004E403880ACEA9F069A221C2D4893D5BBDDA2AFE4A63D17DC04;


Because different machines have different encryption/decryption keys, the same Authentication cookie won't be able to be decrypted in all machines inside a web farm. To resolve this problem, you have to explicitly specify the values for "validationKey" and "decryptionKey" attributes in the section of your application’s web.config file in each machine cross the web farm, so all machines will use the same encryption/decryption keys.

(2006/03 updated) The same encryption/decryption keys are used to handled the ViewState for ASP.NET pages. You have to do the same configuration update if you have a web farm, otherwise you will get a server error:

Saturday, February 12, 2005

ASP.NET Cookies

A cookie is a small piece of data that is created in the server and stored in a client's web browser. Cookies are part of the request data that sent to the server. And server are able to retrieve or update cookies.

Cookie is used in ASP.NET to do the authentication and session tracing. Cookie can be easily manipulated in your ASP.NET web application, such as storing user preferences. It's a good practice to only store small piece of data inside cookie because browsers have cookie size limit. For example, IE has 4K of maximum size of cookie, and only allows 20 cookies per domain name.

To read a cookie, you get the HttpCookie collection from the Request object:
HttpCookie cookie = Request.Cookies["MyCookie"]; // Or Request.Cookies[0]
string cookieName = cookie.Name; // = "MyCookie"
string cookieValue = cookie.Value;
To set or update a cookie:
HttpCookie cookie = new HttpCookie("MyCookie");
cookie.Value = "My Value";
Response.Cookies.Add(aCookie);
Or simply:
Response.Cookies["MyCookie"].Value = "My Value";
To delete a Cookie, you have to update the cookie with expiration defined before current time:
Response.Cookies["MyCookie"].Expires = DateTime.Now.AddDays(-1);
Cookie with expiration time tells browser to remove the cookie when it's expired. A cookie without expired time is called non-persistent cookie because it won't store in user's hard disk, and the cookie is discarded when browser is closed. By default, Session cookie in ASP.NET is non-persistent cookie, while authentication cookie is persistent cookie.

Domain is another property you can set for a cookie. You may have a website of http://www.mysite.com and have a subdomain of http://sales.mysite.com. If you want to share the cookie in these two domains then you should do:
cookie.Domain = "mysite.com";
You can also limit the cookie to a folder in your application by setting cookie's Path property. For example, you want to have a cookie only available in http://www.mysite.com/Application1 and another cookie only available for http://www.mysite.com/Applicaiton2:
cookie1.Path = "/Application1";
cookie2.Path = "/Application2";
You can store one value in a cookie as described above. You can also store multiple name-value pairs in a single cookie:
Response.Cookies["MyCookie"]["Name1"] = "value1";
Response.Cookies["MyCookie"]["Name2"] = "value2";
To get a multi-value cookie:
string cookieValue1 = Request.Cookies["MyCookie"]["Name1"];
string cookieValue2 = Request.Cookies["MyCookie"]["Name2"];
Just for your curiosity, the multi-value cookie transferred between browser and server is something like:

Cookie: MyCookie=Name1=value1|Name2=value2;

Wednesday, January 19, 2005

A Few Findings About .NET Delegate/Event

Events are allowed in an interface. Delegates are not allowed in an interface, but delegate properties are allowed in an interface.

Delegates are immutable, and cannot be modified after they are constructed.

There's no straight or easy way to remove all delegates attached to an event.

A separate thread will be created by .NET to handle the FileSystemWatcher events with asynchronous processing.