Recently we faced a strange issue in one of the production servers. The session information was shared between 2 users in the ASP.net application. We then thought to print the Session ID in the browser and was perplexed to watch that 2 different sessions printing the same session id. I then googled out to see the possibility of
a session id getting duplicated but it was never possible. Then I got a vital information in the URL below
ASP.NET page is stored in the HTTP.sys
The culprit was output caching. When enabled the cache in the ASP.net kernel stores the page along with the set cookie response in the HTTP header. This causes the same page to be redirected to different users. A weird issue is'nt it. You can get the solution in the aforesaid URL.
Saturday, December 27, 2008
Thursday, November 6, 2008
Browser Close Vs Session End in ASP.net
Browser Close Vs Session End in ASP.net
After a few R&D’s here goes my findings
There are n number of scenarios by which the browser can be closed viz
1. Clicking the (x) button
2. Clicking Alt+F4
3. File menu à Exit.
4. Alt+f+x
The OnUnload event will fire for all the above scenarios. So write a Javascript function like this which uses Remote scripting to Abandon the session in the Server side.
function abandonSession()
{
// Use remote scripting to
var xmlhttp;
if (window.ActiveXObject)
{ // IE
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET","AbandonSession.aspx",false);
xmlhttp.send();
}
}
Attach the above function in the HTML as shown below
In the page load of AbandonSession.aspx
protected void Page_Load(object sender, EventArgs e)
{
Session.Abandon();
// Write your code to logout the user since sometimes Session_End may not fire. Also Session_Event in Global.asax will not fire in Web farms.
}
However the weird thing is the OnUnload event also fires during following scenarios
1. When F5 or Page refresh is done
2. When the user types a different url in the same browser
3. Page is submitted or Post back happens
4. Click the browser back
So somehow you need to figure out a way which will not execute the abandonSession() function for all the above scenarios.
After a few R&D’s here goes my findings
There are n number of scenarios by which the browser can be closed viz
1. Clicking the (x) button
2. Clicking Alt+F4
3. File menu à Exit.
4. Alt+f+x
The OnUnload event will fire for all the above scenarios. So write a Javascript function like this which uses Remote scripting to Abandon the session in the Server side.
function abandonSession()
{
// Use remote scripting to
var xmlhttp;
if (window.ActiveXObject)
{ // IE
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET","AbandonSession.aspx",false);
xmlhttp.send();
}
}
Attach the above function in the HTML as shown below
In the page load of AbandonSession.aspx
protected void Page_Load(object sender, EventArgs e)
{
Session.Abandon();
// Write your code to logout the user since sometimes Session_End may not fire. Also Session_Event in Global.asax will not fire in Web farms.
}
However the weird thing is the OnUnload event also fires during following scenarios
1. When F5 or Page refresh is done
2. When the user types a different url in the same browser
3. Page is submitted or Post back happens
4. Click the browser back
So somehow you need to figure out a way which will not execute the abandonSession() function for all the above scenarios.
Sunday, October 12, 2008
Object.Equals C#
This is one of the basics which every .Net developer has to know. Hence I would like to explain this with a proper example.
Let us consider the following class.
class Employee
{
public Employee(int empID, string name)
{
this.empID = empID;
this.empName = name;
}
private int empID;
public int EmpID
{
get { return empID; }
set { empID = value; }
}
private string empName = string.Empty;
public string EmpName
{
get { return empName; }
set { empName = value; }
}
}
Now if I execute the folowing snippet of code
Employee emp1 = new Employee(1, "Ajeeth");
Employee emp2 = new Employee(1, "Ajeeth");
MessageBox.Show(emp1.Equals(emp2).ToString());
bool eq = (emp1 == emp2);
MessageBox.Show(eq.ToString());
The output would be
False
False
The reason being the Equals and == checks for the references by default. Hence it is necessary to override the Equals method in the Employee class which would now look like.
class Employee
{
public Employee(int empID, string name)
{
this.empID = empID;
this.empName = name;
}
private int empID;
public int EmpID
{
get { return empID; }
set { empID = value; }
}
private string empName = string.Empty;
public string EmpName
{
get { return empName; }
set { empName = value; }
}
public override bool Equals(object obj)
{
if (this.empID == (obj as Employee).empID)
return true;
return false;
}
public override int GetHashCode()
{
return this.empID;
}
public static bool operator ==(Employee emp1, Employee emp2)
{
return emp1.Equals(emp2);
}
public static bool operator !=(Employee emp1, Employee emp2)
{
return !(emp1 == emp2) ;
}
}
If you notice above I am overriding Equals and overloading the operator ‘==’ which internally calls the Employee.Equals() method. If you also see I am comparing the 2 objects of Employee object with their unique fields. This would enforce a value comparison. The C# compiler provides a warning if we forget to override the Object.GetHashCode() method when Object.Equals is overridden. The GetHashCode() should ensure that it returns unique id for 2 different objects of same class. This would impact the performance of HashTables if the objects are stored as Keys.
If you execute the following snippet would be
Employee emp1 = new Employee(1, "Ajeeth");
Employee emp2 = new Employee(1, "Ajeeth");
MessageBox.Show(emp1.Equals(emp2).ToString());
bool eq = (emp1 == emp2);
MessageBox.Show(eq.ToString());
The output would be
True
True
If you execute the following snippet would be
Employee emp1 = new Employee(1, "Ajeeth");
Employee emp2 = new Employee(2, "Aravind");
MessageBox.Show(emp1.Equals(emp2).ToString());
bool eq = (emp1 == emp2);
MessageBox.Show(eq.ToString());
The output would be
False
False
Happy Coding..
Let us consider the following class.
class Employee
{
public Employee(int empID, string name)
{
this.empID = empID;
this.empName = name;
}
private int empID;
public int EmpID
{
get { return empID; }
set { empID = value; }
}
private string empName = string.Empty;
public string EmpName
{
get { return empName; }
set { empName = value; }
}
}
Now if I execute the folowing snippet of code
Employee emp1 = new Employee(1, "Ajeeth");
Employee emp2 = new Employee(1, "Ajeeth");
MessageBox.Show(emp1.Equals(emp2).ToString());
bool eq = (emp1 == emp2);
MessageBox.Show(eq.ToString());
The output would be
False
False
The reason being the Equals and == checks for the references by default. Hence it is necessary to override the Equals method in the Employee class which would now look like.
class Employee
{
public Employee(int empID, string name)
{
this.empID = empID;
this.empName = name;
}
private int empID;
public int EmpID
{
get { return empID; }
set { empID = value; }
}
private string empName = string.Empty;
public string EmpName
{
get { return empName; }
set { empName = value; }
}
public override bool Equals(object obj)
{
if (this.empID == (obj as Employee).empID)
return true;
return false;
}
public override int GetHashCode()
{
return this.empID;
}
public static bool operator ==(Employee emp1, Employee emp2)
{
return emp1.Equals(emp2);
}
public static bool operator !=(Employee emp1, Employee emp2)
{
return !(emp1 == emp2) ;
}
}
If you notice above I am overriding Equals and overloading the operator ‘==’ which internally calls the Employee.Equals() method. If you also see I am comparing the 2 objects of Employee object with their unique fields. This would enforce a value comparison. The C# compiler provides a warning if we forget to override the Object.GetHashCode() method when Object.Equals is overridden. The GetHashCode() should ensure that it returns unique id for 2 different objects of same class. This would impact the performance of HashTables if the objects are stored as Keys.
If you execute the following snippet would be
Employee emp1 = new Employee(1, "Ajeeth");
Employee emp2 = new Employee(1, "Ajeeth");
MessageBox.Show(emp1.Equals(emp2).ToString());
bool eq = (emp1 == emp2);
MessageBox.Show(eq.ToString());
The output would be
True
True
If you execute the following snippet would be
Employee emp1 = new Employee(1, "Ajeeth");
Employee emp2 = new Employee(2, "Aravind");
MessageBox.Show(emp1.Equals(emp2).ToString());
bool eq = (emp1 == emp2);
MessageBox.Show(eq.ToString());
The output would be
False
False
Happy Coding..
Thursday, October 9, 2008
Simple Authorization Framework
Note: I cant upload the source code here. If you need the source code please mail me at ajeeth4u@gmail.com
The authorization framework provides a generic mechanism to authorize each user’s request to complete an operation. It could be a Database Write, Read or Delete. From a more generic view there exist the following entities
1. Principal: – Any actor or role who initiates or request for an operation or Service.
2. Resource: – A resource is something which stores the business data. It could be a database table, XML file, Flat file etc.. The Principal can access the Resources through any Services and change the data using operations.
3. Service and Operation: A service could be a set of operations performed against a set of business entities which causes a change in their internal state.
4. Access Rights: Access rights are security mechanisms which can prevent or allow any Principal to call a Service or do any operations on a Resource.
All the above entities could vary from a business to business. Hence they have been generalized in the framework. The specialization of the entities could be done in the Framework extensions. For instance when it comes to the Data Repository project, the Principal could be an Organization’s Windows Principal Object. A Windows Principal represents the user who belongs to an AD group in the Organization domain.
The framework also has some extension points or hooks which can be extended by the client.
1. Rules Reader: The rules reader constructs the rules entity by reading from any persistent data storages like DB tables, XML files etc. The Authorization engine accepts a Rules object for the authorization strategy.
The framework has some frozen spots like the Rules Configuration, Authorization Strategy and the Authorization Context as an Authorization engine.
2. Rules Configurations: The access rights for each role have to be configured in a permanent storage. The storage could an XML file or could be a data base table. A rules configuration screen would be developed for the admin user to configure the rules. The sample screenshot for the screen is provided in the Appendix 8.2 of this document.
3. Rule Entity: The rule entity would be constructed by the Rules Reader and would be cached in entity.
4. Authorization Context: The authorization context should hold the 3 business entities Principal, Resource and Service/Operation. The authorization context would be sent to the authorization engine. The authorization engine parses the necessary information from the entities and implements a strategy to read the Access Rights for the context from the Rules Entity.
5. Authorization Strategy: The authorization strategy expects an Authorization context and a Rule entity to process the request and returns a Boolean flag true if the Principal is authorized to do the operation. The frozen strategy uses SQL server tables to configure the Rules, Principals and Access Rights. The schema details are provided further this document.
Sample Code
UI SaveButton_Clicked()
//Call the Business Layer
BLLookUpCodes objLookUpCodes = new BLLookUpCodes();
objLookUpCodes.Update(objTypedDataSet);
Business Layer
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Organization.DataAcess;
///
/// Summary description for BLLookupCodes
///
public class BLLookupCodes
{
public BLLookupCodes()
{
//
// TODO: Add constructor logic here
//
}
///
/// Calls the DAL to save the changes
///
/// dataset
public void Update(LookupCodeDataSet dataset)
{
//Create the authentication context
AuthenticationContext objContext = new AuthenticationContext();
//Set the Principal
objContext.Principal = HttpContext.Current.User.Identity.Name;
//Set the Table name
objContext.Resource = “InfLookupCodes”;
//Set the Operation name
objContext.Operation = “Update”;
//Build the Authorization Strategy
IAuthorizationStrategy objAuthStrategy= new OrganizationAuthorizationStategy();
//Authorize the user's role before calling the Data Access layer
if (objAuthStrategy.IsAuthorized(objContext))
{
//If Authorized call DAL
DALLooupCodes objDALLookupCodes = new DALLookUpCodes()
objDALLooupCodes.Update(dataset);
}
}
}
Configuration Schema details
The authorization framework provides a generic mechanism to authorize each user’s request to complete an operation. It could be a Database Write, Read or Delete. From a more generic view there exist the following entities
1. Principal: – Any actor or role who initiates or request for an operation or Service.
2. Resource: – A resource is something which stores the business data. It could be a database table, XML file, Flat file etc.. The Principal can access the Resources through any Services and change the data using operations.
3. Service and Operation: A service could be a set of operations performed against a set of business entities which causes a change in their internal state.
4. Access Rights: Access rights are security mechanisms which can prevent or allow any Principal to call a Service or do any operations on a Resource.
All the above entities could vary from a business to business. Hence they have been generalized in the framework. The specialization of the entities could be done in the Framework extensions. For instance when it comes to the Data Repository project, the Principal could be an Organization’s Windows Principal Object. A Windows Principal represents the user who belongs to an AD group in the Organization domain.
The framework also has some extension points or hooks which can be extended by the client.
1. Rules Reader: The rules reader constructs the rules entity by reading from any persistent data storages like DB tables, XML files etc. The Authorization engine accepts a Rules object for the authorization strategy.
The framework has some frozen spots like the Rules Configuration, Authorization Strategy and the Authorization Context as an Authorization engine.
2. Rules Configurations: The access rights for each role have to be configured in a permanent storage. The storage could an XML file or could be a data base table. A rules configuration screen would be developed for the admin user to configure the rules. The sample screenshot for the screen is provided in the Appendix 8.2 of this document.
3. Rule Entity: The rule entity would be constructed by the Rules Reader and would be cached in entity.
4. Authorization Context: The authorization context should hold the 3 business entities Principal, Resource and Service/Operation. The authorization context would be sent to the authorization engine. The authorization engine parses the necessary information from the entities and implements a strategy to read the Access Rights for the context from the Rules Entity.
5. Authorization Strategy: The authorization strategy expects an Authorization context and a Rule entity to process the request and returns a Boolean flag true if the Principal is authorized to do the operation. The frozen strategy uses SQL server tables to configure the Rules, Principals and Access Rights. The schema details are provided further this document.
Sample Code
UI SaveButton_Clicked()
//Call the Business Layer
BLLookUpCodes objLookUpCodes = new BLLookUpCodes();
objLookUpCodes.Update(objTypedDataSet);
Business Layer
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Organization.DataAcess;
///
/// Summary description for BLLookupCodes
///
public class BLLookupCodes
{
public BLLookupCodes()
{
//
// TODO: Add constructor logic here
//
}
///
/// Calls the DAL to save the changes
///
/// dataset
public void Update(LookupCodeDataSet dataset)
{
//Create the authentication context
AuthenticationContext objContext = new AuthenticationContext();
//Set the Principal
objContext.Principal = HttpContext.Current.User.Identity.Name;
//Set the Table name
objContext.Resource = “InfLookupCodes”;
//Set the Operation name
objContext.Operation = “Update”;
//Build the Authorization Strategy
IAuthorizationStrategy objAuthStrategy= new OrganizationAuthorizationStategy();
//Authorize the user's role before calling the Data Access layer
if (objAuthStrategy.IsAuthorized(objContext))
{
//If Authorized call DAL
DALLooupCodes objDALLookupCodes = new DALLookUpCodes()
objDALLooupCodes.Update(dataset);
}
}
}
Configuration Schema details
Tuesday, October 7, 2008
SQL Records to CSV
I had a requirement to display the list of financial markets for an Advisor as a Comma Separated values in the ASP.net page. I wrote the COALESCE function in the SQL stored procedure to do this as following.
CREATE FUNCTION [DBO].[UFN_GETUSERMARKETSCSV](@PUSERID VARCHAR(50))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE @TEMPMARKET TABLE (MARKET_ID INT, MARKET_NAME VARCHAR(255))
INSERT INTO @TEMPMARKET
SELECT m.Market_ID, m.Market_Name
FROM MMS_Market m
JOIN MMS_User_Market_Access uma
On.uma.Market_ID = m.Market_ID
Where uma.User_ID = @pUserID
DECLARE @CSV VARCHAR(1000)
SELECT @CSV = COALESCE(@CSV + ', ', '') + MARKET_NAME FROM @TEMPMARKET
RETURN @CSV
END
CREATE FUNCTION [DBO].[UFN_GETUSERMARKETSCSV](@PUSERID VARCHAR(50))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE @TEMPMARKET TABLE (MARKET_ID INT, MARKET_NAME VARCHAR(255))
INSERT INTO @TEMPMARKET
SELECT m.Market_ID, m.Market_Name
FROM MMS_Market m
JOIN MMS_User_Market_Access uma
On.uma.Market_ID = m.Market_ID
Where uma.User_ID = @pUserID
DECLARE @CSV VARCHAR(1000)
SELECT @CSV = COALESCE(@CSV + ', ', '') + MARKET_NAME FROM @TEMPMARKET
RETURN @CSV
END
Monday, October 6, 2008
Batch Update in SQL Server
I recently faced an issue with batch updates in SQL server. There was a table in SQL server 2000 which had 1 billion records and I had to introduce a new column to that with a default value. Adding a default value along with the alter script failed with an exception as shown below.
Error: 9002, Severity: 17, State: 2
The log file for database '%.*ls' is full.
Further info: http://support.microsoft.com/kb/317375
Hence I used a script which updates the records in a batch as following
Set nocount on
DECLARE @batch_size int, @rowc int
SELECT @batch_size = 20000
SELECT @rowc = @batch_size
SET ROWCOUNT @batch_size
WHILE @rowc = @batch_size
BEGIN
Begin Tran
Update dbo.<> Set <> = <> where <> is null
SELECT @rowc = @@rowcount
Commit Tran
END
SET ROWCOUNT 0
Set nocount off
This had few drawbacks it was very slow (took 10 hours) to update ½ a billion records and caused an exception as following
[Microsoft][ODBC SQL Server Driver][DBNETLIB]ConnectionCheckForData (CheckforData()).
Server: Msg 11, Level 16, State 1, Line 0
General network error. Check your network documentation.
Connection Broken
I had to rewrite the script as following
SET NOCOUNT ON
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE name LIKE '#tMonths%')
BEGIN
DROP TABLE #tMonths
END
SELECT [Month]
INTO #tMonths
FROM dbo. tblHoldings_Securities_Total
GROUP BY [Month]
DECLARE @dtMonth DATETIME
DECLARE @sMonth VARCHAR(30)
DECLARE @rc INT
DECLARE cur_months CURSOR FAST_FORWARD
FOR SELECT [Month] FROM #tMonths ORDER BY [Month]
OPEN cur_months
FETCH NEXT FROM cur_months INTO @dtMonth
WHILE @@fetch_status = 0
BEGIN
set @sMonth = cast(@dtMonth as varchar)
UPDATE dbo.tblHoldings_Securities_Total
SET Sec_identifier = 1
WHERE [Month] = @dtMonth
AND Sec_identifier IS NULL
SELECT @rc = @@ROWCOUNT
RAISERROR ('Month: %s; Updated count: %d',10,1, @sMonth, @rc) WITH NOWAIT
FETCH NEXT FROM cur_months INTO @dtMonth
END
CLOSE cur_months
DEALLOCATE cur_months
Advantages
1.The above method used grouping logic based on an Indexed column.
2.Completed in 4 hours.
Error: 9002, Severity: 17, State: 2
The log file for database '%.*ls' is full.
Further info: http://support.microsoft.com/kb/317375
Hence I used a script which updates the records in a batch as following
Set nocount on
DECLARE @batch_size int, @rowc int
SELECT @batch_size = 20000
SELECT @rowc = @batch_size
SET ROWCOUNT @batch_size
WHILE @rowc = @batch_size
BEGIN
Begin Tran
Update dbo.<
SELECT @rowc = @@rowcount
Commit Tran
END
SET ROWCOUNT 0
Set nocount off
This had few drawbacks it was very slow (took 10 hours) to update ½ a billion records and caused an exception as following
[Microsoft][ODBC SQL Server Driver][DBNETLIB]ConnectionCheckForData (CheckforData()).
Server: Msg 11, Level 16, State 1, Line 0
General network error. Check your network documentation.
Connection Broken
I had to rewrite the script as following
SET NOCOUNT ON
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE name LIKE '#tMonths%')
BEGIN
DROP TABLE #tMonths
END
SELECT [Month]
INTO #tMonths
FROM dbo. tblHoldings_Securities_Total
GROUP BY [Month]
DECLARE @dtMonth DATETIME
DECLARE @sMonth VARCHAR(30)
DECLARE @rc INT
DECLARE cur_months CURSOR FAST_FORWARD
FOR SELECT [Month] FROM #tMonths ORDER BY [Month]
OPEN cur_months
FETCH NEXT FROM cur_months INTO @dtMonth
WHILE @@fetch_status = 0
BEGIN
set @sMonth = cast(@dtMonth as varchar)
UPDATE dbo.tblHoldings_Securities_Total
SET Sec_identifier = 1
WHERE [Month] = @dtMonth
AND Sec_identifier IS NULL
SELECT @rc = @@ROWCOUNT
RAISERROR ('Month: %s; Updated count: %d',10,1, @sMonth, @rc) WITH NOWAIT
FETCH NEXT FROM cur_months INTO @dtMonth
END
CLOSE cur_months
DEALLOCATE cur_months
Advantages
1.The above method used grouping logic based on an Indexed column.
2.Completed in 4 hours.
Subscribe to:
Posts (Atom)