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..

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

Tuesday, October 7, 2008

Website Copier

An excellent tool to download a website to you local drive.

Download HTTRack

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

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.