Tuesday, July 29, 2008

Programming practices in C#

C# Coding Standards and Best Programming Practices




Anybody can write code! And with a few months of programming experience, you can write 'working applications'. Making it work is easy, but doing it the most efficient way requires more work, than just making it work!

Believe it, majority of the programmers write 'Working Code', but not 'Efficient Code'. As we mentioned in the beginning of this tutorial, do you want to become the 'Most Valued Professional' of your company? Writing 'Efficient Code' is an art and you must learn and practice it.

Naming Conventions and Standards



Note :
Pascal Casing - First character of all words are Upper Case and other characters are lower case.
Camel Casing - First character of all words, except the first word are Upper Case and other characters are lower case.
·  Use Pascal casing for Class names
public class HelloWorld
{
        ...
}
·  Use Pascal casing for Method names
public class HelloWorld
{
        void SayHello(string name)
        {
               ...
        }
}
·  Use Camel casing for variables and method parameters
public class HelloWorld
{
        int totalCount = 0;
        void SayHello(string name)
        {
               string fullMessage = "Hello " + name;
               ...
        }
}
·  Do not use Hungarian notation to name variables.

In earlier days most of the programmers liked it - having the data type as a prefix for the variable name and using m_ as prefix for member variables. Eg:
 
string m_sName;
int nAge;
 
However, in .NET coding standards, this is not reccommended. Usage of datatype and M_ to represent member variables should not be used. All variables should use camel casing.
·  Use Meaningfull, descriptive words to name variables.

- Do not use abbreviations. Use
name, address, salary etc instead of nam, addr, sal
- Do not use single character variable names like
i, n, x etc. Use names like index, temp
One exception in this case would be variables used for iterations in loops:
for ( int i = 0; i < count; i++ )
{
        ...
}
If the variable is used only as a counter for iteration and is not used anywhere else in the loop, many people still like to use a single char variable (i) instead of inventing a different suitable name.
- Do not use underscores (_) in variable names.
- Namespace names should follow the standard pattern
...
·  File name should match with class name

For example, for the class
HelloWorld, the file name should be helloworld.cs (or, helloworld.vb)

Indentation and Spacing

·  Use TAB for indentation. Do not use SPACES.
·  Comments should be in the same level as the code.
·  Curly braces ( {} ) should be in the same level as the code outside teh braces.
·  Use one blank line to separate logical groups of code.
        bool SayHello (string name)
        {
               string fullMessage = "Hello " + name;
               DateTime currentTime = DateTime.Now;
               string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
               MessageBox.Show ( message );
               if ( ... )
               {
                       // Do something
                       // ...
                       return false;
               }
               return true;
        }
This code looks better, than the code shown above:
        bool SayHello ( string name )
        {
               string fullMessage = "Hello " + name;
               DateTime currentTime = DateTime.Now;
 
               string message = fullMessage + ", the time is : " + currentTime.ToShortTimeString();
 
               MessageBox.Show ( message );
 
               if ( ... )
               {
                       // Do something
                       // ...
 
                       return false;
               }
 
               return true;
        }
·  There should be one and only one single blank line between each method inside the class.
·  The curly braces should be on a separate line adn not in the same line as if, for etc.
Good:
               if ( ... )     
               {
                       // Do something
               }
Not Good:
               if ( ... )     {
                       // Do something
               }
·  Use a single space before and after each operator and brackets.

Good:
               if ( showResult == true )
               {
                       for ( int i = 0; i < 10; i++ )
                       {
                               //
                       }
               }
Not Good:
               if(showResult==true)
               {
                       for(int i= 0;i<10;i++)
                       {
                               //
                       }
               }

Good Programming practices


Follow the best practices to for best programming
·  Avoid having too large files. If a file has more than 300~400 lines of code, you must consider refactoring code into helper classes.
·  Avoid writing very long methods. A method should typically have 1~25 lines of code. If a method has more than 25 lines of code, you must consider re factoring into separate methods.
·  Method name should tell what it does. Do not use mis-leading names. If the method name is obvious, there is no need of documentation explaining what the method does.

Good:
        void SavePhoneNumber ( string phoneNumber )
        {
               // Save the phone number.
        }

Not Good:
        // This method will save the phone number.
        void SaveData ( string phoneNumber )
        {
               // Save the phone number.
        }
·  A method should do only 'one job'. Do not combine more than one job in a single method, even if those jobs are very small.

Good:
        // Save the address.
        SaveAddress (  address );
        
        // Send an email to the supervisor to inform that the address is updated.
        SendEmail ( address, email );         
        
        void SaveAddress ( string address )
        {
               // Save the address.
               // ...
        }
        
        void SendEmail ( string address, string email )
        {
               // Send an email to inform the supervisor that the address is changed.
               // ...
        }

Not Good:
        // Save address and send an email to the supervisor to inform that the address is updated.
        SaveAddress ( address, email );
 
        void SaveAddress ( string address, string email )
        {
               // Job 1.
               // Save the address.
               // ...
 
               // Job 2.
               // Send an email to inform the supervisor that the address is changed.
               // ...
        }
·  Use the c# or VB.NET specific types, rather than the alias types defined in System namespace.

Good:
        int age;
        string name;
        object contactInfo;

Not Good:
        Int16 age;
        String name;
        Object contactInfo;
        
·  Do not hardcode numbers. Use constants instead.
·  Do not hardcode strings. Use resource files.
·  Avoid using many member variables. Declare local variables and pass it to methods instead of sharing a member variable between methods. If you share a member variable between methods, it will be difficult to track which method changed the value and when.
·  Use enum wherever required. Do not use numbers or strings to indicate discrete values.
Good:
        enum MailType
        {
               Html,
               PlainText,
               Attachment
        }
 
        void SendMail (string message, MailType mailType)
        {
               switch ( mailType )
               {
                       case MailType.Html:
                               // Do something
                               break;
                       case MailType.PlainText:
                               // Do something
                               break;
                       case MailType.Attachment:
                               // Do something
                               break;
                       default:
                               // Do something
                               break;
               }
        }


Not Good:
        void SendMail (string message, string mailType)
        {
               switch ( mailType )
               {
                       case "Html":
                               // Do something
                               break;
                       case "PlainText":
                               // Do something
                               break;
                       case "Attachment":
                               // Do something
                               break;
                       default:
                               // Do something
                               break;
               }
        }
·  Do not make the member variables public or protected. Keep them private and expose public/protected Properties.
·  Never hardcode a path or drive name in code. Get the application path programmatically and use relative path.
·  Never assume that your code will run from drive "C:". You may never know, some users may run it from network or from a "Z:".
·  In the application start up, do some kind of "self check" and ensure all required files and dependancies are available in the expected locations. Check for database connection in start up, if required. Give a friendly message to the user in case of any problems.
·  If the required configuration file is not found, application should be able to create one with default values.
·  If a wrong value found in the configuration file, application should throw an error or give a message and also should tell the user what are the correct values.
·  Error messages should help the user to solve the problem. Never give error messages like "Error in Application", "There is an error" etc. Instead give specific messages like "Failed to update database. Please make sure the login id and password are correct."
·  When displaying error messages, in addition to telling what is wrong, the message should also tell what should the user do to solve the problem. Instead of message like "Failed to update database.", suggest what should the user do: "Failed to update database. Please make sure the login id and password are correct."
·  Show short and friendly message to the user. But log the actual error with all possible information. This will help a lot in diagnosing problems.

Comments

·  Do not write comments for every line of code and every variable declared.
·  Write comments wherever required. But good readable code will require very less comments. If all variables and method names are meaningfull, that would make the code very readable and will not need much comments.
·  Less lines of comments will make the code more elegant. But if the code is not clean/readable and there are less comments, that is worse.
·  If you have to use some complex or weired logic for any reason, document it very well with sufficient comments.
·  If you initialize a numeric variable to a special number other than 0, -1 etc, document the reason for choosing that value.
·  The bottom line is, write clean, readable code such a way that it doesn't need any comments to understand.
·  Do a spell check on comments and also make sure proper grammar and punctuation is used.

Exception Handling

·  Never do a 'catch exception and do nothing'. If you hide an exception, you will never know if the exception happened or not.
·  In case of exceptions, give a friendly message to the user, but log the actual error with all possible details about the error, including the time it occurred, method and class name etc.
·  Always catch only the specific exception, not generic exception.

Good:
        void ReadFromFile ( string fileName )
        {
               try
               {
                       // read from file.
               }
               catch (FileIOException ex)
               {
                       // log error.
                       //  re-throw exception depending on your case.
                       throw;
               }
        }
Not Good:
        void ReadFromFile ( string fileName )
        {
               try
               {
                       // read from file.
               }
               catch (Exception ex)   
               {
                       // Catching general exception is bad... we will never know whether it
                       // was a file error or some other error.
                       
                       // Here you are hiding an exception. 
                       // In this case no one will ever know that an exception happened.
 
                       return "";             
               }
        }
·  No need to catch the general exception in all your methods. Leave it open and let the application crash. This will help you find most of the errors during development cycle.
·  You can have an application level (thread level) error handler where you can handle all general exceptions. In case of an 'unexpected general error', this error handler should catch the exception and should log the error in addition to giving a friendly message to the user before closing the application, or allowing the user to 'ignore and proceed'.
·  Do not write try-catch in all your methods. Use it only if there is a possibility that a a specific exception may occur. For example, if you are writing into a file, handle only FileIOException.
·  Do not write very large try-catch blocks. If required, write separate try-catch for each task you perform and enclose only the specific piece of code inside the try-catch. This will help you find which piece of code generated the exception and you can give specific error message to the user.
·  You may write your own custom exception classes, if required in your application. Do not derive your custom exceptions from the base class SystemException. Instead, inherit from ApplicationException.




How do I stop a thread?

There are several options. First, you can use your own communication mechanism to tell the ThreadStart method to finish. Alternatively the Thread class has in-built support for instructing the thread to stop. The two principle methods are Thread.Interrupt() and Thread.Abort(). The former will cause a ThreadInterruptedException to be thrown on the thread when it next goes into a WaitJoinSleep state. In other words, Thread.Interrupt is a polite way of asking the thread to stop when it is no longer doing any useful work. In contrast, Thread.Abort() throws a ThreadAbortException regardless of what the thread is doing. Furthermore, the ThreadAbortException cannot normally be caught (though the ThreadStart's finally method will be executed). Thread.Abort() is a heavy-handed mechanism which should not normally be required.

How can I stop my code being reverse-engineered from IL?

You can buy an IL obfuscation tool. These tools work by 'optimising' the IL in such a way that reverse-engineering becomes much more difficult.

Of course if you are writing web services then reverse-engineering is not a problem as clients do not have access to your IL.

Can I create my own metadata attributes?

Yes. Simply derive a class from System.Attribute and mark it with the AttributeUsage attribute. For example:
    [AttributeUsage(AttributeTargets.Class)]
    public class InspiredByAttribute : System.Attribute
    {
        public string InspiredBy;
   
        public InspiredByAttribute( string inspiredBy )
        {
            InspiredBy = inspiredBy;
        }
    }


    [InspiredBy("Andy Mc's brilliant .NET FAQ")]
    class CTest
    {
    }


    class CApp
    {
        public static void Main()
        {       
            object[] atts = typeof(CTest).GetCustomAttributes(true);

            foreach( object att in atts )
                if( att is InspiredByAttribute )
                    Console.WriteLine( "Class CTest was inspired by {0}", ((InspiredByAttribute)att).InspiredBy  );
        }
    }

What is serialization?

Serialization is the process of converting an object into a stream of bytes. Deserialization is the opposite process, i.e. creating an object from a stream of bytes. Serialization/Deserialization is mostly used to transport objects (e.g. during remoting), or to persist objects (e.g. to a file or database).

Monday, July 28, 2008

Stored Procedures (Why Use Stored Procedures? )

In this article, we will examine what a stored procedure is and how to call one from SQL Server database.

Outline:

  • Why Use Stored Procedures?
  • Creating a Stored Procedure
  • Calling a Stored Procedure
  • Specifying Parameters
  • Data Retrieval
  • Inserting Data Using Parameters



Why Use Stored Procedures?

There are several advantages of using stored procedures instead of standard SQL. First, stored procedures allow a lot more flexibility offering capabilities such as conditional logic. Second, because stored procedures are stored within the DBMS, bandwidth and execution time are reduced. This is because a single stored procedure can execute a complex set of SQL statements. Third, SQL Server pre-compiles stored procedures such that they execute optimally. Fourth, client developers are abstracted from complex designs. They would simply need to know the stored procedure's name and the type of data it returns.

Creating a Stored Procedure

Enterprise Manager provides an easy way to create stored procedures. First, select the database to create the stored procedure on. Expand the database node, right-click on "Stored Procedures" and select "New Stored Procedure...". You should see the following:

CREATE PROCEDURE [OWNER].[PROCEDURE NAME] AS

Substitute OWNER with "dbo" (database owner) and PROCEDURE NAME with the name of the procedure. For example:

CREATE PROCEDURE [dbo].[GetProducts] AS

So far, we are telling SQL Server to create a new stored procedure with the name GetProducts. We specify the body of the procedure after the AS clause:

CREATE PROCEDURE [dbo].[GetProducts] AS

SELECT ProductID, ProductName FROM Products

Click on the Check Syntax button in order to confirm that the stored procedure is syntactically correct. Please note that the GetProducts example above will work on the Northwind sample database that comes with SQL Server. Modify it as necessary to suite the database you are using.

Now that we have created a stored procedure, we will examine how to call it from within a C# application.

Calling a Stored Procedure

A very nice aspect of ADO.NET is that it allows the developer to call a stored procedure in almost the exact same way as a standard SQL statement.

1. Create a new C# Windows Application project.

2. From the Toolbox, drag and drop a DataGrid onto the Form. Resize it as necessary.

3. Double-click on the Form to generate the Form_Load event handler. Before entering any code, add "using System.Data.SqlClient" at the top of the file.

Enter the following code:

private void Form1_Load(object sender, System.EventArgs e)

{

SqlConnection conn = new SqlConnection("Data

Source=localhost;Database=Northwind;Integrated Security=SSPI");

SqlCommand command = new SqlCommand("GetProducts", conn);

SqlDataAdapter adapter = new SqlDataAdapter(command);

DataSet ds = new DataSet();

adapter.Fill(ds, "Products");

this.dataGrid1.DataSource = ds;

this.dataGrid1.DataMember = "Products";

}

As you can see, calling a stored procedure in this example is exactly like how you would use SQL statements, only that instead of specifying the SQL statement, you specify the name of the stored procedure. Aside from that, you can treat it exactly the same as you would an ordinary SQL statement call with all the advantages of a stored procedure.

Specifying Parameters

Most of the time, especially when using non-queries, values must be supplied to the stored procedure at runtime. For instance, a @CategoryID parameter can be added to our GetProducts procedure in order to specify to retrieve only products of a certain category. In SQL Server, parameters are specified after the procedure name and before the AS clause.

CREATE PROCEDURE [dbo].[GetProducts] (@CategoryID int) AS

SELECT ProductID, ProductName FROM Products WHERE CategoryID = @CategoryID

Parameters are enclosed within parenthesis with the parameter name first followed by the data type. If more than one parameter is accepted, they are separated by commas:

CREATE PROCEDURE [dbo].[SomeProcedure] (

@Param1 int,

@Param2 varchar(50),

@Param3 varchar(50)

) AS

...

For our GetProducts example, if @CategoryID was supplied with the value 1, the query would equate to:

SELECT ProductID, ProductName FROM Products WHERE CategoryID = 1

Which would select all the products that belong to CategoryID 1 or the Beverages category. To call the stored procedure, use Query Analyzer to execute:

exec GetProducts X

where X is the @CategoryID parameter passed to the stored procedure. To call the stored procedure from within a C# application using 1 as the @CategoryID parameter value, use the following code:

SqlConnection conn = new SqlConnection("Data

Source=localhost;Database=Northwind;Integrated Security=SSPI");

SqlCommand command = new SqlCommand("GetProducts", conn);

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add("@CategoryID", SqlDbType.Int).Value = 1;

SqlDataAdapter adapter = new SqlDataAdapter(command);

DataSet ds = new DataSet();

adapter.Fill(ds, "Products");

this.dataGrid1.DataSource = ds;

this.dataGrid1.DataMember = "Products";

Note that you must now specify the CommandType property of the SqlCommand object. The reason we did not do this in the first example was that it is not required if the stored procedure does not accept parameters. Of course, specifying the CommandType property even if it is not needed may improve readability. The next line actually combines two lines in one:

command.Parameters.Add("@CategoryID", SqlDbType.Int);

command.Parameters["@CategoryID"].Value = 1;

The first line of this segment specifies that the command object (which calls the GetProducts stored procedure) accepts a parameter named @CategoryID which is of type SqlDbType.Int. The type must be the same as the data type specified by the stored procedure. The second line of this code segment gives the parameter the value 1. For simplicity, especially when using more than one parameter, I prefer to combine to two lines into a single line:

command.Parameters.Add("@CategoryID", SqlDbType.Int).Value = 1;

The rest of the code is the same as in the previous example without parameters. As illustrated in the previous examples, ADO.NET takes a lot of pain out of database programming. Calling a stored procedure uses virtually the same code as using standard SQL and specifying parameters is a painless process.

Data Retrieval

Data Retrieval with stored procedures is the same (surprise!) as if using standard SQL. You can wrap a DataAdapter around the Command object or you can use a DataReader to fetch the data one row at a time. The previous examples have already illustrated how to use a DataAdapter and fill a DataSet. The following example shows usage of the DataReader:

SqlConnection conn = new SqlConnection("Data

Source=localhost;Database=Northwind;Integrated Security=SSPI");

SqlCommand command = new SqlCommand("GetProducts", conn);

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add("@CategoryID", SqlDbType.Int).Value = 1;

conn.Open();

SqlDataReader reader = command.ExecuteReader();

while (reader.Read())

{

Console.WriteLine(reader["ProductName"]);

}

conn.Close();

Again, using either a DataAdapter or a DataReader against a query from a stored procedure is the same as specifying the SQL from within the code.

Inserting Data Using Parameters

Using other SQL statements such as INSERT, UPDATE or DELETE follow the same procedure. First, create a stored procedure that may or may not accept parameters, and then call the stored procedure from within the code supply the necessary values if parameters are needed. The following example illustrates how to insert a new user in a users table that has a username and password field.

CREATE PROCEDURE [dbo].[InsertUser] (

@Username varchar(50),

@Password varchar(50)

) AS

INSERT INTO Users VALUES(@Username, @Password)

string username = ... // get username from user

string password = ... // get password from user

SqlConnection conn = new SqlConnection("Data

Source=localhost;Database=MyDB;Integrated Security=SSPI");

SqlCommand command = new SqlCommand("InsertUser", conn);

command.CommandType = CommandType.StoredProcedure;

command.Parameters.Add("@Username", SqlDbType.VarChar).Value = username;

command.Parameters.Add("@Password", SqlDbType.VarChar).Value = password;

conn.Open();

int rows = command.ExecuteNonQuery();

conn.Close();

First, we retrieve the username and password information from the user. This information may be entered onto a form, through a message dialog or through some other method. The point is, the user specifies the username and password and the applicaton inserts the data into the database. Also notice that we called the ExecuteNonQuery() method of the Connection object. We call this method to indicate that the stored procedure does not return results for a query but rather an integer indicating how many rows were affected by the executed statement. ExecuteNonQuery() is used for DML statements such as INSERT, UPDATE and DELETE. Note that we can test the value of rows to check if the stored procedure inserted the data successfully.

if (rows == 1)

{

MessageBox.Show("Create new user SUCCESS!");

}

else

{

MessageBox.Show("Create new user FAILED!");

}

We check the value of rows to see if it is equal to one. Since our stored procedure only did one insert operation and if it is successful, the ExecuteNonQuery() method should return 1 to indicate the one row that was inserted. For other SQL statements, especially UPDATE and DELETE statements that affect more than one row, the stored procedure will return the number of rows affected by the statement.

DELETE FROM Products WHERE ProductID > 50

This will delete all products whose product ID is greater than 50 and will return the number of rows deleted.

CLASS

The most common definition states that a class is a template for an object. In OOP you program a class as a template for a specific object or groups ob objects that will always have the same features.


OOPS(Object-Oriented Programming)

OOPS: Object-Oriented Programming concepts and its implementation in .NET. classes, objects,structures,inheritance, abstraction,polymorphism,interface, multiple interface inheritance, collections, and overloading.

How do I run managed code in a process?

The first step in running managed code in a process is to get the CLR loaded and initialized using a CLR host. Typically, a host consists of both managed code and unmanaged code. The managed code which executes in the default domain is usually responsible for creating the application domains in which the user code exists. All CLR hosts must contain unmanaged code because execution must begin in unmanaged code. The .NET Frameworks provides a set of unmanaged APIs that the host can use to configure the CLR, load the CLR into a process, load the hosts managed code into the default domain, and transition from the unmanaged code to the managed code.
The second step in running managed code in a process is to create application domains in which the user code will execute. The creator of the application domain can specify criteria which control code isolation, security, and loading of assemblies.
The third step in running managed code in a process is to execute user code in one or more application domains created in the previous step. All code that is run in the CLR must be part of an assembly. There are three options for loading assemblies. First, precompiled assemblies can be loaded from disk. Second, precompiled assemblies can be loaded from an array of bytes. Third, assemblies can be built dynamically in an application domain using the BCL Reflection Emit APIs.

What is a default domain?

The default domain is an application domain that is created automatically by the CLR every time it is initialized in a process. The default domain is not unloaded until the process shuts down. Most hosts dont run code in the default domain for two main reasons. First, the default domain cannot be shut down independently of the process. Second, for security and isolation reasons, it is desirable that the user code and hosting code execute in different application domains. Instead, hosts create application domains with a set of properties that control security, isolation, loading, etc.