Saturday, 24 January 2009

Invoking a button click event in ASP.NET when the button wasn’t clicked, but you want the code to run anyway.

I have just had a situation where I need a button click to invoke inside a control that part of a COTS product. On our login page, there’s the username and password fields, along with a login LinkButton control.

When the login button is clicked, or the user hits ENTER, then the login button click event handler invokes as it should.  However, on some mobile browsers and using my Dell Biometric reader for authentication, the button click is not performed because it’s doing a direct submit on the form – hence, the login button click event handler doesn’t invoke, therefore, the user cannot login.

After a bit of research, it seems there’s no easy way to just invoke a click on a button.  People suggest that I refactor the control so that it has a public “Login” method… however, this is a Community Server control… I don’t really want to customise the codebase for a product, for something so simple.

So the following does the trick via reflection

void Page_PreRender(object sender, EventArgs args) 
{
if (Page.IsPostBack
&& Request.Form["__EVENTTARGET"] != loginButton.UniqueID)
Login();
}

private void Login()
{
// Manually invoke login button click
Type t = loginButton.GetType();
object[] p = new object[1];
p[0] = EventArgs.Empty;
MethodInfo m = t.GetMethod("OnClick",
BindingFlags.NonPublic
| BindingFlags.Instance);
m.Invoke(loginButton, p);
}



Not the cleanest and most optimal solution, but it gets the job done!

Tuesday, 13 January 2009

Bulk Update with SQL Server

There appears to be no way in SQL Server to avoid an exclusive lock when updating multiple rows inside a table.  To ensure ACID, SQL Server prevents access to data that is being updated until the operation is complete.  I understand this; however, if the update operation spans multiple rows, surely there should be a way to ask SQL Server to treat each row update independently.

I have just written a cursor-based approach which performs a read with NOLOCK specified, and then the write is an update statement inside a WHILE construct.

DECLARE @LeadId INT
DECLARE
@TerritoryId INT

DECLARE
db_cursor CURSOR FOR
select
l.leadid, pc.territoryid
from lead l with(nolock)
inner join
postcode pc with(nolock)
on lower(pc.code) = lower(substring(l.postcode, 1, len(pc.code)))
inner join
territory t1 with(nolock) on t1.id = l.territoryid
inner join territory t2 with(nolock) on t2.id = pc.territoryid
where l.countryid = 1
and l.territoryid <> pc.territoryid

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @LeadId, @TerritoryId

WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE
Lead SET TerritoryId = @TerritoryId WHERE LeadId = @LeadId

print @LeadId
FETCH NEXT FROM db_cursor INTO @LeadId, @TerritoryId
END

CLOSE
db_cursor
DEALLOCATE db_cursor


It works fine, doesn’t cause any problems with data consistency, because ACID rules are at the single-row-update level; not the whole operation.  I’m surprised there’s not a way to ask SQL Server to do something like this by using the WITH hints and the like.