Search

May 31, 2008

Duplicate key/value pair in Querystring

I was facing a problem, when the Application_AuthenticateRequest get fails and redirects to login page of the system along with the ReturnUrl which contains the actualy requested url.

Problem was like my ReturnUrl contains the same key twice, which leads to crash in system.

Apart from this, I found in Trace that the ReturnUrl is fine at the requested Page.
like... I made request for http://www.imran/MyProfile.aspx?id=102; it shows 302 as [redirected flag] and the ReturnUrl get the value of http://www.imran/MyProfile.aspx?id=102, but when it goes to Login.aspx it shows 2 query string params.

1. ReturnUrl = http://www.imran/MyProfile.aspx?id=102
2. id=102

Here is the problem, we can see the id is part of ReturnUrl, but in this case it generates following redirection url

Login.aspx?ReturnUrl=http://www.imran/MyProfile.aspx?id=102&id=102

Now when I ask for Request["id"], which gives me value comma saperate like 101,102 and this is what we can say a bug.

After searching long I found the solution, which include the Event that we have to capture in Global.asax, and one property of Response object which sets/gets the Http location Header, here is the code.


protected void Application_EndRequest(object sender, EventArgs e)
{
if (Response.RedirectLocation != null && Response.RedirectLocation.Contains("ReturnUrl"))
{
Response.RedirectLocation = string.Format("{0}ReturnUrl={1}",
Response.RedirectLocation.Remove(Response.RedirectLocation.IndexOf("ReturnUrl")
, (Request.RawUrl.Contains("ReturnUrl") ?
Request.RawUrl.Substring(Request.RawUrl.IndexOf("ReturnUrl") + 10)
: Request.RawUrl
)
));
}
}

This will add the RawUrl into ReturnUrl so there will be no duplication.

May 28, 2008

The generations of GC

As we all knows the .NET garbage collector provides a high-speed allocation service with good use of memory and no long-term fragmentation problems.

This article explains the generation of the GC.

When an object is created it will be on managed head. The managed heap are devided in mainly 3 groups, that are based on the age of variable. These is called Generations. Its useful to previent memory leak on managed heap. As far as performance is concern GC search the dead object on each of these generation at a time.

So lets see these groups [generations]

1. Gen0 [Generation 0]
The objects falles under this category are short leaving, and they are on the top zone of heap. GC is quick to collect and get them destroid, so this swipe is more ofen to release the memory space.

2. Gen1 [Generation 1]
It contains all the living objects from Gen0 that have survived to several Gen0 collects. That means they are upgraded from Generation 0 to Generation 1!!. The zone of Gen1 is in the middle of the heap and so the swipe is less then Gen0

3. Gen2 [Generation 2]
The last group which contains all the living objects from Gen1 that have survived to several Gen2 collects. The age of these object are logner then all and its expensive to destory them, by this reason GC will hardly swipe this area. Its in the lowest memore zone of the heap that is at the bottom.

Read more : Framework-Internals

Extension Methods in C# 3.0

Extension methods are static methods that can be invoked using instance method syntax. In effect, extension methods make it possible to extend existing types and constructed types with additional methods.
Note: Extension methods are less discoverable and more limited in functionality than instance methods. For those reasons, it is recommended that extension methods be used sparingly and only in situations where instance methods are not feasible or possible.
Extension members of other kinds, such as properties, events, and operators, are being considered but are currently not supported.
Lets see the example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Features
{
public static class Extensions
{
public static int ToInt32(this string s)
{
return Int32.Parse(s);
}
}

class Program
{
static void Main(string[] args)
{
string strTest = "1";
Console.WriteLine(strTest.ToInt32()); //Will print 1
}
}
}


Now lets look close to the following statement
(this string s)

This means, method ToInt32 is applied to a variable which type is string.

So simple as that :)

Reand More on C# 3.0

May 15, 2008

How one can generate objects scripts with DROP and Create in same file in SQl Server 2005 Like in SQl Server 2000

I often generate scripts of stored procedures and save that code in a txt file,in development env its very crucial because to overwrite stored procedures any other development db.

For my this task sql server 2000 was very good it generated drop and create statemntments implicitly. But now in sql server its very difficult and in our development environment there are more than 3000 procs so when i try to generate script in sql server 2005 ,first i need to generate "drop to" and then "create to" and it also take a very long time and often goes to stuck...

I found solution from Microsoft site, we just need to install SQL Server 2005 SP2.

If you interested to see whats new in SQL Server 2005 SP2 then check it out here.

May 7, 2008

Converting row to column in Sql Server 2005

Consider the following data and our target is to have all the ClientId starting form 247 to 252 will be in column
name Client1, Client2... etc. 
UserID      CaseID      CaseNumber  ClientID
----------- ----------- ----------- -----------
80 216 1087 247
80 216 1087 248
80 216 1087 249
80 216 1087 250
80 216 1087 251
80 216 1087 252
80 276 1140 328
80 277 1143 329
80 347 1191 438
80 348 1192 439

SQL Server 2005 introduced Ranking, Partitioning and Pivoting. By using all togather
we can achive our goal.



Ranking functions that provide the ability to rank a record within a partition. 
In this case, we can use RANK() to assign a unique number for each record, and partition
by the ClientID (so that the RANK will reset for each ClientID)

By prefixing some text to the rank number, we end up with something like:


SELECT UserID, C.CaseID, CaseNumber, ClientID, 
'ClientId' + CAST(
RANK() OVER (
PARTITION BY C.CaseID, CaseNumber
ORDER BY ClientID) AS VARCHAR(10)) ClientIdListing
FROM [Case] C, CaseClient CC
WHERE C.CaseId = CC.CaseID AND USerID = 80

Result:


UserID      CaseID      CaseNumber  ClientID    ClientIdListing
----------- ----------- ----------- ----------- ------------------
80 216 1087 247 ClientId1
80 216 1087 248 ClientId2
80 216 1087 249 ClientId3
80 216 1087 250 ClientId4
80 216 1087 251 ClientId5
80 216 1087 252 ClientId6
80 276 1140 328 ClientId1
80 277 1143 329 ClientId1
80 347 1191 438 ClientId1
80 348 1192 439 ClientId1

The new column (ClientIdListing) is the concatenation of the literal string "ClientId"
and the string representation of the number that the RANK function returned. But
the bigger point is that now this column can be used for pivoting, and result in
a series of new columns called [ClientId1], [ClientId2], [ClientId3], etc.




Pivoting in SQL Server 2005 requires explicit declaration of values as a column
list. In this case, we can't just say "Pivot on the ClientIdListing column", but
rather must say "Pivot on the ClientIdListing column, and make new columns only
for these specific values". This restriction is a little bit of a downside because
we need knowledge of the values in the column. Or, in this case, we need to know
how many ClientIds a Case could possibly have so that we create enough columns in
the result.




So here is the final query:

SELECT * FROM
(SELECT UserID, C.CaseID, CaseNumber, ClientID, 'ClientId'
+ CAST(RANK() OVER (PARTITION BY C.CaseID, CaseNumber
ORDER BY ClientID)
AS VARCHAR(10)) ClientIdListing
FROM [Case] C, CaseClient CC
WHERE C.CaseId = CC.CaseID
AND USerID = 80) P

PIVOT
(MAX(ClientID) FOR ClientIdListing IN
(ClientID1, ClientID2, ClientID3, ClientID4, ClientID5, ClientID6)
) AS Clients

And here is the Output:


UserID CaseID CaseNumber ClientID1 ClientID2 ClientID3 ClientID4 ClientID5 ClientID6
----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
80 216 1087 247 248 249 250 251 252
80 276 1140 328 NULL NULL NULL NULL NULL
80 277 1143 329 NULL NULL NULL NULL NULL
80 347 1191 438 NULL NULL NULL NULL NULL
80 348 1192 439 NULL NULL NULL NULL NULL

May 4, 2008

Multiple Active Result Sets - Yet another powerful feature of SQL Server 2005

MARS [Multiple Active Result Sets ] is a new SQL Server 2005 feature that allows the user to run more than one SQL batch on an open connection at the same time.

If you for instance wanted to do some processing of the data in your data reader and updating the processed data back to the database you had to use another connection object which again hurts performance. There was no way to use the same opened connection easily for more than one batch at the time. There are of course server side cursors but they have drawbacks like performance and ability to operate only on a single select statement at the time.

SQL Server 2005 team recognized the above mentioned drawback and introduced MARS. So now it is possible to use a single opened connection for more than one batch. A simple way of demonstrating MARS in action is with this code:


string strConn = "Data Source=[DATASOURCE];Initial Catalog=[DATABASE];User ID=[UID];Password=[PWD];MultipleActiveResultSets=true";
string strSql = "select DoctorId, PatientId from [Patient] where DoctorId = {0}";
string strOutput = "<br/>DoctorId:{0} - PatientId{1}";

using (SqlConnection con = new SqlConnection(strConn))
{
//Opening Connection
con.Open();

//Creating two commands form current connection
SqlCommand cmd1 = con.CreateCommand();
SqlCommand cmd2 = con.CreateCommand();

//Set the comment type
cmd1.CommandType = CommandType.Text;
cmd2.CommandType = CommandType.Text;

//Setting the command text to first command
cmd1.CommandText = "select distinct DoctorId from [Doctor] where HospitalId = 8";



//Execute the first command
IDataReader idr1 = cmd1.ExecuteReader();

while (idr1.Read())
{
//Read the first doctor from data source
int intDoctorId = idr1.GetInt32(0);

//create another command, which get patients of doctor
cmd2.CommandText = string.Format(strSql, intDoctorId);

//Execute the reader
IDataReader idr2 = cmd2.ExecuteReader();

while (idr2.Read())
{
//Read the doctor and patient
Response.Write(string.Format(strOutput, idr2.GetInt32(0), idr2.GetInt32(1)));
}
//Dont forgot to close second reader, this will just close reader not connection
idr2.Close();
}
}



MARS is disabled by default on the Connection object. You have to enable it with the addition of MultipleActiveResultSets=true in your connection string.