Showing posts with label Gotchas. Show all posts
Showing posts with label Gotchas. Show all posts

Saturday, 9 May 2009

Repeater NamingContainer Gotcha

Here was an odd thing. Take one repeater
<asp:Repeater runat="server">
<ItemTemplate>
<asp:CheckBox ID="chkBox1" AutoPostBack="true"
OnCheckedChanged="CheckBox_Click" runat="server" />
<asp:TextBox ID="txtBox1" runat="server" /><br />
</ItemTemplate>
</asp:Repeater>


Bind it up to something and get a list of textboxes and checkboxes. Like so



The task is to wire up the Checkbox click event so that when it is clicked the corresponding textbox is disabled i.e.

Easy peasy I thought and wired it up with this code
protected void CheckBox_Click(object sender, EventArgs e)
{
CheckBox myCheckbox = (CheckBox)sender;
TextBox textBox = (TextBox)Page.FindControl("textBoxId");
textBox.Enable = !myCheckbox.Enable;
}

The surprising result is that no matter which checkbox is clicked it is always the first textbox that is disabled. The problem is with finding the control. The line fourth line has changed -
protected void CheckBox_Click(object sender, EventArgs e)
{
CheckBox myCheckbox = (CheckBox)sender;
TextBox textBox = (TextBox)myCheckbox.NamingContainer.FindControl("textBoxId");
textBox.Enable = !myCheckbox.Enable;
}


I suddenly realised what a NamingContainer was. Previously I just thought it was a piece of arcane .Net knowledge – defined in MSDN as

Gets a reference to the server control's naming container, which creates a unique namespace for differentiating between server controls with the same ID.

Yeah right whatever. Well apparently it’s actually important stuff.

In the first example the code finds the first textBox called “textboxId” in the page. This is always the first one hence my code doesn’t work. In the second, corrected code – the control is searched for within the NamingContainer of the event sender – in this case it’s the RepeaterItem. So in the repeater there are any number of texboxes called “textboxId” but the don’t clash and can be accessed because each one exists in it’s own NamingContainer – i.e. unique naming space.
I appreciate this will be obvious stuff to many people but I felt like a super-brained coding rock star when I found this out. Then again I’m easily impressed.

Wednesday, 8 April 2009

.Net Control Collection Gotcha

Making puff pastry is something that sounds like it should be easy but when you’ve got the butter, flour and mixing bowl out it turns out to be excessively difficult. In the same vein – iterating through a collection of .Net controls and finding all the ones that are checkboxes sounds straight forward but is in fact a bit of a pen chewer.

My first attempt. This is a winner surely.
foreach (Control control in thisPanel.Controls)
{

if (control.GetType() is CheckBox)
{
//never going to work
}

}
Nope – the compiler complains straight away that ‘Control control’ will never be of type checkbox. OK sounds reasonable. After some discussion with colleagues a fellow programming minion suggested that it’s the foreach that’s the problem – so try the following.

for (int i = 0; i < thisPanel.Controls.Count; i++)
{
if (thisPanel.Controls[i].GetType() is CheckBox)
{
//won’t get here either
}
}
Looking good – but sadly again won’t work. The problem is that the control collection has already been downcast to a generic control. Therefore it doesn’t ‘know’ anything of its original type. What’s a boy to do? The best 4 programmers could come up with a combined amount of programming experience of over thirty years was –
foreach (Control control in thisPanel.Controls)
{
CheckBox checkBox = control as CheckBox;

if (checkBox != null)
{
//now we can do something
checkBox.Enabled = false;
}
}
This solves the problem so hurray. I have a nagging feeling that there is a better solution. It seems a simple request that should be achievable without guessing what types are in there. It’s probably something wildly clever with generics/delegates/lambda expressions or some such. Further pondering required I think.

Saturday, 28 March 2009

Hard-Coded Passwords Gotcha

Everyone agrees we should give to charity but not that many people do. In the same way everyone agrees we should write secure software but I’ve seen again and again web applications that you could break with a Swiss army knife and a bent paper clip. I think part of the problem that security advice seems overwhelming and hectoring. Reading the recent list of CWE/SANS Top 25 Most Dangerous Programming Errors is like been nagged at by your office manager. I can’t disagree but I can’t take in anymore.

The best way to know and appreciate something is to see it. CTW-259 warns of hardcoded passwords. It was only when I downloaded reflector that I realised how much of a problem this was.

Consider this fragment of a class
public class TestClass

{

private string _password = "supersecretpasswordnoonecansee";

private int _testInt;



/// <summary>

/// Test Int

/// </summary>



public int TestInt

{

get { return _testInt * 6; }

}



//etc....

}
That password is secure as houses when complied. Well once it’s loaded into Reflector and disassembled then probably not


That took me about 30 seconds to break. Reflector is a free download so it’s not even costly for the hacker. Bit more costly for the victim though.

Sunday, 14 December 2008

DataTable Filtering Gotcha

The mission – if you choose to accept it is to filter a .Net DataTable to return results from a year or more ago. The code is
DataTable dtDataTable = GetDataTableMethod()

//.. limit the dataset
DataView dvDataTable = new DataView(dtDataTable);
dvSurveyList.RowFilter = "DateField < '" + DateTime.Now.AddYears(-1) + "'";
Our spies tell us that it doesn’t actually work. Agent tech splurge what is the solution? Stand well back folks while I insert the missing line.
DataTable dtDataTable = GetDataTableMethod()
dtDataTable.Locale = CultureInfo.CurrentCulture;

//.. limit the dataset
DataView dvDataTable = new DataView(dtDataTable);
dvSurveyList.RowFilter = "DateField < '" + DateTime.Now.AddYears(-1) + "'";
To me this makes sense. If you’re playing with dates then you’ll need the culture information – dates being different in different cultures. The odd thing is that the first set of code worked perfectly – until it stopped working. I have to confess I still don’t know why.

Saturday, 6 December 2008

T-SQL Identity

This is probably as old as the hills but I still see this everywhere. What’s wrong with this line of T-SQL
Declare @PrimaryKeyVariable int
Set @PrimaryKeyVariable = @@IDENTITY
Nothing until it gets deployed into a high volume usage situation and the number of users ramps up after a few months. Then it will blow up and someone will be in at 3a.m. desperately trying to restore the referential integrity of the database.

The problem is that it will set the variable to the most recently set identity flag in the database no matter who has done it. That might be you or it might be another connection – thus linking up/deleting/updating the wrong records. If you’re lucky the transaction will fail. If not then you’ll be digging around in those two hourly transactional backups to get it back. Oops.

And the answer is really straight forward. Just use this instead
Declare @PrimaryKeyVariable int
Set @ PrimaryKeyVariable = Scope_Identity()
The identity is now only taken from current connection. It’s scoped – just like mother always used to scope her identities. Lovely.

Sunday, 23 November 2008

Website Publishing Gotcha

I was trying to publish a web site in Visual Studio 2005 this week and kept coming across this strange error when browsing to the published website.

Cannot convert type 'ASP.login_aspx' to 'System.Web.UI.WebControls.Login'

After some pen chewing, head scratching and news group ferreting I came across the solution in this posting.

Apparently if you call an aspx page the same as a standard WebControl then the publishing freaks out. Which fool would do that? Well this fool does on a regular basis. I always call my log in pages Login.aspx – the same as the login control. The solution – a quick rename of the page to Default.aspx and stride manfully out of the office declaring that the website is now released into the wild. Job done (and will be done a bit quicker next time).

Saturday, 15 November 2008

ExecuteNonQuery Gotcha

Here’s something that made me roll my eyes this week. I’ve been trying to get the number of rows executed returned from SQL Server to an ASP.Net data layer. It promised to be straight forward but it had a cheeky little gotcha nestled in the middle of it. To take the simplest example for demo purposes (all error handling type stuff omitted)

The stored procedure

CREATE PROCEDURE usp_TestProc
@PersonId int
AS
BEGIN
Set Nocount On

Update Person
Set
FirstName = 'Willy',
Surname = 'Wonka'
Where
Id = @PersonId
END

The calling business layer

conn.Open();
cmdNonQuery = conn.CreateCommand();
cmdNonQuery.CommandType = CommandType.StoredProcedure;
cmdNonQuery.CommandText = “usp_TestProc”;
cmdNonQuery.Parameters.AddRange(sqlParameters);
numberOfRecords = cmdNonQuery.ExecuteNonQuery();
 

Logic (and the .Net API documentation) dictates that the ExecuteNonQuery method returns the number of rows. But it steadfastly returns -1 no matter what is occurring in the database.

So what is happening?? It turns out that the Nocount setting stops this information coming back. Removing the statement – or setting NoCount Off causes the ExecuteNonQuery method to behave as expected. It returns -1 if no rows affected otherwise the number of rows actually affected.

This would probably be too arcane to worry about except the SQL Server standard template for creating stored procedures includes the Set NoCount On statement. In other words – select ‘create new procedure from the SQL Server menu’ and you get this template

CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName>
-- Add the parameters for the stored procedure here
<@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>,
<@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

-- Insert statements for procedure here
SELECT <@Param1, sysname, @p1>, <@Param2, sysname, @p2>
END
GO

Very lovely – except it contains the very code that disables the proc return values. Ouch.

Thursday, 2 October 2008

JavaScript and Html Not-Comments

Here’s a puzzle for your Mum, Grandma or beloved

Take one scoop of HTML

<body>
<!--element 1 -->
<div id="element1">Content</div>
<!-- element 2 -->
<div id="element2">Content</div>
</body>

And run a cheeky sprinkle of JavaScript against it


Var element = document.getElementById("element2").previousSibling
Alert(element);

Which element do you hit?

You don’t need to be a Web developing genius to work out that it going to pickup the div element2 then navigate back one so it will hit div element1. Easy! Welll – not really – it actually navigates to the comment. The alert spits out

<!-- element 2 -->

So if you’re navigating the JavaScript DOM that can be a real gotcha. What I found particularly interesting is that this code totally breaks my mental model of how all programming/scripting/markup languages work i.e. comments never count. They’re not included in binaries, they don’t count for code coverage analysis, they don’t display on the UI. Comments never count – except when they do.