Labels

Monday, July 2, 2007

RAISERROR in SQL Server & Catch in C#

I was reading about raising exceptions from stored procedures using RAISERROR (note the spelling) statement and catching those in c# code. To try it out, I wrote a very simple procedure that accepts a ProductID as it's sole parameter. If that ProductID exists in the table it returns the product details, else it raises an "Unknown ProductID" exception.

 

Here goes my simple little procedure:

 

USE [AdventureWorks]
GO
CREATE PROCEDURE [dbo].[uspGetProductFromProductID] 
@productID int
AS
BEGIN 
    SET NOCOUNT ON;
    SELECT * FROM Production.Product WHERE ProductID = @productID
 IF @@ROWCOUNT = 0 
  RAISERROR('Unknown ProductID: %d', 16, 1, @productID) WITH NOWAIT
END;
GO

 

The c# code was as follows:

 

     SqlDataReader reader = null;
     SqlConnection conn = new SqlConnection("Data Source=ps3119; Initial Catalog=Adventureworks; Integrated Security=SSPI");
            SqlCommand cmd = new SqlCommand("uspGetProductFromProductID", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            SqlParameter productID = cmd.Parameters.Add("@productID", SqlDbType.Int);
            productID.Direction = ParameterDirection.Input;
            productID.Value = 100;

            try
            {
                conn.Open();
                reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
            catch (SqlException sqlex)
            {
                conn.Close();
                MessageBox.Show(sqlex.Message);
            }
            catch (Exception ex)
            {
                conn.Close();
                MessageBox.Show(ex.Message);            
            }

 

ProductID 100 did not exist in the table, and hence I was expecting that the "Unknown ProductID" exception will be thrown. But it wasn't.

 

Apparently, if a resultset (empty or not) is returned from the stored procedure, the exception thrown by RAISERROR are not 'seen' in c# code.

 

Hence as a work around, I modified the SP to return no data when ProductID did not exist. This is how:

 

USE [AdventureWorks]
GO
ALTER PROCEDURE [dbo].[uspGetProductFromProductID] 
@productID int
AS
BEGIN 

    DECLARE @cnt int;
    SET NOCOUNT ON;


    SELECT @cnt = count(*) FROM Production.Product WHERE ProductID = @productID
    IF @cnt = 0
        RAISERROR('Unknown ProductID: %d', 16, 1, @productID) WITH NOWAIT

    ELSE
        SELECT * FROM Production.Product WHERE ProductID = @productID
END;
GO

 

...and now it works :)


Similar problem is faced when the UpdateCommand method of the SqlDataAdapter object uses a SQL Server stored procedure that raises an error after it returns a result set, the ADO.NET client application may not trap the error raised by SQL Server. Find out how to solve it at http://support.microsoft.com/kb/811482

 

 

More about RAISERROR and handling exceptions ...

 

1. You can hard-code the exception message as the first parameter to RAISERROR. Second parameter specifies the severity level (described later) and the third parameter indicates a state number that identifies the source from which the error was issued (if the error can be issued from more than one place).

 

RAISERROR('Unknown ProductID: %d', 16, 1, @productID)

 

 

2. To avoid hard coding message text, add your own message to the sysmessages table by using the sp_addmessage system stored procedure. You can then reference the message by using an ID. The message IDs that you define must be greater than 50,000.

 

RAISERROR( 50001, 16, 1, @ProductID )

 

 

3. Severity levels indicate the type of problem that has occured. Choose your security level according to following table.

 

Severity Level

Connection Closed?

Generates Exception?

Meaning

10 and below

No

No

Informational Messages                    

11-16

No

Yes

Errors that can be corrected by user

17-19

No

Yes

Resource or system errors              

20-25

Yes

Yes

Fatal system errors

 

Problems :-

 

The work-around suggested is nice with one major problem—we are executing the same query twice, which can be grave in situation where the query you are firing is too expensive.

The point I am making is that you have to properly weight how important it is for you to get the exception from the SQL Server to the cost you are incurring on your server for running the query twice. Think of a table having more than 10,000 records and where you have very miniscule chance for getting ‘zero’ rows. It might be the case that the query takes seconds to run. Now to such tables we would rather prefer handling simple exceptions like “No records found” in the application rather than firing the query twice.

The work-around provided although can be used on a small table, where there is high probability of returning ‘zero’ columns (so that the query is executed just once J), but I would still argue of handling these errors in the application where it can be done very easily.

 

 

Thanks & Regards,

Arun Manglick

SMTS || Microsoft Technology Practice || Bridgestone - Tyre Link || Persistent Systems || 3023-6258

 

DISCLAIMER ========== This e-mail may contain privileged and confidential information which is the property of Persistent Systems Pvt. Ltd. It is intended only for the use of the individual or entity to which it is addressed. If you are not the intended recipient, you are not authorized to read, retain, copy, print, distribute or use this message. If you have received this communication in error, please notify the sender and delete all copies of this message. Persistent Systems Pvt. Ltd. does not accept any liability for virus infected mails.

The "Predicate Delegate"

 

A delegate is like a function pointer. The Predicate delegate is a Generic method which takes an object of type T as a parameter. It returns true or false, indicating whether or not the object of type T satisfies the condition it tests. The System.Array and System.Collections.Generic.List classes of the .NET Framework 2.0 each provide a number of methods, such as Find, FindAll, and FindLast, that let you avoid writing code to loop through every element of an array or list to find the one or more items you're looking for. You get the ability to "walk" an entire data structure, determining whether each item meets a set of criteria, without having to write the boilerplate code to loop through each row manually.

 

Let's say, for example, I have an array of numbers and I want to find all of the numbers which are odd. The obvious way is simply to loop through them checking each number for its "oddness" and adding it to an output array, like so:

 

 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

       int[] odds = GetOdds(numbers);

 

       private static int[] GetOdds(int[] numbers)

       {

           int[] odds = new int[numbers.Length];

           int counter = 0;

           for (int i = 0; i < numbers.Length; i++)

           {

               if ((numbers[i] % 2) != 0)

               {

                   // Found an odd!

                   odds[counter++] = numbers[i];

               }

           }

           return odds;

       }

 

This works...but it's a little ugly. Majority of our method seems to be just overhead to support the output array, looping through the input array, etc.  The core logic itself is actually constrained to just one line:

 

  if ((numbers[i] % 2) != 0)

 

It seems like that there should be a cleaner way to do this and, there is using Predicates, we can actually just write this little line of code into a stand alone method and reference it directly from the Array.FindXXX() methods. 

 

        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };       

        int[] odds = Array.FindAll<int>(numbers, IsOdd);

        private static bool IsOdd(int number)

        {

            return ((number % 2) != 0);

        }

 

Now the only thing that we have to worry about is our core logic, .NET takes care of all of the additional overhead for us!

 

We can even reference it inline using anonymous delegates!

 

  int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        int[] odds = Array.FindAll<int>(numbers,

            delegate(int number)

            {

                return ((number % 2) != 0);

            });

 

We can use this technique for any of the Find methods of the Array class (Find, FindAll, FindIndex, FindLast, and FindLastIndex) as well as the Exists method to see if any objects matching our condition exist in the array.

 

Hopefully this will help clean up some of those annoying routines you have scattered through your code that are just in place solely to get what you need from an array, check if an array has what you need, or just pull out a certain type of object from your array.

 

 

Thanks & Regards,

Arun Manglick

SMTS || Microsoft Technology Practice || Bridgestone - Tyre Link || Persistent Systems || 3023-6258

 

DISCLAIMER ========== This e-mail may contain privileged and confidential information which is the property of Persistent Systems Pvt. Ltd. It is intended only for the use of the individual or entity to which it is addressed. If you are not the intended recipient, you are not authorized to read, retain, copy, print, distribute or use this message. If you have received this communication in error, please notify the sender and delete all copies of this message. Persistent Systems Pvt. Ltd. does not accept any liability for virus infected mails.

Nullable DateTime

Hi,

 

A variable of type DateTime in C# cannot be assigned to a null value. However, in some cases, we do need to do that.

 

In such cases, we can use the Nullable version of DateTime. Two suggested ways of assigning a null value to DateTime are:

 

1. Nullable<DateTime> currentDate = null

2. DateTime? currentDate = null

 

One Problem I faced while using the Nullable DateTime is that, when I tried to convert the date into string I was not able to specify the any format as there is no overload for DateTime?.ToString() which takes one or more arguments like DateTime.ToString().

 

Nullable are actually Generic, Value properly gives directly the DateTime inside the Generics.

 

This is what works.

 

DateTime?.Value.ToString();

Ex:

DateTime? delvDate;

delvDate = DateTime.Now;

string str = delvDate.Value.ToString(“yyyy-MM-dd”);

 

Following is the link which talks about the workarounds and Nullable in .Net2.0

http://www.asptoday.com/Content.aspx?id=2371

Thanks & Regards,

Arun Manglick

SMTS || Microsoft Technology Practice || Bridgestone - Tyre Link || Persistent Systems || 3023-6258

 

DISCLAIMER ========== This e-mail may contain privileged and confidential information which is the property of Persistent Systems Pvt. Ltd. It is intended only for the use of the individual or entity to which it is addressed. If you are not the intended recipient, you are not authorized to read, retain, copy, print, distribute or use this message. If you have received this communication in error, please notify the sender and delete all copies of this message. Persistent Systems Pvt. Ltd. does not accept any liability for virus infected mails.

Choosing Correct Data Provider for Your Application

What is Data Provider?

 

A data provider in the .NET Framework serves as a bridge between an application and a data source. ADO.NET relies on the services of .NET data providers. These provide access to the underlying data source.

 

Currently, ADO.NET ships with two categories of providers: bridge providers and native providers. Bridge providers, such as those supplied for OLE DB and ODBC, allow you to use data libraries designed for earlier data access technologies. Native providers, such as the SQL Server and Oracle providers, typically offer performance improvements due, in part, to the fact that there is one less layer of abstraction.

 

Choosing between Data Providers

 

 

Data Provider

Use when connecting to

Comments

SQL Server .NET Data Provider                     

  1. Microsoft SQL Server 7.0 or later                                   
  2. Microsoft Data Engine (MSDE) from a single-tier application
  1. Found in System.Data.SqlClient namespace                                 
  2. Lightweight and fast. Accesses database directly without any intermediate layers

OLE DB .NET Data Provider

  1. Microsoft SQL Server 6.5 or earlier
  2. Microsoft Access database in a single-tier application
  3. Any other data source exposed using OLE DB
  1. Found in System.Data.OleDb namespace
  2. Slightly less efficient since it communicates to OLE DB data source through OLE DB Service Component and OLE DB Provider

.NET Data Provider for Oracle

Oracle data sources

  1. Found in System.Data.OracleClient namespace
  2. Supports Oracle client software version 8.1.7 or later
  3. Requires Oracle client to be installed on the system

ODBC .NET Data Provider

Data source exposed using ODBC

  1. Found in System.Data.Odbc namespace
  2. Slightly less efficient since it communicates to ODBC data source through ODBC Service Component and ODBC Provider

 

 

 

Thanks & Regards,

Arun Manglick

SMTS || Microsoft Technology Practice || Bridgestone - Tyre Link || Persistent Systems || 3023-6258

 

DISCLAIMER ========== This e-mail may contain privileged and confidential information which is the property of Persistent Systems Pvt. Ltd. It is intended only for the use of the individual or entity to which it is addressed. If you are not the intended recipient, you are not authorized to read, retain, copy, print, distribute or use this message. If you have received this communication in error, please notify the sender and delete all copies of this message. Persistent Systems Pvt. Ltd. does not accept any liability for virus infected mails.

How To: Thwart SQL Injection Attack

Hi,

 

For more info, please see: http://msdn2.microsoft.com/en-us/library/ms161953.aspx

 

What is SQL Injection Attack?

SQL Injection is an attack in which malicious code is inserted into strings that are later passed to SQL Server for parsing and execution.

The primary form of SQL injection consists of direct insertion of code into user input variables that are concatenated with SQL commands and executed. A less direct attack injects malicious code into strings destined for storage in a table or as metadata. When the stored strings are subsequently concatenated into a dynamic SQL command, the malicious code is executed.

Example of SQL Injection

Consider following piece of code

 

// Accept Employee Id in a text box

string Name = textBoxID.Text;

// Concatenate ID with a SELECT query

string query = "SELECT * FROM Employee WHERE Name = '" + Name + "';"

// Code to connect to a data source goes here

SqlCommand cmd = new SqlCommand(query, connection);

SqlDataReader reader = cmd.ExecuteReader();

reader.Close();

 

A potential attacker could enter a value of  “John’;DELETE FROM Employee;--“ in the text box. Due to this:-

 

1. John becomes the value for WHERE clause.

2. Single quote after John completes WHERE clause.

3. Semi colon after that completes the SELECT command

4. DELETE FROM Employee; is a new command representing SQL Injection Attack

5. Double-hyphens indicate that whatever follows must be treated as a comment thereby supressing the single quote-semi colon combination concatenated in the original code.

 

Hence the server executes following statements:

 

 SELECT * FROM Employee WHERE Name=’John’;DELETE FROM Employee;--‘;

 

 

Ways to Prevent SQL Injection

 

1.       Validate Input Data: Check the data for type, lenth, format and range.

 

2.       Run under least-privileged Account: Ideally, stored procedures should be written and granted the execute permission. No direct table access should be provided.

 

3.       Avoid Disclosing Sensitive Database Info through Errors: Attackers often use information from an exception, such as the name of server, database, or table to mount an attack on your system.

 

4.       Use type-safe SQL Parameters: Use parameterized stored procedures and queries. ParameterCollections such as SQLParameterCollection provide type checking and length validation. They throw an exception if the data is not of proper type or length, saving the trip to the server. If you use a parameters collection, input is treated as a literal value, and SQL Server does not treat it as executable code. Following is the modified version of code snippet given above.

 

// Accept Employee Id in a text box

string Name = textBoxID.Text;

 

string queryString = "SELECT * FROM Employee WHERE Name = @name";

 

SqlCommand cmd = new SqlCommand(queryString, conn);

cmd.Parameters.Add("@name", SqlDbType.VarChar, 10).Value = Name;

 

// Code to connect to a data source goes here

SqlDataReader reader = cmd.ExecuteReader();

reader.Close();

 

Thanks & Regards,

Arun Manglick

SMTS || Microsoft Technology Practice || Bridgestone - Tyre Link || Persistent Systems || 3023-6258

 

DISCLAIMER ========== This e-mail may contain privileged and confidential information which is the property of Persistent Systems Pvt. Ltd. It is intended only for the use of the individual or entity to which it is addressed. If you are not the intended recipient, you are not authorized to read, retain, copy, print, distribute or use this message. If you have received this communication in error, please notify the sender and delete all copies of this message. Persistent Systems Pvt. Ltd. does not accept any liability for virus infected mails.

Using Authentication Modes with ADO.NET

Best Practices: Using Authentication Modes with ADO.NET

Authentication is the process of determining if a user is who he claims to be. When your application connects to a SQL Server database, you have a choice of Windows authentication or SQL Server authentication. Windows authentication offers greater protection. But sometimes, you might need to use SQL authentication to connect to the database using a number of different accounts. Make use of following guidelines to protect your approach as much as possible.

 

1.       If possible, use Windows Authentication: Use Windows authentication when your application connects to SQL Server or other databases that support Windows authentication because

a.       Accounts are centralized and managed by your Active Directory, so user works with a single (Windows) security model rather than the separate SQL Server security model.

b.       No user names and passwords are embedded in the code

c.       No user names and passwords are sent over the network in clear text

d.       Strong password policies can be controlled and enforced by domain or local security policy. E.g. password expiration, minimum length.

 

The following example uses Windows authentication with the ADO.NET data provider for SQL Server

 

SqlConnection Conn = new SqlConnection("Data Source=dbserver; Initial Catalog=pubs; Integrated Security=SSPI;");

 

The following example uses the ADO.NET data provider for OLE DB data sources.

 

OleDbConnection Conn = new OleDbConnection("Provider=SQLOLEDB; Data Source=dbserver; Integrated Security=SSPI; Initial Catalog=northwind");

 

2.       If you use SQL Server Authentication, use strong passwords: If you use SQL server Authentication, use a least-privileged account with a strong password to prevent an attacker from guessing the password. A strong password should be at least 7 characters in length and contain a combination of alphanumeric, numeric and special characters.

 

Avoid using blank password with sa account as in the following connection string.

 

String SqlConnectionString = "Server=YourServer\Instance; Database=YourDatabase; uid=sa; pwd=;"

 

3.       If You Use SQL Server Authentication, Protect Credentials on the Network: When you connect to SQL Server with SQL authentication, the credentials are not encrypted prior to transmission across the network. So, an attacker can easily capture credentials by using a network monitor. Therefore, you should use Internet Protocol Security (IPSec) or Secure Sockets Layer (SSL) to create an encrypted communication channel between web server and database while building ASP.NET applications.

 

Use SSL when you need granular channel protection for a particular application, instead of for all applications and services running on a computer. Here is a link that shows how to use SSL to secure communication with SQL Server: http://msdn2.microsoft.com/en-us/library/aa302414.aspx

 

If you want to secure all of the IP traffic between the Web and database servers, use IPSec. You can also use IPSec to restrict which computers can communicate with one another. This link shows how to use IPSec to provide secure communication between two servers: http://msdn2.microsoft.com/en-us/library/aa302413.aspx

 

4.       If You Use SQL Server Authentication, Protect Credentials in the Configuration Files: To protect credentials in configuration files, place connection strings inside the <connectionStrings> section of web.config file (for ASP.NET apps) or app.config file (for Windows apps). Following example shows a part of configuration file.

 

<connectionStrings>

<add name="MyConnectionString" connectionString="Data Source=dbserver; Initial Catalog=pubs; Integrated Security=SSPI;"/>

</connectionStrings>

 

            Following code can be used to retrieve above connection string.

 

using System.Configuration;

...

string connectionString = ConfigurationManager.ConnectionStrings["MyConnectionString "].ConnectionString;

 

For additional protection of ASP.NET apps, encrypt <connectionStrings> section using either RSA or DPAPI encryption with help of aspnet_regiis utility. For more information about how to use DPAPI and RSA encryption to encrypt configuration file elements, see:

 

1.       How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI, at http://msdn2.microsoft.com/en-us/library/ms998280.aspx

2.    How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA, at http://msdn2.microsoft.com/en-us/library/ms998283.aspx

 

Note: Encrypting connection strings with aspnet_regiis does not change the code required to access the string because the decryption occurs automatically.

 

 

 

Thanks & Regards,

Arun Manglick

SMTS || Microsoft Technology Practice || Bridgestone - Tyre Link || Persistent Systems || 3023-6258

 

DISCLAIMER ========== This e-mail may contain privileged and confidential information which is the property of Persistent Systems Pvt. Ltd. It is intended only for the use of the individual or entity to which it is addressed. If you are not the intended recipient, you are not authorized to read, retain, copy, print, distribute or use this message. If you have received this communication in error, please notify the sender and delete all copies of this message. Persistent Systems Pvt. Ltd. does not accept any liability for virus infected mails.