Tuesday, 15 April 2008

AjaxControlToolKit zIndex issue.

Update: On June 20th 2009, Codeplex notified me that the patch I did for the ACT has been applied. I haven't tested it yet, though. Get the latest source (not latest stable version) and you should be fine.

The Ajax Control Toolkit has a PopupExtender module that is used throughout the library in whichever controls need to show above other controls. I wanted to use the Calendar Extender in my web site, but the calendar appeared underneath other controls. I checked it out and it had a zIndex of 1000, which should have been enough. I took me an hour to realise that in the toolkit code zIndex was a property of the div element, not of the div style!

A download of the latest version from Feb 29 shows the problem is still there. The fix? go to the PopupExtender folder in the source code, open the PopupBehaviour.js file, search for a line that looks like this:
element.zIndex = 1000;
and replace it with
element.style.zIndex = 1000;
. Now it works!

The issue is already in the AjaxControlToolKit issue tracker, but it was not addressed yet.

Monday, 14 April 2008

Hot Kiss - Juliette & the Licks

No, you are not mistaken, the voice for this band is Juliette Lewis herself, a pretty known actress that usually interprets beautifully screwed up characters. No wonder her music is similar :) But when I first heard of the band I didn't even bother to listen to them. Not another acting celebrity trying to sing! But when I found out the vocal on Prodigy's Spitfire and Hot Ride songs was Juliette Lewis, I changed my mind. And I am glad I did. Here is the weird and nicely sung Hot Kiss.



Licks:
MySpace page for Juliette & the Licks
Wikipedia entry
Juliette & the Licks fan site

Using the SqlConnection InfoMessage to return variable amounts of data

A little used thingie on the SqlConnection object called the InfoMessage event fires whenever there were info messages (duh!) from the last query or stored procedure execution. That means errors, of course, but it also means warnings and simple prints! You get where I am going with this?

Instead of changing stored procedures, datalayers and code whenever I need to get some new information from SQL, I can just add some nicely formatted PRINT commands and get all the information I need! Here is some code:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Windows.Forms;

namespace SqlPrintCommands
{
public partial class Form2 : Form
{
public Dictionary<string, string> values;

public Form2()
{
InitializeComponent();
values = new Dictionary<string, string>();
}

private void button1_Click(object sender, EventArgs e)
{
SqlConnection connection =
new SqlConnection("[connectionString]");
connection.Open();
connection.InfoMessage += sc_InfoMessage;
SqlCommand comm =
new SqlCommand("pr_Test", connection);
comm.ExecuteNonQuery();
connection.Close();
string s = "";
foreach (KeyValuePair<string, string> pair in values)
{
s += string.Format("{0} : {1}\r\n",
pair.Key, pair.Value);
}
label1.Text = s;
}

private void sc_InfoMessage(object sender,
SqlInfoMessageEventArgs e)
{
string commandPrefix = "Eval: ";
foreach (SqlError err in e.Errors)
{
if ((err.Message ?? "").StartsWith(commandPrefix))
{
string command =
err.Message.Substring(commandPrefix.Length);
string[] cmd = command.Trim().Split('=');
string commandArgument = cmd[0];
string commandValue = cmd[1];
values[commandArgument] = commandValue;
}
}
}
}
}


In this scenario I have a simple form with a button and a label. I execute a pr_Test stored procedure and then I parse the messages it returns. If the messages are of the format
Eval: Name=Value
I store the keys and values in a Dictionary. Not the nicest code, but it's for demo purposes.

So, you want to know the count of whatever operation you executed? Add
PRINT 'Eval: RowCount='+cast(@@rowcount as varchar)
in your stored procedure. Pretty cool huh?

Unfortunately I haven't been able to send messages asynchronously, even if the connection was async and the running was async and the messages were generated with
RAISERROR('message',1,1) WITH NOWAIT
. BTW, who is the idiot that spelled RAISERROR with only one E? What's a Rror and why would I raise it?

Grendel by John Gardner

Don't go all "Oh no, not another Beowulf remake!" on me. This is a book that was written in 1971 by John Gardner, presenting the story of Beowulf through the eyes of Grendel. But it is not really the same story, just uses it as a scaffold for the philosophical ideas that he wanted to expose.
Grendel book cover

Structured into 12 chapters - each for a year in Grendel's life, each for a description of a philosophical current, each for an astrological sign - the book is not an easy one to understand, albeit pretty short. The language is modern and the wording is clear, but the underlying ideas need time and brain power to process, so don't read it in short bursts when you feel bored. Give it what it needs.

In the book, Grendel is not an animal monster, a thing with no thinking, quite the opposite. He is intelligent, articulate, philosophical, all these qualities being given to him at birth, not as a merit to anyone. He is hopelessly depressed and malevolent. He sees life and existence as meaningless, all the Universe a hollow illusion, a thing set to hurt him, set him apart, mock him. It is really easy to identify with him and to feel his feelings, while in the same time despise what he does and why he does it. Grendel is the part of us which we hate and which hates itself.

Enough, though, the book has bad parts as well. The occasional poem lyrics are meaningless in this book. The ending is confused and confusing. I would have liked a clearer ending, that's for sure. And also, it is hard to understand the book without at least knowing the Beowulf story and researching a bit from the Wikipedia article to find out what are the philosophical references hidden in each chapter. But then again, it was never a simple book, and the research (even if I haven't found time to do it) is worth it.

There was an animation film made in Australia in 1981 and featuring Peter Ustinov called Grendel Grendel Grendel which was based on the book, although I haven't been able to get my hands on it. It was partly musical as well, as expected in such a period, ugh!

If you are interested in finding out more about the meanings in the book and discussing about it, here is a link: The Grendel Board.

Wednesday, 9 April 2008

The DataRow value setter is slow!

Update 19 February 2016:
I've done the test again, using another computer and .Net 4.6.1. The speed of filling the DataTableReplacement class given at the end of the article, plus copying the data into a DataTable object is 30% faster than using a DataTable directly with BeginLoadData/EndLoadData and 50% faster than using DataTable without the LoadData methods.

Now for the original post:

It was about time I wrote a smashing IT entry. Here is to the obnoxious DataTable object, something about I have written before of bugs and difficulty in handling. Until now I haven't really thought about what kind of performance issues I might face when using it. I mean, yeah, everybody says it is slow, but how slow can it be? Twice as slow? Computers are getting faster and faster, I might not need a personal research into this. I tried to make a DataTable replacement object once and it was not really compatible with anything that needed DataTables so I gave up. But in this article I will show you how a simple piece of code became 7 times faster when taking into account some DataTable issues.

But let's get to the smashing part :) I was using C# to transform the values in a column from a datatable into columns. Something like this:
NameColumnValue
GeorgeMoney100
GeorgeAge31
GeorgeChildren1
JackMoney150
JackAge26
JackChildren0
JaneMoney300
JaneAge33
JaneChildren2


and it must look like this:

NameMoneyAgeChildren
George100311
Jack150260
Jane300332


I have no idea how to do this in SQL, if you have any advice, please leave a comment.
Update: Here are some links about how to do it in SQL and SSIS:
Give the New PIVOT and UNPIVOT Commands in SQL Server 2005 a Whirl
Using PIVOT and UNPIVOT
Transposing rows and columns in SQL Server Integration Services

Using PIVOT, the SQL query would look like this:
SELECT * 
FROM #input
PIVOT (
MAX([Value])
FOR [Column]
IN ([Money],[Age],[Children])
) as pivotTable

Anyway, the solution I had was to create the necessary table in the code behind add a row for each Name and a column for each of the distinct value of Column, then cycle through the rows of the original table and just place the values in the new table. All the values are present and already ordered so I only need to do it using row and column indexes that are easily computed.

The whole operation lasted 36 seconds. There were many rows and columns, you see. Anyway, I profiled the code, using the great JetBrains dotTrace program, and I noticed that 30 seconds from 36 were used by DataRow.set_Item(int, object)! I remembered then that the DataTable object has two BeginLoadData and EndLoadData methods that disable/enable the checks and constraints in the table. I did that and the operation went from 36 to 27 seconds.

Quite an improvement, but the bottleneck was still in the set_Item setter. So, I thought, what will happen if I don't use a DataTable at all. After all, the end result was being bound to a GridView and it, luckily, knows about object collections. But I was too lazy for that, as there was quite a complicated binding code mess waiting for refactoring. So I just used a List of object arrays instead of the datatable, then I used DataTable.Rows.Add(object[]) from this intermediary list to the DataTable that I originally wanted to obtain. The time spent on the operation went from... no, wait

The time spent on the operation went from the 27 seconds I had obtained to 5! 5 seconds! Instead of 225.351 calls to DataRow.set_Item, I had 1533 calls to DataRowCollection.Add, from 21 seconds to 175 miliseconds!

Researching the reflected source of System.Data.dll I noticed that the DataRow indexer with an integer index was going through
DataColumn column=_columns[index]; return this[column];
How bad can it get?! I mean, really! There are sites that recommend you find the integer index of table columns and then use them as integer variables. Apparently this is NOT the best practice. Best is to use the DataColumn directly!

So avoid the DataRow setter.

Update July 18, 2013:

Someone requested code, so here is a console application with some inline classes to replace the DataTable in GridView situations:

class Program
{
static void Main(string[] args)
{
fillDataTable(false);
fillDataTable(true);
fillDataTableWriter();
Console.ReadKey();
}

private static void fillDataTable(bool loadData)
{
var dt = new DataTable();
dt.Columns.Add("cInt", typeof(int));
dt.Columns.Add("cString", typeof(string));
dt.Columns.Add("cBool", typeof(bool));
dt.Columns.Add("cDateTime", typeof(DateTime));
if (loadData) dt.BeginLoadData();
for (var i = 0; i < 100000; i++)
{
dt.Rows.Add(dt.NewRow());
}
var now = DateTime.Now;
for (var i = 0; i < 100000; i++)
{
dt.Rows[i]["cInt"] = 1;
dt.Rows[i]["cString"] = "Some string";
dt.Rows[i]["cBool"] = true;
dt.Rows[i]["cDateTime"] = now;
}
if (loadData) dt.EndLoadData();
Console.WriteLine("Filling DataTable"+(loadData?" with BeginLoadData/EndLoadData":"")+": "+(DateTime.Now - now).TotalMilliseconds);
}

private static void fillDataTableWriter()
{
var dt = new DataTableReplacement();
dt.Columns.Add("cInt", typeof(int));
dt.Columns.Add("cString", typeof(string));
dt.Columns.Add("cBool", typeof(bool));
dt.Columns.Add("cDateTime", typeof(DateTime));
for (var i = 0; i < 100000; i++)
{
dt.Rows.Add(dt.NewRow());
}
var now = DateTime.Now;
for (var i = 0; i < 100000; i++)
{
dt.Rows[i]["cInt"] = 1;
dt.Rows[i]["cString"] = "Some string";
dt.Rows[i]["cBool"] = true;
dt.Rows[i]["cDateTime"] = now;
}
var fillingTime = (DateTime.Now - now).TotalMilliseconds;
Console.WriteLine("Filling DataTableReplacement: "+fillingTime);
now = DateTime.Now;
var newDataTable = dt.ToDataTable();
var translatingTime = (DateTime.Now - now).TotalMilliseconds;
Console.WriteLine("Transforming DataTableReplacement to DataTable: " + translatingTime);
Console.WriteLine("Total filling and transforming: " + (fillingTime+translatingTime));
}
}

public class DataTableReplacement : IEnumerable<IEnumerable<object>>
{
public DataTableReplacement()
{
_columns = new DtrColumnCollection();
_rows = new DtrRowCollection();
}

private readonly DtrColumnCollection _columns;
private readonly DtrRowCollection _rows;

public DtrColumnCollection Columns
{
get { return _columns; }
}

public DtrRowCollection Rows { get { return _rows; } }

public DtrRow NewRow()
{
return new DtrRow(this);
}

public DataTable ToDataTable()
{
var dt = new DataTable();
dt.BeginLoadData();
_columns.CreateColumns(dt);
_rows.CreateRows(dt);
dt.EndLoadData();
return dt;
}

#region Implementation of IEnumerable

public IEnumerator<IEnumerable<object>> GetEnumerator()
{
foreach (var row in _rows)
{
yield return row.ToArray();
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion
}

public class DtrRowCollection : IEnumerable<DtrRow>
{
private readonly List<DtrRow> _rows;

public DtrRowCollection()
{
_rows = new List<DtrRow>();
}

public void Add(DtrRow newRow)
{
_rows.Add(newRow);
}

public DtrRow this[int i]
{
get { return _rows[i]; }
}

public void CreateRows(DataTable dt)
{
foreach (var dtrRow in _rows)
{
dt.Rows.Add(dtrRow.ToArray());
}
}

#region Implementation of IEnumerable

public IEnumerator<DtrRow> GetEnumerator()
{
return _rows.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion
}

public class DtrRow
{
private readonly object[] _arr;
private readonly DataTableReplacement _dtr;

public DtrRow(DataTableReplacement dtr)
{
_dtr = dtr;
var columnCount = _dtr.Columns.Count;
_arr = new object[columnCount];
}

public object this[string columnName]
{
get
{
var index = _dtr.Columns.GetIndex(columnName);
return _arr[index];
}
set
{
var index = _dtr.Columns.GetIndex(columnName);
_arr[index] = value;
}
}

public object this[int columnIndex]
{
get
{
return _arr[columnIndex];
}
set
{
_arr[columnIndex] = value;
}
}

public object[] ToArray()
{
return _arr;
}
}

public class DtrColumnCollection
{
private readonly Dictionary<string, int> _columnIndexes;
private readonly Dictionary<string, Type> _columnTypes;

public DtrColumnCollection()
{
_columnIndexes = new Dictionary<string, int>();
_columnTypes = new Dictionary<string, Type>();
}

public int Count { get { return _columnIndexes.Count; } }

public void Add(string columnName, Type columnType)
{
var index = _columnIndexes.Count;
_columnIndexes.Add(columnName, index);
_columnTypes.Add(columnName, columnType);
}

public int GetIndex(string columnName)
{
return _columnIndexes[columnName];
}

public void CreateColumns(DataTable dt)
{
foreach (var pair in _columnTypes)
{
dt.Columns.Add(pair.Key, pair.Value);
}
}
}

As you can see, there is a DataTableReplacement class which uses three other classes instead of DataColumnCollection, DataRowCollection and DataRow. For this example alone, the DtrRowCollection could have been easily replaced with a List<DtrRow>, but I wanted to allow people to replace DataTable wherever they had written code without any change to the use code.

In the example above, on my computer, it takes 1300 milliseconds to populate the DataTable the old fashioned way, 1000 to populate it with BeginLoadData/EndLoadData, 110 seconds to populate the DataTableReplacement. It takes another 920 seconds to create a new DataTable with the same data (just in case you really need a DataTable), which brings the total time to 1030. So this is the overhead the DataTable brings for simple scenarios such as these.

Tuesday, 8 April 2008

The Winamp Playlist Generator

Winamp Plugins
A while ago there was this site called Pandora (similar to lastFM, but better) that tried to match songs based on their internal structure not user preference. By choosing which songs you liked or you didn't like it would guess your preferences and try to play only songs you would listen to.

Apparently Winamp has a little known (or blogged) addin that does this. It is called the Nullsoft Playlist Generator and comes bundled with WinAmp. This is how you use it:
  1. Open Winamp and go to Media Library
  2. Create a playlist (or more) and add all your songs there
  3. Right click on the playlist and select Send To: Add to Local Media
  4. Go to Options, Preferences, Plug-ins, Media Library and click on Nullsoft Playlist Generator
  5. Click on Configure selected plug-in, select your options and click Scan. I recommend the background scanning option.
  6. After the scan is complete (or during it) you can right click on any song and select "Play similar song to..." and you will listen to songs that this software thinks are similar

Playlist Generator

That's it. The analysis is pretty superficial, but still better than nothing. It is perfect when you have gigs of songs and you don't want to browse forever, selecting which one you want or you don't want.

Wake Up Call - Prodigy

I am obsessed by this song, I am just listening to it again and again, typing at hyperspeed while I am doing it (I wish I would type meaningful things, too :) ). The video itself is from Smack My Bitch Up, but YouTube again blocked a cool clip so I had to take another from some other place. I couldn't find the original video for the song. (One that wasn't a fan made anime clip :-|)

Update: even worse, all video platforms other than Youtube have been sued out of existence and even on YouTube the only versions of this song you find are live concerts. It's amazing: a beloved video of a famous song just vanished off the Internet... If you find it somewhere, please let me know. Meanwhile, this is a remix version...



Usage: Pump up the volume and get in front of a keyboard on monday morning. :)