Wednesday, November 28, 2007

SQL 2005 Server Side Paging using CTE (Common Table Expression)

Submit this story to DotNetKicks

When a application want to show the result from an SQL-query as a paged view, i.e. showing ten or fifteen result on first page, then have a "next" function to show the next page of results.

To minimize traffic over the network, it is best practice to do the paging on the SQL-server, so that only the results you want to show is sendt to the application.

With SQL Server 2005 this is quite easy using the new CTE capabilities and the new ROW_NUMBER() function.

(using the AdventureWorks example database for SQL-2005 http://codeplex.com/)

Consider the following T-SQL to select out all employees from Person.Contact:

SELECT [FirstName]
,[MiddleName]
,[LastName]
,[EmailAddress]
FROM [Person].[Contact]


This will result in 19972 rows returned, and the paging logic has to be done on the client application. not good.. so first we implement the ROW_NUMBER() function like this:

SELECT ROW_NUMBER() OVER (Order by [Person].[Contact].[LastName]) AS RowID,
[FirstName]
,[MiddleName]
,[LastName]
,[EmailAddress]
FROM [Person].[Contact]


This will create a unique RowID for each row in the result.

Now we need to wrap the result in a CTE using just the WITH [ctename] AS () statement:

WITH AllEmployees AS
(SELECT ROW_NUMBER() OVER (Order by [Person].[Contact].[LastName]) AS RowID,
[FirstName]
,[MiddleName]
,[LastName]
,[EmailAddress]
FROM [Person].[Contact])


Now we have all the Employees in a in-memory table called AllEmployees, and we can select from this table as any other table, with all the common clauses. Simplest term:

SELECT [FirstName]
,[MiddleName]
,[LastName]
,[EmailAddress]
FROM AllEmployees


Then, to use this for a Server-side paging solution, the simplest way we use the RowID to establish what rows to return, either by using DECLARE [varname] or by putting the whole code into a Parameterized StoredProcedure like this:

CREATE PROC GetPagedEmployees (@NumbersOnPage INT=25,@PageNumb INT = 1)
AS BEGIN

WITH AllEmployees AS
(SELECT ROW_NUMBER() OVER (Order by [Person].[Contact].[LastName]) AS RowID,
[FirstName],[MiddleName],[LastName],[EmailAddress] FROM [Person].[Contact])


SELECT [FirstName],[MiddleName],[LastName],[EmailAddress]
FROM AllEmployees WHERE RowID BETWEEN
((@PageNumb - 1) * @NumbersOnPage) + 1 AND @PageNumb * NumbersOnPage
ORDER BY RowID

END

The parameter for this procedure is used for the paging and when executed it will return a result based on how many results per page, and what page number to return, like this:

EXEC GetPagedEmployees 15,2 -- will return 15 employees from id = 16 and up (page 2)

This technique can be used for all type of results where you want to return a fixed number of results, paged server-side.

/Leo

Remote desktop stops working

Submit this story to DotNetKicks

Suddenly my workstation wouldn't accept incoming remote desktop sessions. I first thought it was because I was connecting from a Vista computer, but as it turned out, I couldn't even connect from other 2003 servers. The event log only said this:

Application popup: : \SystemRoot\System32\RDPDD.dll failed to load

Which was not very helpful! After googling around quite a bit, I had tried reinstalling the RDP software's latest version and rebooted, when I found multiple people describing the same problem and fixing it by removing their ATI video drivers... As I have a nvidia card, I first thought it had nothing to do with this. However... there wasn't much else to try. I uninstalled the Asus Enhanced Video Driver and the nvidia drivers - and then it worked. Crazy. Worst part is, now I've reinstalled the video driver, and remote desktop still works. Huh.

So it could be that Asus Enhanced Video Driver is the culprit. I won't try and find out.

Thursday, November 1, 2007

IIS and .net 3.5 beta 2

Submit this story to DotNetKicks

We installed .net 3.5 beta 2 on a web server this morning. It did not work. Even our 2.0 webs stopped working. So to save others the trouble...
Go ahead to
%WINDIR%\Microsoft.NET\Framework\v.2.0.something...\CONFIG\web.config

Go ahead and remove all instances of xmlns="" and you're good to go!

Also worth mentioning, 3.5 and 3.0 does not appear in the ASP.NET tag on IIS, only 2.0 does. This is because 3.0 and 3.5 are extensions of the 2.0 framework.

Friday, October 19, 2007

Online MachineKey Generator

Submit this story to DotNetKicks

Found this today, when creating MachineKey for a web farm on ASP.NET:
http://www.orcsweb.com/articles/aspnetmachinekey.aspx

Will save you some time when creating webapps that run in a web farm environment, and it does work.

Session_End and SessionState mode="SQLServer"

Submit this story to DotNetKicks

A webapp I'm working on now needs to keep track of the number of logged in users at any time. We used to rely on Session_End for deleting sessions. However, this turned out to work only if the SessionState mode was set to InProc.  For other scenarios, Session_End is simply not called. Ever.

So we programmed a background service that polls the database now and then, and deletes from our table the rows that don't exist in the AspState-database (AspState obviously keeps track of timed out sessions and removes them from the table). Clean and simple solution.

Tuesday, October 16, 2007

Using using

Submit this story to DotNetKicks

When writing code that makes use of alot of unmanaged memory resources, all objects should be disposed of before exiting methods and such. The keyword using is often forgotten, and also sometimes seen as messy. example:

using (Bitmap bitmap1 = new Bitmap(100, 100))
{
Console.WriteLine("Width:{0}, Height:{1}", bitmap1.Width, bitmap1.Height);
}

should basically be the same as

Bitmap bitmap1 = new Bitmap(100, 100)
Console.WriteLine("Width:{0}, Height:{1}", bitmap1.Width, bitmap1.Height);
bitmap1.Dispose();

shouldn't it?
No, not really... The using-clause does a little more than this. Actually, my using-example is equivalent to the following block of code.

Bitmap bitmap1 = new Bitmap(100, 100);
try
{
Console.WriteLine("Width:{0}, Height:{1}", bitmap1.Width, Bitmap1.Height);
}
finally
{
if (bitmap1 != null)
{
bitmap1.Dispose();
}
}
Console.ReadLine();


Imagine writing nested using-clauses, and try writing the expanded code for that :)
Using using really cleans up your code, and your memory. So use it!


Edit: Oh, as Stian commented. Using should be used for all objects that implements the IDisposable interface. But remember - if you plan on implementing this interface on your own classes, you need to make sure it fully implements all the necessary methods for cleaning up. Otherwise it will all be to waste :)

Monday, October 15, 2007

LINQ and Stored Procedures

Submit this story to DotNetKicks

Just starting off with LINQ, I've already found a strange behavior. In my SP, I have to select some blank columns, that used to exist in the table. Now, they're only there to stop old apps from crashing:

Select '' as inst from footable

This made Linq return only a cryptic error message:
System.FormatException: String must be exactly one character long.
at System.Data.Linq.DBConvert.ChangeType(Object obj, Type type)


But when I replaced the '' in the SP with a ' ' (that is, added a blank space) - everything works. So it looks as if Linq is trying to type every column in the result, and if it's empty, it dies.

Oh, and if you have a variable named @@foo in your script - I don't know why it was called @@foo, it just was - Linq will not be able to run it! It presumes that all variables are named @foo, which probably is the correct SQL, by the way. But - the error message, again, is not so helpful: The procedure expects the parameter "foo", which was not provided.