19

So in C# to use a stored procedure I have code like the following (connection code omitted):

 string sql = "GetClientDefaults";

 SqlCommand cmd = new SqlCommand(sql);
 cmd.CommandType = CommandType.StoredProcedure;    //<-- DO I NEED THIS??
 cmd.Parameters.AddWithValue("@computerName", computerName);

Where sql is the name of a stored procedure. Now, this code seems to work just fine with and without the commented line.

So, do I need this line? Is there some performance (or other) benefit to setting this? Is there a benefit to NOT setting it or setting it to Text?

Andreas Ågren
  • 3,879
  • 24
  • 33
MAW74656
  • 3,449
  • 21
  • 71
  • 118

4 Answers4

21

According to the tests in this blog post SQL Server will do the parameterization for you, by wrapping your statement in sp_executesql, when you use CommandType.Text. But when you use CommandType.StoredProcedure you will parameterize it and thereby saving the database some work. The latter method is faster.

Edit:

Setup

I've done some tests myself and here are the results.

Create this procedure:

create procedure dbo.Test
(
   @Text1 varchar(10) = 'Default1'
  ,@Text2 varchar(10) = 'Default2'
)
as
begin
   select @Text1 as Text1, @Text2 as Text2
end

Add a trace to it using SQL Server Profiler.

And then call it using the following code:

using System;
using System.Data;
using System.Data.SqlClient;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main()
        {
            CallProcedure( CommandType.Text );
            CallProcedure( CommandType.StoredProcedure );
        }

        private static void CallProcedure(CommandType commandType)
        {
            using ( SqlConnection connection = new SqlConnection("Data Source=localhost;Initial Catalog=Test;Integrated Security=SSPI;") )
            {
                connection.Open();
                using ( SqlCommand textCommand = new SqlCommand("dbo.Test", connection) )
                {
                    textCommand.CommandType = commandType;
                    textCommand.Parameters.AddWithValue("@Text1", "Text1");
                    textCommand.Parameters.AddWithValue("@Text2", "Text2");
                    using ( IDataReader reader = textCommand.ExecuteReader() )
                    {
                        while ( reader.Read() )
                        {
                            Console.WriteLine(reader["Text1"] + " " + reader["Text2"]);
                        }
                    }
                }
            }
        }
    }
}

Results

In both cases the calls are made using RPC.

Here's what the trace reveals using CommandType.Text:

exec sp_executesql N'dbo.Test',N'@Text1 nvarchar(5),@Text2 nvarchar(5)',@Text1=N'Text1',@Text2=N'Text2'

And here is the result using CommandType.StoredProcedure:

exec dbo.Test @Text1=N'Text1',@Text2=N'Text2'

As you can see the text-call is wrapped in a call to sp_executesql so that it is properly parameterized. This will of course create a slight overhead, and thus my previous statement that using CommandType.StoredProcedure is faster still stands.

Another noteworthy thing, and which is also kind of a deal breaker here, is that when I created the procedure without default values I got the following error:

Msg 201, Level 16, State 4, Procedure Test, Line 0 Procedure or function 'Test' expects parameter '@Text1', which was not supplied.

The reason for this is how the call to sp_executesql is created, as you can see the parameters are declared and initialized, but they are not used. For the call to work, it should have looked like this:

exec sp_executesql N'dbo.Test @Text1, @Text2',N'@Text1 nvarchar(5),@Text2 nvarchar(5)',@Text1=N'Text1',@Text2=N'Text2'

Meaning, when you're using CommandType.Text you have to add the parameters to the CommandText unless you always want the default values to be used.

So, to answer your question

  1. Using CommandType.StoredProcedure is faster.
  2. If you're using CommandType.Text, then you'll have to add the parameter names to the call to the procedure unless you want the default values to be used.
Andreas Ågren
  • 3,879
  • 24
  • 33
  • -So using StoredProcedure may be faster? – MAW74656 Feb 13 '12 at 20:48
  • 1
    @MAW74656 Yes. Also note Panagiotis Kanavos answer that there might be providers, other than SQL Server, which doesn't understand that you're trying to execute a proc unless you specify it. – Andreas Ågren Feb 13 '12 at 21:34
  • -I understand the point about other providers, its just that this application is unlikely to ever need that sort of change. And there are many commercial enterprise applications which require that you use SQL Server (any specific database server), and I'll bet they don't use a provider factory there. – MAW74656 Feb 13 '12 at 21:44
  • -Tons of great work here! My only remaining question is the difference between the two with parameter-less stored procedures. When there are parameters I use StoredProcedure and add the parameters to the Command object's paramater collection. – MAW74656 Feb 14 '12 at 15:44
  • @MAW74656 The only difference I can see for a call to a parameterless procedure would be that it is slightly faster to use `CommandType.StoredProcedure`. – Andreas Ågren Feb 14 '12 at 20:11
10

There is actually a huge difference. If you specify the command type StoredProcedure then any parameter you add to your SqlCommand will be a parameter added the procedure call. If you leave it as Text then the parameters will be added to the batch, not to the procedure. To illustrate the point, lets create a dummy procedure:

create procedure usp_test 
    @p1 char(10)  = 'foo',
    @p2 int = 42
as
    select @p1, @p2;    
go

Then compile this tiny C# application:

   static void Main(string[] args)
    {
        ExecWithType(CommandType.Text);
        ExecWithType(CommandType.StoredProcedure);
    }

    static void ExecWithType(CommandType type)
    {
        using (SqlConnection conn = new SqlConnection(Settings.Default.connString))
        {
            conn.Open();
            using (SqlCommand cmd1 = new SqlCommand("usp_test", conn))
            {
                cmd1.CommandType = type;
                cmd1.Parameters.AddWithValue("@p1", "bar");
                cmd1.Parameters.AddWithValue("@p2", 24);
                using (SqlDataReader rdr = cmd1.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        Console.WriteLine("Type: {0} Result: @p1: {1} @p2: {2}", type, rdr[0], rdr[1]);
                    }
                }
            }
        }
    }

The result is:

Type: Text Result: @p1: foo        @p2: 42
Type: StoredProcedure Result: @p1: bar        @p2: 24

Ouch! For the CommandType.Text setting although the parameters were passed to the batch, they were not passed to the procedure. Source of many hours of debugging fun...

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • 2
    -So with parameters, no gray area, CommandType.StoredProcedure is definately better, more accurate, faster. – MAW74656 Feb 14 '12 at 15:37
7

You would set this to allow ADO.NET to help you out. When you use CommandType.StoredProcedure, you'll have to just put CommandText equal to the stored procedure name.

For instance, this:

YourSqlCommand.CommandType = CommandType.StoredProcedure;
YourSqlCommand.CommandText = "dbo.YourStoredProc";

Is equivalent to:

YourSqlCommand.CommandText = "exec dbo.YourStoredProc";
  • -I'm not saying "Exec" or "dbo." in either version and it works just fine. Is there some other way this would help? – MAW74656 Feb 13 '12 at 19:58
  • I would actually recompile it.. debug it and truly test that.. because I have not seen something like work without having to set the CommandTpe... hummmmm??? – MethodMan Feb 13 '12 at 20:01
  • 4
    @MAW74656 That's because in SQL Server you don't HAVE to type in `exec` if the stored proc is the first statement of the batch. –  Feb 13 '12 at 20:01
  • 1
    @Shark -OK, that explains that part, but is this the only reason to set commandtype? To make longer syntax? There must be some `benefit`. – MAW74656 Feb 13 '12 at 20:02
  • @MAW74656 No, it also has to do with parameters. A stored proc name if interpreted as ad hoc, parameters would be useless unless shown in the ad hoc query. But with a stored proc that requires parameters, you wouldn't put those parameters in the command text, but you'd add to the parameter collection. Make sense? –  Feb 13 '12 at 20:25
  • @Shark -So if I don't set CommandType, SQL can't use the cached execution plan for the procedure? – MAW74656 Feb 13 '12 at 20:28
  • @MAW74656 Hmmm...Good question. I think these are unrelated topics. As that would have to do with what's in your cache already. –  Feb 13 '12 at 20:36
  • @Shark -I do use it and intend to continue using it with parameters, but where there are no parameters is really what I'm trying to figure out. – MAW74656 Feb 13 '12 at 21:19
  • 1
    @MAW74656 Well with a parameterless query or stored procedure, there probably is no implementing difference between using a different CommandType. I'd run SQL Profiler and see how they both look against the database, but I bet they would look identical. –  Feb 13 '12 at 21:22
  • This answer is incorrect. Prefixing with exec or not has nothing to do with the command type, and the queries that gets executed for `CommandType.Text` and `CommandType.StoredProcedure` are not identical. – Andreas Ågren Feb 14 '12 at 20:16
3

CommandType is not specific to SQL Server. It is a property of the IDbCommand interface that instructs the underlying provider to treat the CommandText in a specific way. While SQL Server may treat single-word names as procedures, you should not expect this to work in other providers.

In general, you should prefer using the provider-generated classes like DbCommand instead of specific classes like SqlCommand. This way you can target different databases simply by changing a provider string in your config file.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 1
    @PagagiotisKanavos -I think you're fighting a different battle here. Most of the sample code online uses SQLCommand. But even if I accept that premise, you still haven't said what effect setting CommandType does. – MAW74656 Feb 13 '12 at 20:43
  • There is no battle to fight. You use a specific class, you are tied to the provider and have to rewrite everything. Sample code is not production code. As for what CommandType does - do you think you can execute a stored procedure in Oracle just by passing the name and assuming the default CommandType.Text? In any case, I would never trust an accidental and undocumented behavior in production code, simply to avoid setting a value. – Panagiotis Kanavos Feb 13 '12 at 20:51
  • 1
    -But what does setting it DO? – MAW74656 Feb 13 '12 at 21:10