Showing posts with label Technology. Show all posts
Showing posts with label Technology. Show all posts

Monday, February 15, 2010

Difference in Repeat behavior between Windows Media Player and Winamp

Most of you would have used at the Windows media player (WMP) or Winamp or both as your music player of choice. Both of them provide 2 options to control the sequence in which songs are played. These are Shuffle and Repeat. The definition of Shuffle is the same in both the players and basically means that instead of the songs being played sequentially, they will be chosen randomly from the playlist. However, there is a subtle difference in the definition of Repeat. Let me try to highlight this difference in this post.

There are 4 combinations possible between Shuffle and Repeat. The table below highlights the behavior of each player for the various combinations.

Shuffle/Repeat combination

Windows Media Player

Winamp

Shuffle – OFF
Repeat – OFF
Plays songs in sequence. Cycles to the beginning of the list once the last song is played Plays songs in sequence. Stops playing songs once the last song is played
Shuffle – OFF
Repeat – ON
Plays songs in sequence. Cycles to the beginning of the list once the last song is played Plays songs in sequence. Cycles to the beginning of the list once the last song is played
Shuffle – ON
Repeat – OFF
Plays songs randomly. Cycles to the beginning of the list once the last song is played Plays songs randomly and stops when each song in the playlist has been played once
Shuffle – ON
Repeat – ON
Plays songs randomly. Songs can be repeated even when all the songs in the playlist have not yet been played at least once Plays songs randomly. Songs can be repeated even when all the songs in the playlist have not yet been played at least once

 

As is evident from the above table, the difference in the behaviors of both the players comes when Repeat is OFF. This is because of 2 reasons:

  • Repeat in Winamp means turning repeat of song and playlist on/off
  • Repeat in WMP only means turning repeat of songs on or off. Repeat of playlist is not under the control of the user. Once the playlist is complete, WMP will automatically cycle back to the beginning of the list.

Given this understanding of Repeat, when using Shuffle in WMP, based on the playlist type, we should set the Repeat as on/off as follows:

  • Playlist with few songs : We might want to set Repeat OFF so that all the songs in the playlist have an equal chance of being played. No song will be repeated till ALL songs have been played once
  • Playlist with large number of songs : We might want to set Repeat ON otherwise we might have to wait a long time before a song we hear once gets played again

Hope this post helps in clarifying the difference in behavior of both these popular players and aids you in choosing the right setting when listening to your favorite songs.

Sunday, October 5, 2008

Dataset v/s DataReader

In this post, I will be discussing the behavioral characteristics and the performance differences of the DataSet and the DataReader as well as indicate the suitability of use of these objects in various scenarios.

The Dataset is a "disconnected" data store. What this means is that the DataSet object need not maintain a connection with the database at all times, a connection is needed only at the time of fetching data and updating it. The DataSet can be populated with data using something like this ,

SqlConnection conn=new SqlConnection();
conn.ConnectionString="Data Source=.;Database=TempDB;Integrated Security=true;";
SqlDataAdapter da=new SqlDataAdapter("select * from Temp",conn);
DataSet ds=new DataSet();
da.Fill(ds);
conn.Close();
//Process data in the DataSet ds

As can be seen from the above snippet, once the data has been read into the DataSet, the connection can be closed immediately. The data can still be accessed from within the DataSet.

DataReader, on the other hand, is a "connected" data store which means that there needs to be a connection maintained to the database in order to be able to access the values in the DataReader. The DataReader can be populated with data using something like this ,


SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=.;Database=TempDB;Integrated Security=true;";
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "select * from Temp";
cmd.CommandType = CommandType.Text;
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
//Process the data read from the DB
}
conn.Close();

Notice here that the connection is closed only after iterating through the entire record set returned by the query.

As can be seen from above, a DataSet, although providing a lot of flexibility in terms of usage scenarios, is memory intensive. Since it is a disconnected data store, it stores all the data read from the database in memory. As can be inferred, this can really slow down the application if the DataSet is populated with millions of rows of data. On the other hand, the DataSet provides "random" access : any record stored in it can be directly accessed and records can be accessed in any order desired. One can also go back and forth through the DataSet records. Another important flexibility with DataSets is that data within it can be modified and the updates will be percolated to the database automatically. Thus, a DataSet is suitable for scenarios in which data update is needed or where random access is needed but should be used cautiously when huge data is being fetched from the database.

As for the DataReader, it is almost an opposite of the DataSet. Since it maintains an open connection to the database at all times, it needn't store data in local memory. Instead records are read in chunks on a need basis. Thus, it proves to be pretty efficient in terms of memory usage. However, this efficiency comes at a cost : records within a DataReader can be traversed only forward and that too, only once. If a record needs to be read a second time, the query needs to be executed again as there is no provision to move backwards through the DataReader. Also the data read through the DataReader is read only. Thus, a DataReader lends itself to scenarios where huge amounts of data are being read without having the need to update them or have random access over that data.

Accessing return value of SPs

When working with databases, it is recommended that all the database queries should be moved to Stored Procedures (hereafter referred to as SP). This makes perfect sense because having all the queries in one place makes it easy to debug in case the database integration is not working as well as makes it less cumbersome to make changes (since it is known where those changes need to be made and the changes need not be duplicated in multiple code files). Given this best practice, it is often a requirement to capture the return value of the SP in order to determine whether the SP execution succeeded or failed.

ADO.NET has the SqlCommand and SqlConnection objects to allow the user to invoke the SP and have access to the results returned by the SP. Specifically, there are 3 methods that can be used to execute a query or an SP on the database :
  1. SqlCommand.ExecuteNonQuery() : Used for queries which don't return any value, i.e. insert, update and delete queries
  2. SqlCommand.ExecuteScalar() : Used for queries which are guaranteed to return a single value
  3. SqlCommand.ExecuteReader() : Used for queries that return multi column and/or multi row result sets

There is 1 important difference in the use of these 3 methods when it comes to accessing return values. It turns out that when using ExecuteScalar() and ExecuteNonQuery(), we can access the return value immediately following this method call whereas, for the ExecuteReader() method this is not the case. If we try to access the value of the parameter object created for the return value, it will have a null value. The return value is set only after we iterate through the entire result set returned by the reader object. The reasoning behind this behaviour can be as follows. If the result set was returned successfully, it means the SP succeeded so there's no point of checking the return value. It only makes sense to check the return value in case there was no result returned. The return value will then enable us to determine whether there was actually no data in the database for the given query or there is a bug which caused incorrect results to be returned.

Do keep this slight variation in the behaviour of the ExecuteReader() the next time you use it.

Sunday, September 21, 2008

Javascript : Creating Textbox watermarks

One of the common requirements when working with web page forms is to have some default text show within a text box and make it disappear when the user enters something in the text box, in other words, create a text box watermark.

Now if you are working with ASP.NET then an easy way to do this is to use the AJAX Control Toolkit's 'TextboxWatermark' control and just link the AJAX control to the text box in which we want the watermark to appear. The AJAX control provides a number of options to control the watermark. However, if you are working with pure HTML pages then, you are stuck with Javascript and need to figure out a way to control the watermarking. I recently wrote some code to do the same and would like to share it in this post.

Assuming you have a html page added to your project, the following steps would enable the watermarking of the text box :

  1. The first step obviously is to define the basic HTML tag which will add a normal text box to your page. Here's a sample code for this :

    <input id="txtUsername" name="txtUsername">

  2. The next step is to define a variable which will store the string that will act as the watermark (Thanks Pratik for sharing this best practice with me). The advantage of creating a variable is that it makes it easy to perform comparisons (which will be mentioned in a later step) and also requires you to change the watermark text in only one place.

    var gcUsernameTbDefaultText='Please enter a username';

  3. The trick to have the watermark appear and disappear is to capture the 'onblur' and 'onfocus' events and manipulate the contents of the text box. The following code snippet shows functions being called for these 2 events.

    <input type="text" id="txtUsername" name="txtUsername" onfocus="clearTextbox('txtUsername',gcUsernameTbDefaultText);" onblur="showDefaultText('txtUsername',gcUsernameTbDefaultText);" />

    As you can see, the functions called when the events occur are passed the id of the text box on which the watermark is needed and also the watermark text, making these functions generic and usable with any text box.

  4. The last and final step is to implement the 2 functions that do the actual manipulation. So here they are :

    • function clearTextbox(objId,text)
      {
      try
      {
      var obj = document.getElementById(objId);
      //Clear the textbox only if the textbox contains the original default value
      if (obj != null && obj.value.toString().toLowerCase() == text.toLowerCase())
      {
      obj.value = "";
      }
      }
      catch (e)
      {
      alert(e.message);
      }
      }

    • function showDefaultText(objId,text)
      {
      try
      {
      var obj = document.getElementById(objId);
      if (obj != null && obj.value == "")
      {
      obj.value = text;
      }
      }
      catch (e)
      {
      alert(e.message);
      }
      }

So there you have it, simple text box watermarking. There are 2 improvements one can do on this implementation :

  1. Dynamically apply styles in order to show the watermark text in gray, with some sort of transparency and have the normal text with normal appearance
  2. This approach won't work with 'password' text boxes since even the watermark will appear as asterisks. This post talks about implementing watermarks for password text boxes