First, just to be clear, I recognize that ExecuteNonQuery should only be used for UPDATE, INSERT, or DELETE statements, and that for all other types of statements, such as SELECT, the return value is -1.
My question is, why does the following stored procedure:
CREATE PROCEDURE `ExampleProc`(IN Name varchar(60), OUT ID bigint(20), OUT SP varchar(255))
BEGIN
SELECT id, sp INTO ID, SP FROM users WHERE username = Name;
END
When executed using ExecuteNonQuery:
using (var conn = new MySqlConnection("Secret"))
{
conn.Open();
using (var cmd = new MySqlCommand("ExampleProc", conn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddWithValue("Name", request.Name).MySqlDbType = MySqlDbType.VarChar;
cmd.Parameters.Add("ID", MySqlDbType.Int64).Direction = ParameterDirection.Output;
cmd.Parameters.Add("SP", MySqlDbType.VarChar).Direction = ParameterDirection.Output;
var returnVal = cmd.ExecuteNonQuery();
}
}
Yield a 0 in returnVal when a row with Name is not found, and a 1 if it is found? Based on all the documentation I have read, since the stored procedure contains a single SELECT statement, I should be seeing -1 being returned no matter what. Instead, it's returning the number of rows affected/found, which doesn't make sense according to the documentation.
Lastly, I've also tried using just "SELECT *" instead of "SELECT id, sp INTO ID, SP". That seems to always return 0. Still not the -1 that I am expecting.
Let's understand the working of the ExecuteNonQuery API in the ADO.Net, as you have understood from the documentation that it shall provide the number of Rows impacted as part of DML query and -1 for the Select query.
However the internal working is such that in case of Sql Server, default setting is Set NoCount Off helps in writing to the TDS protocol, the number of modified rows and same is read by the Ado.Net APIs, thus the correct result, this is the result of native binary integration of ado.net with Sql Server. more details at the following links:
Ado.Net TDS Protocol
SET NOCOUNT ON usage
When it comes to other database, like MySql, Oracle, there's no guarantee of the same behavior, as their integration is not at same level as Sql Server. This is the main reason for discrepancy, posted in the question, to make it consistent across the database, you may want to rely of a Output Parameter to automatically fill the value internally in the procedure, since every database has mechanism to figure out the number of rows updated
Related
I am very new to working with databases. Now I can write SELECT, UPDATE, DELETE, and INSERT commands. But I have seen many forums where we prefer to write:
SELECT empSalary from employee where salary = #salary
...instead of:
SELECT empSalary from employee where salary = txtSalary.Text
Why do we always prefer to use parameters and how would I use them?
I wanted to know the use and benefits of the first method. I have even heard of SQL injection but I don't fully understand it. I don't even know if SQL injection is related to my question.
Using parameters helps prevent SQL Injection attacks when the database is used in conjunction with a program interface such as a desktop program or web site.
In your example, a user can directly run SQL code on your database by crafting statements in txtSalary.
For example, if they were to write 0 OR 1=1, the executed SQL would be
SELECT empSalary from employee where salary = 0 or 1=1
whereby all empSalaries would be returned.
Further, a user could perform far worse commands against your database, including deleting it If they wrote 0; Drop Table employee:
SELECT empSalary from employee where salary = 0; Drop Table employee
The table employee would then be deleted.
In your case, it looks like you're using .NET. Using parameters is as easy as:
string sql = "SELECT empSalary from employee where salary = #salary";
using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
var salaryParam = new SqlParameter("salary", SqlDbType.Money);
salaryParam.Value = txtMoney.Text;
command.Parameters.Add(salaryParam);
var results = command.ExecuteReader();
}
Dim sql As String = "SELECT empSalary from employee where salary = #salary"
Using connection As New SqlConnection("connectionString")
Using command As New SqlCommand(sql, connection)
Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
salaryParam.Value = txtMoney.Text
command.Parameters.Add(salaryParam)
Dim results = command.ExecuteReader()
End Using
End Using
Edit 2016-4-25:
As per George Stocker's comment, I changed the sample code to not use AddWithValue. Also, it is generally recommended that you wrap IDisposables in using statements.
You are right, this is related to SQL injection, which is a vulnerability that allows a malicioius user to execute arbitrary statements against your database. This old time favorite XKCD comic illustrates the concept:
In your example, if you just use:
var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query
You are open to SQL injection. For example, say someone enters txtSalary:
1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.
When you execute this query, it will perform a SELECT and an UPDATE or DROP, or whatever they wanted. The -- at the end simply comments out the rest of your query, which would be useful in the attack if you were concatenating anything after txtSalary.Text.
The correct way is to use parameterized queries, eg (C#):
SqlCommand query = new SqlCommand("SELECT empSalary FROM employee
WHERE salary = #sal;");
query.Parameters.AddWithValue("#sal", txtSalary.Text);
With that, you can safely execute the query.
For reference on how to avoid SQL injection in several other languages, check bobby-tables.com, a website maintained by a SO user.
In addition to other answers need to add that parameters not only helps prevent sql injection but can improve performance of queries. Sql server caching parameterized query plans and reuse them on repeated queries execution. If you not parameterized your query then sql server would compile new plan on each query(with some exclusion) execution if text of query would differ.
More information about query plan caching
Two years after my first go, I'm recidivating...
Why do we prefer parameters? SQL injection is obviously a big reason, but could it be that we're secretly longing to get back to SQL as a language. SQL in string literals is already a weird cultural practice, but at least you can copy and paste your request into management studio. SQL dynamically constructed with host language conditionals and control structures, when SQL has conditionals and control structures, is just level 0 barbarism. You have to run your app in debug, or with a trace, to see what SQL it generates.
Don't stop with just parameters. Go all the way and use QueryFirst (disclaimer: which I wrote). Your SQL lives in a .sql file. You edit it in the fabulous TSQL editor window, with syntax validation and Intellisense for your tables and columns. You can assign test data in the special comments section and click "play" to run your query right there in the window. Creating a parameter is as easy as putting "#myParam" in your SQL. Then, each time you save, QueryFirst generates the C# wrapper for your query. Your parameters pop up, strongly typed, as arguments to the Execute() methods. Your results are returned in an IEnumerable or List of strongly typed POCOs, the types generated from the actual schema returned by your query. If your query doesn't run, your app won't compile. If your db schema changes and your query runs but some columns disappear, the compile error points to the line in your code that tries to access the missing data. And there are numerous other advantages. Why would you want to access data any other way?
In Sql when any word contain # sign it means it is variable and we use this variable to set value in it and use it on number area on the same sql script because it is only restricted on the single script while you can declare lot of variables of same type and name on many script. We use this variable in stored procedure lot because stored procedure are pre-compiled queries and we can pass values in these variable from script, desktop and websites for further information read Declare Local Variable, Sql Stored Procedure and sql injections.
Also read Protect from sql injection it will guide how you can protect your database.
Hope it help you to understand also any question comment me.
Old post but wanted to ensure newcomers are aware of Stored procedures.
My 10¢ worth here is that if you are able to write your SQL statement as a stored procedure, that in my view is the optimum approach. I ALWAYS use stored procs and never loop through records in my main code. For Example: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.
When you use stored procedures, you can restrict the user to EXECUTE permission only, thus reducing security risks.
Your stored procedure is inherently paramerised, and you can specify input and output parameters.
The stored procedure (if it returns data via SELECT statement) can be accessed and read in the exact same way as you would a regular SELECT statement in your code.
It also runs faster as it is compiled on the SQL Server.
Did I also mention you can do multiple steps, e.g. update a table, check values on another DB server, and then once finally finished, return data to the client, all on the same server, and no interaction with the client. So this is MUCH faster than coding this logic in your code.
Other answers cover why parameters are important, but there is a downside! In .net, there are several methods for creating parameters (Add, AddWithValue), but they all require you to worry, needlessly, about the parameter name, and they all reduce the readability of the SQL in the code. Right when you're trying to meditate on the SQL, you need to hunt around above or below to see what value has been used in the parameter.
I humbly claim my little SqlBuilder class is the most elegant way to write parameterized queries. Your code will look like this...
C#
var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();
Your code will be shorter and much more readable. You don't even need extra lines, and, when you're reading back, you don't need to hunt around for the value of parameters. The class you need is here...
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}
I am very new to working with databases. Now I can write SELECT, UPDATE, DELETE, and INSERT commands. But I have seen many forums where we prefer to write:
SELECT empSalary from employee where salary = #salary
...instead of:
SELECT empSalary from employee where salary = txtSalary.Text
Why do we always prefer to use parameters and how would I use them?
I wanted to know the use and benefits of the first method. I have even heard of SQL injection but I don't fully understand it. I don't even know if SQL injection is related to my question.
Using parameters helps prevent SQL Injection attacks when the database is used in conjunction with a program interface such as a desktop program or web site.
In your example, a user can directly run SQL code on your database by crafting statements in txtSalary.
For example, if they were to write 0 OR 1=1, the executed SQL would be
SELECT empSalary from employee where salary = 0 or 1=1
whereby all empSalaries would be returned.
Further, a user could perform far worse commands against your database, including deleting it If they wrote 0; Drop Table employee:
SELECT empSalary from employee where salary = 0; Drop Table employee
The table employee would then be deleted.
In your case, it looks like you're using .NET. Using parameters is as easy as:
string sql = "SELECT empSalary from employee where salary = #salary";
using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
var salaryParam = new SqlParameter("salary", SqlDbType.Money);
salaryParam.Value = txtMoney.Text;
command.Parameters.Add(salaryParam);
var results = command.ExecuteReader();
}
Dim sql As String = "SELECT empSalary from employee where salary = #salary"
Using connection As New SqlConnection("connectionString")
Using command As New SqlCommand(sql, connection)
Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
salaryParam.Value = txtMoney.Text
command.Parameters.Add(salaryParam)
Dim results = command.ExecuteReader()
End Using
End Using
Edit 2016-4-25:
As per George Stocker's comment, I changed the sample code to not use AddWithValue. Also, it is generally recommended that you wrap IDisposables in using statements.
You are right, this is related to SQL injection, which is a vulnerability that allows a malicioius user to execute arbitrary statements against your database. This old time favorite XKCD comic illustrates the concept:
In your example, if you just use:
var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query
You are open to SQL injection. For example, say someone enters txtSalary:
1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.
When you execute this query, it will perform a SELECT and an UPDATE or DROP, or whatever they wanted. The -- at the end simply comments out the rest of your query, which would be useful in the attack if you were concatenating anything after txtSalary.Text.
The correct way is to use parameterized queries, eg (C#):
SqlCommand query = new SqlCommand("SELECT empSalary FROM employee
WHERE salary = #sal;");
query.Parameters.AddWithValue("#sal", txtSalary.Text);
With that, you can safely execute the query.
For reference on how to avoid SQL injection in several other languages, check bobby-tables.com, a website maintained by a SO user.
In addition to other answers need to add that parameters not only helps prevent sql injection but can improve performance of queries. Sql server caching parameterized query plans and reuse them on repeated queries execution. If you not parameterized your query then sql server would compile new plan on each query(with some exclusion) execution if text of query would differ.
More information about query plan caching
Two years after my first go, I'm recidivating...
Why do we prefer parameters? SQL injection is obviously a big reason, but could it be that we're secretly longing to get back to SQL as a language. SQL in string literals is already a weird cultural practice, but at least you can copy and paste your request into management studio. SQL dynamically constructed with host language conditionals and control structures, when SQL has conditionals and control structures, is just level 0 barbarism. You have to run your app in debug, or with a trace, to see what SQL it generates.
Don't stop with just parameters. Go all the way and use QueryFirst (disclaimer: which I wrote). Your SQL lives in a .sql file. You edit it in the fabulous TSQL editor window, with syntax validation and Intellisense for your tables and columns. You can assign test data in the special comments section and click "play" to run your query right there in the window. Creating a parameter is as easy as putting "#myParam" in your SQL. Then, each time you save, QueryFirst generates the C# wrapper for your query. Your parameters pop up, strongly typed, as arguments to the Execute() methods. Your results are returned in an IEnumerable or List of strongly typed POCOs, the types generated from the actual schema returned by your query. If your query doesn't run, your app won't compile. If your db schema changes and your query runs but some columns disappear, the compile error points to the line in your code that tries to access the missing data. And there are numerous other advantages. Why would you want to access data any other way?
In Sql when any word contain # sign it means it is variable and we use this variable to set value in it and use it on number area on the same sql script because it is only restricted on the single script while you can declare lot of variables of same type and name on many script. We use this variable in stored procedure lot because stored procedure are pre-compiled queries and we can pass values in these variable from script, desktop and websites for further information read Declare Local Variable, Sql Stored Procedure and sql injections.
Also read Protect from sql injection it will guide how you can protect your database.
Hope it help you to understand also any question comment me.
Old post but wanted to ensure newcomers are aware of Stored procedures.
My 10¢ worth here is that if you are able to write your SQL statement as a stored procedure, that in my view is the optimum approach. I ALWAYS use stored procs and never loop through records in my main code. For Example: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.
When you use stored procedures, you can restrict the user to EXECUTE permission only, thus reducing security risks.
Your stored procedure is inherently paramerised, and you can specify input and output parameters.
The stored procedure (if it returns data via SELECT statement) can be accessed and read in the exact same way as you would a regular SELECT statement in your code.
It also runs faster as it is compiled on the SQL Server.
Did I also mention you can do multiple steps, e.g. update a table, check values on another DB server, and then once finally finished, return data to the client, all on the same server, and no interaction with the client. So this is MUCH faster than coding this logic in your code.
Other answers cover why parameters are important, but there is a downside! In .net, there are several methods for creating parameters (Add, AddWithValue), but they all require you to worry, needlessly, about the parameter name, and they all reduce the readability of the SQL in the code. Right when you're trying to meditate on the SQL, you need to hunt around above or below to see what value has been used in the parameter.
I humbly claim my little SqlBuilder class is the most elegant way to write parameterized queries. Your code will look like this...
C#
var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();
Your code will be shorter and much more readable. You don't even need extra lines, and, when you're reading back, you don't need to hunt around for the value of parameters. The class you need is here...
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}
I want to know whether there is any way besides the OUT parameter to get data from stored procedure into C# code.
Today my colleague told me that all select queries and the OUT parameters in a stored procedure are returned to the C# code. Is this correct? If yes, then how do I choose which results should be returned?
Is the answer same in case of VB6 code instead of c#?
Yes you can return values back to your application from a SP using either OUT parameters or a SELECT within the SP.
The OUT parameters are generally used for single values. The SELECT can be used for returning rows of results. A combination of both can be used in many different variations, such as the SP will return rows and a status OUT parameter can indicate row count or existence of the requested data.
CREATE PROC usp_MySpecialSP
#conditionValue INT, #SPStatus INT OUT
AS
IF EXISTS(SELECT * FROM TableName WHERE column1=conditionValue)
BEGIN
SELECT #SPStatus=COUNT(*) FROM TableName WHERE column1=conditionValue
SELECT Column2, Column3, Column4 FROM TableName WHERE column1=conditionValue
END
ELSE
BEGIN
SELECT #SPStatus=0
END
GO
Here you can pickup values if the m_SPStatusReturned>0.
Check out below MSDN article how to pick up returned rows from SP
http://msdn.microsoft.com/en-us/library/d7125bke.aspx
or a single value using SELECT
http://msdn.microsoft.com/en-us/library/37hwc7kt.aspx
Yes it is correct - and the way you handle this is:
to get an OUT parameter, you need define a SqlParameter on your SqlCommand with ParameterDirection.Output
to get the result set of the SELECT in a stored procedure, you need to use a SqlDataReader or a SqlDataAdapter to get the results (as if you execute an inline SQL SELECT query)
and there's actually a third way : the RETURN keyword inside a stored procedure - typically used to return a numeric status value. You can capture that by using a SqlParameter with a value of ParameterDirection.ReturnValue
I have a stored procedure in a SQL Server 2005 database that's accessed by ASP.NET / C# code.
The parameters for the stored procedure are defined in the typical manner:
Try
{
SqlCommand cmd = new SqlCommand("mystoredprocedure",myConnection)
cmd.Parameters.Add(new SqlParameter("#p1"), SqlDbType.Int))
cmd.Parameters["#p1"].Value = myvalue (usually form inputs)
.
.
.
myConnection.Open()
cmd.ExecuteNonQuery()
}
catch (Exception xx)
{
lblError.Text = xx.Message;
}
finally
{
myConnection.Close();
}
The problem is my data never updates, though the stored procedure doesn't throw any errors. I've checked the procedure in SQL Server, and when I call it directly through SQL Server the proc makes my updates.
I'm tearing my hair out on this one since it worked fine for a long time, then stopped working. I've checked the usual culprits (db connection is pointing to the right database, the procedure seems to work fine in SQL Server, and I've commented out the few new parameters I've created)
Any thoughts on what could be causing this? It's strange that the same procedure works in SQL Server but not through my code without throwing any errors. (To be sure, I put a return value in the stored procedure, and the return value indicates I'm not rolling back)
EDIT
Code for the stored procedure:
BEGIN TRANSACTION
--get count
update dbo.myTable set
val1=#val1,
val2=#val2,
.
.
.
WHERE ID=#MyID
SET #Err = ##ERROR
--if error rollback transaction
IF #Err <> 0
BEGIN
ROLLBACK TRANSACTION
GOTO ErrorHandler
END
Select #ReturnCode = 1
COMMIT TRANSACTION
RETURN
ErrorHandler:
--unknown error
Select #ReturnCode = 0
RAISERROR (#Err, 16, 1 ) WITH LOG
RETURN -100
EDIT
When I parse cmd.ExecuteNonQuery, I get -1 as a result. Still trying to figure out why the C# code is doing this but not throwing any errors. Shouldn't ExecuteNonQuery return the number of rows affected by the stored procedure?
EDIT
Using TFS I've stepped back in my history a couple of revs - it seems like there's an additional field I added recently that's breaking the query. When I comment out that field and call my sproc, it works fine.
I don't get why, though - it's just a varchar(255) field. I've got plenty of other fields in this database that are set up in similar ways. Any thoughts as to why this would be a problem?
EDIT
Using SQL Profiler, I can see the statement execute AND commit...but still, no data updates. I'm wondering whether I need to trash the sproc and start over again?
Run the stored procedure without the .Net code (i.e directly in SQL Server Management Studio) and see whether the changes are updated or not. Probably you are missing an explicit commit statement.
May sound stupid, but if you are out of disk space I've seen SQL server go through the motions, no errors but your data does not get saved. Easy to check too!
Try to set CommandType like that :
SqlCommand cmd = new SqlCommand("mystoredprocedure",myConnection)
cmd.CommandType = CommandType.StoredProcedure
Try to use:
SqlCommand cmd;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
or you maybe use:
CALL mystoredprocedure(#ParameterName)
as SQL-Text
try this !
SqlCommand cmd = new SqlCommand(string.Format("mystoredprocedure('{0}')",inputValue),myConnection)
Try to run sp directly from SQL SERVER and remove error handling from stored procedure to get the exact error in .Net.
Can you try one simple thing for me?
Change your stored procedure to accept no parameter. Change logic of stored proc to just insert one row in one table and then change your code to call that procedure and find out whether call is successful or not.
I think that you use a wrong way to call a stored procedure. The value -1 returned by ExecuteNonQuery is because of the statement: if INSERT/UPDATE/DELETE the value is the number of rows affected, -1 if there's a rollback. In all the other cases the return value is -1 (your case, I think).
The SqlCommand constructor when used with the two parameters SqlCommand(String, SqlConnection) needs that the first one is a generic query so you can run something like "exec myProcedure #par1" and the parameters can be set e.g.:
Dim par1 As New SqlParameter("#par1", SqlDbType.VarChar, 50)
par1.Value = "DummyValue"
cmd.Parameters.Add(par1)
But in the ideal situation, when you've to call a stored than you've to set the CommandType to "StoredProcedure", like then MSDN suggests in this link http://msdn.microsoft.com/it-it/library/yy6y35y8.aspx.
(I will show you VB code... sorry.. it's easily converted to C# with tools like this.)
If you want to read a value from the stored procedure that you run, you can populate a datased (for example, "ds") using a SqlAdapter -this is only an example-:
Dim da As New SqlDataAdapter(cmd)
da.Fill(ds, "result")
If ds.Tables.Item("result").Rows.Count > 0 Then
Return True
Else
Return False
End If
If you don't need to read anything you can simply use:
cmd.ExecuteNonQuery()
Surely you've to tell that you want to launch a stored procedure:
cmd.CommandType = CommandType.StoredProcedure
Hope to be useful to you...
Bye, Massimo.
I think you are missing Command Type while creating Command object.. Set command type of command object as StoreProcegure and then check again.. Since if u dont set command type then ur query execute but u wont get any error and required result also.
From your code I can see that the parameter name you are passing is not the same that you are using in you procedure.
I am trying to insert > 8000 characters (submit from a web page) via ExecuteNonQuery (and DatabaseFactory.CreateDatabase() from MS Practices Enterprise Library). The stored procedure defines the parameter as VARCHAR(MAX). The column is VARCHAR(MAX). In theory, 2GB of data should be able to be passed.
What can I do to pass data > 8000? I set a breakpoint and the string.Length is indeed > 8K.
public static void UpdateTerms(string terms)
{
Database db = DatabaseFactory.CreateDatabase();
db.ExecuteNonQuery("uspUpdateTerms", terms);
}
Stored procedure:
ALTER PROCEDURE [dbo].[uspUpdateTerms]
#Terms VARCHAR(MAX)
AS
SET NOCOUNT ON
INSERT INTO tblTerms(Terms)
VALUES(#Terms)
Table (just to show that everything is varchar(max)):
CREATE TABLE [dbo].[tblTerms](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Terms] [varchar](max) NULL,
[DateUpdated] [datetime] NULL,
.
Update:
I just changed the code, and this seems to work, though I am not sure what the difference is:
public static void UpdateTerms(string terms)
{
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("uspUpdateTerms");
db.AddInParameter(cmd, "Terms", DbType.String, terms);
db.ExecuteNonQuery(cmd);
}
The issue may not be the storage of the data, it may be the retrieval.
If you are trying to determine whether or not more than 8000 chars were stored in the DB through enterprise manager, then you are out of luck if you just select the contents of the columns and look at the text length: enterprise manager limits the column output.
To determine how much data is actually stored in the column, execute the following query:
SELECT DATALENGTH(Terms) FROM tblTerms
This will tell you how much text was stored.
EDIT:
Another thought just occurred: the enterprise library caches stored procedure parameters in order to improve performance. If you changed the stored procedure after testing with the parameter set to nvarchar(8000), then switch the parameter to nvarchar(max) without resetting the application (if IIS-hosted, then iisreset or dirty web.config), then you will still be using the old stored proc parameter.
REPLICATE returns the input type irrespective of later assignment. It's annoying, but to avoid silent truncation, try:
SET #x = REPLICATE(CONVERT(VARCHAR(MAX), 'a'), 10000);
This is because SQL Server performs the REPLICATE operation before it considers what you're assigning it to or how many characters you're trying to expand it to. It only cares about the input expression to determine what it should return, and if the input is not a max type, it assumes it is meant to fit within 8,000 bytes. This is explained in Books Online:
If string_expression is not of type varchar(max) or nvarchar(max), REPLICATE truncates the return value at 8,000 bytes. To return values greater than 8,000 bytes, string_expression must be explicitly cast to the appropriate large-value data type.
Your sample code can be fixed by doing:
declare #x varchar(max)
set #x = replicate (cast('a' as varchar(max)), 10000)
select #x, len(#x)
You haven't shown the code where you are trying to use ExecutenonQuery. Note that you should use parameters.
using(var con = new SqlConnection(conString))
using(var cmd = new SqlCommand("storedProcedureName", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#text", SqlDbType.NVarChar, -1);
cmd.Parameters["#text"].Value = yourVeryLongText;
cmd.ExecuteNonQuery();
}