Wednesday, April 30, 2008

How to write to the Windows event log using .NET

In the .NET framework you have the System.Diagnostics.EventLog class which provides static methods for working with the Windows event log.

In order to write a new entry to the event log, you can use the WriteEntry method as follows:

using System.Diagnostics;

...

EventLog.WriteEntry("My Application", "My message", EventLogEntryType.Error);

The EventLogEntryType enumeration has the following members: Error, FailureAudit, Information, SuccessAudit, Warning.

If the event source "My Application" doesn't exist, then the WriteEntry method will try to create it. However, if the current process runs under a non-admin account, then this may fail due to lack of permissions. In that case, you can create a console application, which creates the event source by using the CreateEventSource method:

using System;
using System.Diagnostics;

namespace CreateEventSource
{
 class Program
 {
  static void Main(string[] args)
  {
   if (args.Length == 0)
   {
    Console.WriteLine("The event source name is required as an argument.");
    return;
   }
   
   string source = args[0];
   
   try
   {
    if (!EventLog.SourceExists(source))
    {
     EventLog.CreateEventSource(source, "Application");
     Console.WriteLine("The event source {0} was successfully created.", source);
    }
    else
    {
     Console.WriteLine("The event source {0} already exists.", source);
    }
   }
   catch(Exception ex)
   {
    Console.WriteLine("The event source {0} was not created due to: \n{1}", source, ex.ToString());
   }
  }
 }
}

During installation of your application, you run this console application to register the necessary event source(s). Make sure you are logged in as the admin user when you run it.

Monday, April 28, 2008

Kuler from Adobe Labs

In need of some new colors for my blog, I started searching for a color schema tool and then I came across Kuler from Adobe Labs. As of this writing there are more than 46 000 color schemas available, organized by tags, names, popularity and rating. All of them are created by the Kuler users.

Saturday, April 26, 2008

Create a global progress bar for your ASP.NET AJAX applications

In ASP.NET AJAX you can use the UpdateProgress control to display a progress bar for asynchronous page updates. This control needs to be attached to a single UpdatePanel. If you need a consistent progress bar throughout your application, you will thus need to add one UpdateProgress control for every UpdatePanel control. Since this is not an ideal solution, then instead of using the UpdateProgress control, we can create our own progress bar by attaching a method to the beginRequest and endRequest event of the PageRequestManager class, which toggles the visibility of a div-container with an animated image. Have a look at the example below.

1. Start by adding a div-container with the following image to your master-page or web form:

<div><img alt="Progress bar" src="http://www.kindblad.com/wp-content/uploads/2008/04/progressbar.gif" /></div>

2. Add this to your stylesheet file:

#ProgressBar
{
 background: #FFFFFF;
 width: 300px;
 height: 100px;
 border: 1px solid #928F8F;
 position: absolute;
 display: none;
 z-index: 99999998;
}
#ProgressBar img
{
 position: relative;
 left: 43px;
 top: 38px;
}

3. Now we need a method to show and hide the progress bar. Add the following javascript code, make sure you add it below the ScriptManager control:

<script type="text/javascript">
// Toggles the visiblity of the progress bar.
function toggleProgressBar()
{
 var progressBar = $get('ProgressBar');

 if (progressBar.style.display == 'none' || progressBar.style.display == '')
 {
  // Show.
  // Center the progressbar.
  var bounds = getWindowBounds();
  progressBar.style.top = (bounds.y / 2) - 50 + 'px';
  progressBar.style.left = (bounds.x / 2) - 150 + 'px';
  progressBar.style.display = 'block';
 }
 else 
 {
  // Hide.
  progressBar.style.display = 'none';
 }
}
// Returns the height and width of the current window.
function getWindowBounds()
{
 var bounds = {x: 0, y: 0}
 if (self.innerHeight)
 {
  // All except Internet Explorer.
  bounds.x = self.innerWidth;
  bounds.y = self.innerHeight;
 }
 else if (document.documentElement && document.documentElement.clientHeight)
 {
  // Internet Explorer strict mode.
  bounds.x = document.documentElement.clientWidth;
  bounds.y = document.documentElement.clientHeight;
 }
 else if (document.body)
 {
  // Internet Explorer.
  bounds.x = document.body.clientWidth;
  bounds.y = document.body.clientHeight;
 }
 return bounds;
}

// Attach the ToggleProgressBar method.
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(toggleProgressBar);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(toggleProgressBar);
</script>

That's it.

Monday, April 14, 2008

GUID vs. int (Uniqueidentifier vs. identity

I got interested in GUID's when I saw it was used in the ASP.NET membership database schema. At the first glance, 0D40FC92-4F1D-42D1-9A9B-860526C21FF0 didn't seem very nice, but eventually I realized how flexible and effective the GUID datatype is. So let's have a look at why you should use it instead of identities (int)
Let's start by identifying the problems with identity columns (int):
- By default, inserting values into identity columns are not allowed.
- If you try to copy data from one database to another, the identity value is most probably a duplicate as it's already present in the target database.
- No way to know the identity value beforehand until the row is actually inserted into the table.
Using GUID's solves all this:
- In 99,99% of the cases a GUID is unique, due to this you won't see the same value in the target database.
- Since a GUID can be generated in the application layer, like in .NET, it can be generated at any time, even before it's inserted into the database.
So what are the disadvantages of GUIDs'?
- Less readable than int and doesn't look nice in URL's. As a solution to the latter you can use url-rewriting.
- Requires more storage as they are 16 bytes in size instead of 4 bytes for int. But for my own experience I haven't noticed any performance difference when switching to GUID, they perform really well.
To generate a new GUID using MS-SQL Server you use the newid() method, and in .NET you use the GUID.NewGUID().
Since there is no similiar method to scope_identity() or @@identity when using GUID's, then the best way is to generate the value in a stored procedure or in your application code, instead of having newid() as the default value for your GUID-column.

Saturday, April 12, 2008

How to store files in a MS-SQL Server database using ASP.NET

In this post we will take a look at how to store data in a MS-SQL Server database using ASP.NET. We will start by creating the database table in which we will store the actual file data, and then create a new Web Form with a file upload control and an upload button which will save the uploaded file to the database. After that we need to create a generic HTTP Handler which will be responsible for flushing out the file to the client. The reason to why we will use a HTTP Handler for this is that we don't need all the functionality that the System.Web.UI.Page provides, so having both performance and simplicity in mind a HTTP Handler is a better choice.

1. Start by creating a table named File in your database, add the following fields to it:

2. Create a new Web Form, give it the name UploadFile.aspx and add the following code:


C#:
<%@ Page Language="C#" AutoEventWireup="false" CodeFile="UploadFile.aspx.cs" Inherits="UploadFile" %>
<!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>Upload file</title>
</head>
<body>
    <form id="form1" runat="server">
 <asp:FileUpload runat="server" ID="ctrFile" /> <asp:Button ID="btnUploadFile" runat="server" Text="Upload" />
 <asp:HyperLink runat="server" ID="ctrResult" Target="_blank" />
    </form>
</body>
</html>

VB.NET:
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="FileUpload.aspx.vb" Inherits="FileUpload" %>
<!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>Upload file</title>
</head>
<body>
<form id="form1" runat="server">
 <asp:FileUpload runat="server" ID="ctrFile" /> <asp:Button ID="btnUploadFile" runat="server" Text="Upload" />
 <asp:HyperLink runat="server" ID="ctrResult" Target="_blank" />
</form>
</body>
</html>

UploadFile.aspx.cs:


C#:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;

/// 
/// Handles uploading- and storing a file in a database.
/// 
public partial class UploadFile : System.Web.UI.Page
{
 protected override void OnInit(EventArgs e)
 {
  base.OnInit(e);

  btnUploadFile.Click += new EventHandler(btnUploadFile_Click);
 }
 void btnUploadFile_Click(object sender, EventArgs e)
 {
  // exit if file-upload has no file
  if (!ctrFile.HasFile) return;
  
  // generate new fileId
  Guid fileId = Guid.NewGuid();
  // create insert query
  using (SqlCommand command = new SqlCommand())
  {
   command.Connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
   command.CommandText = @"insert into [File](FileId, FileName, FileType, FileSize, FileContent) 
      values(@FileId, @FileName, @FileType, @FileSize, @FileContent)";
   command.Parameters.Add("@FileId", SqlDbType.UniqueIdentifier).Value = fileId;
   command.Parameters.Add("@FileName", SqlDbType.NVarChar, 100).Value = Path.GetFileName(ctrFile.PostedFile.FileName);
   command.Parameters.Add("@FileType", SqlDbType.NVarChar, 100).Value = ctrFile.PostedFile.ContentType;
   command.Parameters.Add("@FileSize", SqlDbType.Int).Value = ctrFile.PostedFile.ContentLength;
   // filecontent, convert from stream to byte array
   byte[] fileContent = new byte[ctrFile.PostedFile.ContentLength];
   ctrFile.PostedFile.InputStream.Read(fileContent, 0, ctrFile.PostedFile.ContentLength);
   command.Parameters.Add("@FileContent", SqlDbType.VarBinary, -1).Value = fileContent;
   command.Connection.Open();
   command.ExecuteNonQuery();
  }
  // show result
  ctrResult.NavigateUrl = "FileStream.ashx?FileId=" + fileId.ToString();
  ctrResult.Text = "Click here to view the uploaded file";
 }
}

VB.NET:

Imports System Imports System.Configuration Imports System.Data Imports System.Data.SqlClient Imports System.IO ''' ''' Handles uploading- and storing a file in a database. ''' Partial Class UploadFile Inherits System.Web.UI.Page Protected Sub btnUploadFile_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnUploadFile.Click ' exit if file-upload has no file If Not ctrFile.HasFile Then Exit Sub ' generate new fileId Dim fileId As Guid = Guid.NewGuid() ' create insert query Dim command As New SqlCommand() Try command.Connection = New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString) command.CommandText = "insert into [File](FileId, FileName, FileType, FileSize, FileContent)" _ & "values(@FileId, @FileName, @FileType, @FileSize, @FileContent)" command.Parameters.Add("@FileId", SqlDbType.UniqueIdentifier).Value = fileId command.Parameters.Add("@FileName", SqlDbType.NVarChar, 100).Value = Path.GetFileName(ctrFile.PostedFile.FileName) command.Parameters.Add("@FileType", SqlDbType.NVarChar, 100).Value = ctrFile.PostedFile.ContentType command.Parameters.Add("@FileSize", SqlDbType.Int).Value = ctrFile.PostedFile.ContentLength ' filecontent, convert from stream to byte array Dim fileContent(ctrFile.PostedFile.ContentLength) As Byte ctrFile.PostedFile.InputStream.Read(fileContent, 0, ctrFile.PostedFile.ContentLength) command.Parameters.Add("@FileContent", SqlDbType.VarBinary, -1).Value = fileContent command.Connection.Open() command.ExecuteNonQuery() Finally command.Dispose() End Try ' show result ctrResult.NavigateUrl = "FileStream.ashx?FileId=" + fileId.ToString() ctrResult.Text = "Click here to view the uploaded file" End Sub End Class

3. Create a new Generic Handler (available under Add New Item > Generic Handler), give it the name FileStream.ashx and add the following code:


C#:
<%@ WebHandler Language="C#" Class="FileStream" %>
using System;
using System.Web;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

/// 
/// Reponsible for flushing out the file from the database to the user.
/// 
public class FileStream : IHttpHandler
{
    public void ProcessRequest (HttpContext context)
    {
 // get the file id
 Guid fileId = new Guid(context.Request.QueryString["FileId"]);

 using (SqlCommand command = new SqlCommand())
 {
  // get the file from database
  command.Connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
  command.CommandText = "select * from [File] where FileId = @FileId";
  command.Parameters.Add("@FileId", SqlDbType.UniqueIdentifier).Value = fileId;
  command.Connection.Open();
  SqlDataReader reader = command.ExecuteReader();
  if (reader.Read())
  {
   // flush out the binary data to the user
   context.Response.Clear();
   context.Response.ContentType = (string) reader["FileType"];
   context.Response.AddHeader("Content-Disposition", String.Format("inline;filename={0};", reader["FileName"].ToString()));
   context.Response.AddHeader("Content-Length", reader["FileSize"].ToString());
   context.Response.BinaryWrite((byte[]) reader["FileContent"]);
   context.Response.End();
  }
 }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

VB.NET:

<%@ WebHandler Language="VB" Class="FileStream" %> Imports System Imports System.Web Imports System.Configuration Imports System.Data Imports System.Data.SqlClient ''' ''' Reponsible for flushing out the file from the database to the user. ''' Public Class FileStream : Implements IHttpHandler Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest ' get the file id Dim fileId As New Guid(context.Request.QueryString("FileId")) Dim command As New SqlCommand() Try ' get the file from database command.Connection = New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString) command.CommandText = "select * from [File] where FileId = @FileId" command.Parameters.Add("@FileId", SqlDbType.UniqueIdentifier).Value = fileId command.Connection.Open() Dim reader As SqlDataReader = command.ExecuteReader() If reader.Read() Then ' flush out the binary data to the user context.Response.Clear() context.Response.ContentType = reader("FileType").ToString() context.Response.AddHeader("Content-Disposition", String.Format("inline;filename={0};", reader("FileName").ToString())) context.Response.AddHeader("Content-Length", reader("FileSize").ToString()) context.Response.BinaryWrite(DirectCast(reader("FileContent"), Byte())) context.Response.End() End If Finally command.Dispose() End Try End Sub Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class

4. Run your code, upload a file using UploadFile.aspx, and then click on the hyperlink "Click here to view the uploaded file" to view the uploaded file.

Tuesday, April 8, 2008

Select random rows from a MS-SQL Server table

Imagine you have a Product table, where you need to get a list of say 5 random products. This is how you can do it:
select top 5 *
from Product
order by newid();
So what actually happens is that newid() returns a new GUID/unique identifier for every row in the result set, it then sorts based on the GUID value and then eventually selects the 5 top most rows.