Wednesday, January 24, 2007

Today I have found a funny example of how to ensure the checkbox is not visible:

<input id="chkIsDirty" type="checkbox" readonly style="DISPLAY: none;
VISIBILITY: hidden; BACKGROUND-COLOR: transparent" size="0" runat="server"
name="chkIsDirty">

I have emphasized the important elements. I wonder if it is also hidden by the style sheets and maybe in the code via the runat="server" attribute :-)

Wednesday, January 24, 2007 3:23:10 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  | 
 Tuesday, January 16, 2007

In most of the ASP.NET projects I have been working on, I have needed a way to easily navigate to any page in the solution. To accomplish this I usually created some kind of User Control with links to all pages. If the project was small I usually entered the links by hand. For bigger projects I have used a Repeater control and filled it with data from the file system.
The method used to get the data from the file system looks more or less like this:

public string[] GetAllPages()
{
    int applicationFolderNumberOfCharacters = HttpRuntime.AppDomainAppPath.Length;
    List<string> pages = new List<string>();
    string[] files = Directory.GetFiles(
      HttpRuntime.AppDomainAppPath, "*.aspx", SearchOption.AllDirectories); return Array.ConvertAll<string, string>(files, delegate(string file) { return "~/" +
         file.Substring(applicationFolderNumberOfCharacters).Replace(@"\", "/"); }); }

On the Page or User Control the Repeater I use looks as follows:

<asp:Repeater ID="Links" runat="server">
   
<ItemTemplate>
      
<a runat="server" href='<%# Container.DataItem %>'><%# Container.DataItem%></a><br />
   
</ItemTemplate>
</
asp:Repeater>

If you put it on the Master Page, you will get access to every page from every page, which is great during development. Just don't forget to remove it from the production code.

kick it on DotNetKicks.com

Tuesday, January 16, 2007 7:38:57 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 

Today Mads shows us a not well known class that is called a Triplet. He also asks a question: how you would use it. Fortunately I have seen this class before so here is what I have learned:

Triplet is used in the same way as a Pair class - mostly in the LoadViewState and SaveViewState methods. Generally if you are saving view state in your control you not only have to store your data, but also the data returned by the call to the base.SaveViewState method. This is where the Pair class or the Triplet come in handy since given two (three) fields, one is used for the base control's state and the other can be used by your state. Similarly, when loading the state in the LoadViewState method you get your own state from one of the fields, and pass the other to the base class'es LoadViewState method. You have to remember to pass the same field you have used in the SaveViewState method or the things will go nasty ;-).

Triplet is used when Pair is not enough. If Triplet is not enough, you have to create your own class.

I hope this sheds some light on the topic.

Tuesday, January 16, 2007 6:55:47 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  | 
 Friday, January 12, 2007

The "as" operator as described in MSDN is an operator: "Used to perform conversions between compatible reference types". We have to keep this in mind when coding. Often it is just easier (and it looks nicer too) to use an "as" operator instead of a cast, or when we want to handle the DBNull value returned from the database like in the following scenario:

string s = reader["Name"] as string;

instead of

string s = (string)((reader["Name"] is DBnull) ? null : reader["Name"]);

Assuming that the name column can be null in the database.

When we use it we accept that something could be of incompatible type such as Name column being of numeric type return. What happens then? What happens if the column type has changed? In the scenario above, we will always get a null reference, which in a worst case scenario we will discover very late in project.

Of course in some situations it is just what we need. What I have found however is that misusing the "as" operator can often lead to unexpected NullReferrenceException exceptions being thrown (or worse yet nothing wrong happens at first). If you find that happening it may mean that you are using "as" operator as a shortcut where an ordinary cast would be in place. Using a cast often causes bugs to appear sooner in the development life cycle which is always a nice thing. So basically a rule of thumb is to always think twice whether to use a cast or an "as" operator.

kick it on DotNetKicks.com

Friday, January 12, 2007 12:17:26 AM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  | 

Since the beginning of my adventure with .NET I have always asked myself one question: why do we need the DBNull type? Is a simple null reference not enough?
MSDN says that DBNull "Represents a nonexistent value". From a logical point of view - this one sentence explains why a null reference cannot be used - because it is a null reference and not a lack of value. But is it enough to introduce a type that causes a lot of trouble?

DBNull is typically used in ADO.NET when returning data from the database. If one column does not have a value in the database, it will be represented as a DBNull object. That is something altogether different from what I would expect at the domain level. When working with objects when there is a Client object, and I ask for his Name then if the name is not there I expect to get null in return. It is very natural to me and probably to most of the developers. When we need to interact with data layer that is where most of the problems with DBNull kick in.

Consider a simple example of a Client's Name property which is of a String type. In your code you accept a situation when the Name is null which may mean that it has never been set. When you save such a Client to a database using a simple Insert call, everything works OK. (it was in fact my data access library that handled null inserts by changing them to DBNulls) You check the database and the data is in fact there and the Name is "null". When next time you try to get the Client from the database, you have to read all the columns and put the values into your object like in the following example:

Client c = new Client();
c.Id = (Guid)row["Id"];
c.Name = (string)row["Name"];
...

This will result in an InvalidCastException exception being thrown with a message "Unable to cast object of type 'System.DBNull' to type 'System.String'." That is of course because null values from the database are represented as DBNull objects.

Typical solution for this problem is to check the column for DBNull before casting or using an "as" operator which you have to be carefull with as I describe in another post.

Whichever way you choose, what you get is an overly complicated code that does a simple thing!

I don't think that the sentence from MSDN that describes the purpose of DBNull is a good excuse for introducing DBNull type. I don't think that returning null instead of DBNull would cause any loss of information. What I think is that DBNull should have never been introduced because it makes developer's work harder without adding any real value. Additionally ADO.NET is inconsistent in that it allows saving a null referrence to a database but does return DBNull instead. Because of the fact that I don't like to deal with DBNulls, in my data access library, I'm always replacing it with a null reference so I never get it to propagate to the upper layers. That way I get a better separation from a database level stuff.

BTW: If anyone has something to say in defense of the DBNull I woulreferenced gladly hear it.

kick it on DotNetKicks.com

Friday, January 12, 2007 12:08:55 AM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [8]  | 
 Thursday, January 11, 2007

It is a common practice in ASP.NET development to use controls provided by third parties. Such controls are usually distributed in form of an assmebly .dll files which you put into the Bin folder of your ASP.NET application. When using Visual Studio, you just add a reference to a file and the file is copied to that directory for you. What you don't get are new icons on the Toolbox representing newly added controls. You have to have to go to the Toolbox, select Choose Items... from context menu and point to the same dll file you have just used when adding a reference.

A much easier way to accomplish the same thing is to just do this second step though. By adding items to a Toolbox, Visual Studio automatically copies the required files into the Bin folder and by doing so, creates a reference to it so you don't have to.

When adding adding items to the Toolbox I usually prefer to create a separate Tab and add items there. For each group of controls I create a new Tab. The reason for this is that it is much easier to remove a whole Tab from the Toolbox than each individual item at a time. (You can also use the Choose Items... window to remove unneeded items, but I have found removing the Tab a lot easier and faster).

The third and in my opinion the best way of adding third party controls to a project is to create a separate project in the solution. In this project, for each control you want in your Toolbox, create a class and inherit it from this control. I have found that Visual Studio 2005 automatically adds to the Toolbox, controls that it finds in the Solution. An advantage of such an approach (other than that it is generally a good idea to always do so) is that now you can go and reset the Toolbox and your controls will still be there - after Visual Studio decides it is time :-) which usually happens when switching from html/aspx view do design view.

kick it on DotNetKicks.com

Thursday, January 11, 2007 11:58:42 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
 Thursday, January 04, 2007

There is a very useful class in the .NET framework that allows you to cancel an event from the handler perspective. This class is the CancelEventArgs. Typically using this class looks as follows:

event EventHandler<CancelEventArgs> Click;
void OnClick()
{
    CancelEventArgs e = new CancelEventArgs();
    Click(this, e);
    if (e.Cancel)
    {
        // do nothing
    }
    else
    {
        // do something
    }
}

void ClickHandler(object sender, CancelEventArgs e)
{
    if (something)
    {
        e.Cancel = true;
    }
}

BTW: I have no idea why Microsoft defined CancelEventArgs under System.ComponentModel namespace.

Simple and easy. But there is a problem. What if there was a second handler for the Click event?

void ClickHandler2(object sender, CancelEventArgs e)
{
    e.Cancel = !IsValid();
}

In some scenarios, the ClickHandler2 would override changes made by ClickHandler - i.e. it would set Cancel property to false in case the state was Valid. I strongly discourage you from ever setting Cancel property to false! The problem is that in the handler method you never know if you are the only one handler or are there more handlers involved! If a previous handler had already cancelled the event you will lose this information!

In order to prevent potential mistakes, I propose an alternative version of CancelEventArgs. The version that will prevent you from making a mistake. Below is a how such a class could look like:

class CancelEventArgs : EventArgs
{
    bool cancelled;
    public bool Cancelled
    {
        get { return cancelled; }
    }
    public void Cancel()
    {
        cancelled = true;
    }
}

Notice that the Cancel property is now called Cancelled and is read-only. To Cancel an event you use the Cancel method which works only one way - there is no way to accidentally set Cancelled to false. The safer, the better :-)

kick it on DotNetKicks.com

Thursday, January 04, 2007 10:34:37 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [4]  | 
 Thursday, December 21, 2006

I have found a post on Web Development Team's blog providing SOME information on what was changed/fixed with the new Service Pack. There is also another post on Scott Guthrie's blog on the same topic. Good!

But wait! Am I really expected to look all over the Internet and gather pieces of information just to build a list that SHOULD be available in a KNOWN and EASILY ACCESSIBLE location? You got to be kidding!

Thursday, December 21, 2006 8:33:04 AM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 

For some time now I have been having problems with "Connect To Server" window of Microsoft SQL Server Management Studio. The standard problem is that has problems remembering the password, or rather it has a habit of forgetting. The effect is that each time I run the MSSMS, there is a chance that my password will not be there even if I have checked the check box to remember it before. I'm currently running Windows 2003 Server, but I had exact the same problem on Windows XP.

Over a time I've got used to it and I no longer bother to check the "Remember Password" check box. Today however I have began to have a new suspicion.

Almost every time I try to log in to the server for the first time, I get a message that login or password was incorrect. I have always reentered my password thinking that I had made a typo. Some times it took me three times to get it right and that made me think.

Why is that window different? When I use the same password for other windows, my success/error rate is almost zero. Here I'm far from that. So I've investigated the issue.

The goal was simple: after entering a user name and password and getting a failed login attempt saying that the user name or password was incorrect, check if the message is right. Options were just to retry without changing anything or compare the entered text with what should be entered.

The first method was too easy :-). So I have decided to see what's in there. User name was a plain text so it was easy. And since it IS remembered, I have confirmed no error there. As for the password. I have used the Runtime Object Editor, to look under the hood. And guess what? Password was also correct!

Given the investigation I can say with certainty that there is a bug in the MS SMS! Such a simple function and it doesn't work!!! I have found one similar bug report. Not exactly the same but I think it is somehow connected. Imagine that. Big company, lots of money, lots of time, one small window and so many bugs!

kick it on DotNetKicks.com

Thursday, December 21, 2006 12:41:53 AM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [6]  | 
 Tuesday, December 19, 2006

Now I have a brand new Visual Studio 2005 Service Pack 1 installed. I'm happy with it, but I would be even more happy if I knew what was fixed by the installation. And so I went searching.

After a while, I just gave up! It is just to hard for me to accomplish the task of finding some kind of change log for the Service Pack 1! What I have found were Visual Studio 2005 Service Pack 1 release notes. On this page hidden in the summary section, just before a list of requirements and a very long list of installation issues, there it was, the link to What’s New in Visual Studio 2005 SP1. I have followed the link.

On the destination page one thing caught my attention - the topic: "What's New in Visual Studio 2005, This topic has been updated for Visual Studio 2005 SP1". Scrolling down a little, confirmed my suspicions. It is not a list of new features or bug fixes that were distributed with the new Service Pack 1. It is an overview of features the Visual Studio has now, after you install SP1. More Over! Look at the "New in Visual Studio 2005 SP1" section!!! Its empty.

I don't suppose that Microsoft is so evil not to provide some kind of change log. I only know that they just cannot make their own website usable and put the interesting things somewhere where people can actually find them. So, if anyone knows where can I get the list of features introduced with the new SP1 and the list of bug fixed, I would be grateful if he shares the knowledge.

kick it on DotNetKicks.com

Tuesday, December 19, 2006 7:52:33 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |