Bob Horn
Bob Horn

Reputation: 34325

Linq to SQL - Update Batch

I have the following inline SQL:

    internal void UpdateWorkflowProcessingByNullToken(Guid processingToken, int numberToProcess)
    {
        string sql = string.Format(CultureInfo.InvariantCulture,
            "UPDATE TOP ({0}) Master.WorkflowEventProcessing " +
            "SET ProcessingToken = '{1}' " +
            "WHERE ProcessingToken IS NULL",
            numberToProcess, processingToken);

        this.Database.ExecuteCommand(sql);
    }

Inline SQL was used for performance. It was my understanding that L2S would create a SQL statement for every row that I needed to update. And that was taking too long. Note, this was a couple of years ago.

Now I have a DBA telling me this:

As developers, we've been reluctant to use stored procedures. We like having all of our data code in our C# data layer. Am I stuck here? Do I need to use a stored procedure? Or is there a way to do a mass update with L2S?

I remember reading about compiling an L2S query. I could look into that as well...

Upvotes: 1

Views: 740

Answers (1)

Jim
Jim

Reputation: 4950

You can use parameterized SQL commands to execute. This will generate a reusable query execution plan that will be as efficient as a stored procedure after it is initially created and cached. Each execution you simply supply new parameters.

More Details

Given the following code that updates a demo database and a table named "Foo"

///////////////////////////////////////////////////////////
// just setup for the context for demo purposes, you would
// reference this.Database in place of creating context.
SqlConnection connection = new SqlConnection("Data Source = .; Initial Catalog = MyDb; Integrated Security = SSPI;");
var dataContext = new System.Data.Linq.DataContext(connection);
///////////////////////////////////////////////////////////

string updateQuery = "UPDATE TOP (@p1) dbo.Foo " +
"SET Data = @p2 " +
"WHERE Data IS NULL";

dataContext.Connection.Open();

var command = dataContext.Connection.CreateCommand();
command.CommandText = updateQuery;
command.CommandType = System.Data.CommandType.Text;

var param1 = new SqlParameter("@p1", System.Data.SqlDbType.Int);   
param1.Value = 3;
command.Parameters.Add(param1);

var param2 = new SqlParameter("@p2", System.Data.SqlDbType.Int);
param2.Value = 1;
command.Parameters.Add(param2);
command.Prepare();
command.ExecuteNonQuery();

param2.Value = 5;
command.ExecuteNonQuery();

From the profiler output you can see it calls sp_prepexec

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@p1 int,@p2 int',N'UPDATE TOP (@p1) dbo.Foo SET Data = @p2 WHERE Data IS NULL',@p1=3,@p2=1
select @p1

and executes the statement passing the parameters 3 and 1 then when param2.Value is set to 5 and the command executed again the profiler shows it reusing the prepared command (thus no recompiling or new execution plan generated)

exec sp_execute 1,@p1=3,@p2=5

This is what the profiler output looks like, FYI... enter image description here

Upvotes: 3

Related Questions