So, we've got this set of code that, for some reason, keeps timing out. It's not the stored procedure that it's running, because that runs fine. Also, if we remove the parameter from the c# code, the code runs. The parameter keeps breaking (causing it to time out) and we can't figure out why.
c#:
public static PTWViewList GetList(int studynumber)
{
PTWViewList tempList = new PTWViewList();
using (SqlConnection myConnection = new SqlConnection(AppConfiguration.cnARDB))
{
string spName = "ardb.PTWViewSelect";
SqlCommand myCommand = new SqlCommand(spName, myConnection);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.AddWithValue("#study", studynumber);
myConnection.Open();
using (NullableDataReader myReader = new NullableDataReader(myCommand.ExecuteReader())) /*this is where the code times out*/
{
tempList = new PTWViewList();
while (myReader.Read())
{
tempList.Add(FillDataRecord(myReader));
}
myReader.Close();
}
}
tempList.ListCount = tempList.Count;
return tempList;
}
stored procedure:
CREATE PROCEDURE [ardb].[PTWViewSelect]
#studynumber int = NULL,
#quoteid uniqueidentifier = NULL,
#lineitemid uniqueidentifier = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT
[Study]
,[LineItemID]
,[QuoteID]
,[Total]
,[COOP]
,[VendorCost]
,[CustCost]
,[LineItemNumber]
,[StudyTypeCode]
,[GroupLeader]
,[PTWDate]
,[PONumber]
,[POStatus]
,[StudyDirector]
,[SL_DESC_L]
,[SL_Code]
,ProjectDescription
,CreatedBy
,chARProcess
,CODate
FROM
[ARDB].[dbo].[PTWView]
WHERE
(#studynumber is null or StudyNumber=#studynumber)
AND (#quoteid is null or QuoteID=#quoteid)
AND (#lineitemid is null or LineItemID = #lineitemid)
END
have you tried
myCommand.Parameters.AddWithValue("#studynumber", studynumber);
instead of:
myCommand.Parameters.AddWithValue("#study", studynumber);
EDIT
If passing parameters is the problem, then it comes down to how much time the stored procedure takes to execute. Default timeout for SQL server is usually 120 secs. You can add "Connect Timeout" to increase timeout in your DB connection string and check out.
** Old Answer -- Ignore **
Without stack trace, and taking your word that the stored procedure is fine, I am guessing that it is timing out due to the connection failure. The code is unable to connect to your DB server and hence timing out.
setting arithabort off made the sp take 45 seconds as opposed to 1. setting it back on changed it back to 1. I updated the stored procedure to set it on, no change in the app. Changed it to off, no change. I then removed the update and then the app worked fine.
I believe what happened is that updating the stored procedure caused it to recompile, fixing the issue. I'm not 100% sure on this though.
One thing could be the ARITHABORT setting, set it to ON...NET defaults to OFF
run the proc in SSMS with ARITHABORT set to OFF and see if it runs slower now like from .NET
example
MyConnection.Execute "SET ARITHABORT ON"
Another thing is that your WHERE clause is not optimal, take a look at Do you use Column=#Param OR #Param IS NULL in your WHERE clause? Don't, it doesn't perform
does the proc run slow with parameters in SSMS? Can you show the execution plan?
Related
Assume we have a stored procedure like so
CREATE PROCEDURE CopyValue(IN src INT, OUT dest INT)
BEGIN
SET dest = src;
END
I want to call this from a .net app (assume connection etc created successfully)
var sql = "call CopyValue(100, #destValue); select #destValue as Results;";
The string in the above statement works perfectly well when called in MySql Workbench.
However this - obviously - fails with "MySqlException: Parameter '#destValue' must be defined" when executed on a MySqlCommand object in .net
How do I arrange this statement so I can capture an output parameter from an existing procedure?
NB: I'm running against MySql 5.6, which I can't upgrade at this time.
NB Calling the procedure directly with CommandType.StoredProcedure goes against company guidelines.
By default, user-defined variables aren't allowed in SQL statements by MySQL Connector/NET. You can relax this restriction by adding AllowUserVariables=true; to your connection string. No modifications to your SQL or how you're executing the MySqlCommand should be necessary.
For information about why this is the default, you can read the research on this MySqlConnector issue (which also has the same default behaviour, but a much better error message that will tell you how to solve the problem): https://github.com/mysql-net/MySqlConnector/issues/194
A colleague (who wishes to remain anonymous) has answered this perfectly. Essentially put backticks ` after the # and at the end of the variable name e.g.
#`MyParam`
A fully working example.
static void Main(string[] args)
{
using var con = new MySql.Data.MySqlClient.MySqlConnection("Data Source=localhost; User Id=...;Password=...;Initial Catalog=...");
con.Open();
using var cmd = con.CreateCommand();
cmd.CommandText = "call CopyValue2(100, #`v2`); select #`v2` as Results;";
using var reader = cmd.ExecuteReader();
if (reader.Read())
Console.WriteLine($"Copied Value {reader.GetInt64(0)}");
}
Thanks OG :)
I have a SQL stored procedure which uses openrowset command and fetches values from an excel sheet and inserts it into the database.
I have created a C# application which will call the procedure and execute it.
PROBLEM!
When I execute the procedure from SQL management studio, there are no errors. It happens perfectly. But when I execute it through the C# application I get an error: "Conversion failed when converting date and/or time from character string."
Code
SQL Query (only the insert part)
insert into tbl_item ([Item code],[Dt Created])
select[Item code] ,
case when [Dt Created] is null or [Dt Created]='' then null when ISDATE(CONVERT(nvarchar,CONVERT(datetime, [Dt Created],103))) =1 then CONVERT(datetime, [Dt Created],103) else null end as [Dt Created]
FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0','Excel 12.0; Database=C:\Upload\Report.xlsx;HDR=YES;IMEX=1;',
'select * from [Sheet1$]')
C# Code
public int updateItem()
{
SqlCommand cmd; cmd = new SqlCommand("usp_updateItem", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter returnParameter = cmd.Parameters.Add("RetVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
try
{
if (conn.State.Equals(ConnectionState.Closed))
conn.Open();
cmd.ExecuteNonQuery();
ret = Convert.ToInt32(returnParameter.Value);
}
catch (Exception e)
{
err = "Error: " + e.Message;
return -1;
}
finally
{
conn.Close();
}
return ret;
}
What is the format you are having in the [Dt Created] variable.
the convert statement you have in the case will convert only the following types below
YYYY-MM-DD
YYYY-DD-MM
DD-MM-YYYY
The error you are getting is since you have a date in the format of "MM-DD-YYYY" something like '12-24-2015'. Due to this you are getting the conversion error.
Excuse me I want to stop you here. Your problem has resolved now but whatever
Karthik Venkatraman had said is correct. Somehow you got solution but for learning purpose i recommended to investigate little bit more. This is not belongs to the whatever you have said but damm sure this belongs to date-format.
**
One trick
Create one DateTimeVariable and once its initialized then just parse it using DateTimeParse class according to the records exist in database.
I am sure you will get solution.. Thanks :)
This is how I finally solved it...
The SQL error message 'Failed Conversion' was absolutely a wrong pointer. It had no connection to the issue at hand. [If only I knew this before :( ]
The actual problem was that I had called another procedure within the main procedure I had posted above. This setup ran perfectly in SQL management studio which was running under my credentials. Now in the C# application, I had created another SQL login user ID to run it. And this user ID did not have execute permission to run the sub procedure. And ironically, SQL gave me a misleading conversion error. Once I gave the right permission it worked perfectly.
I want to update a table through sql code executed in a c# application. To do this, I've used the alter data generated my MSSMS and manually saved it as an sql-file. The c# then reads the file and tries to execute it but it can't. If I use the sql code by itself it works, but not when read by the c# funtion. What's wrong with my c# code?
The sql code generated my MSSMS:
/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.tTest ADD
NewColumn int NULL
GO
ALTER TABLE dbo.tTest SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
The c# code that reads it:
string content = string.Empty;
try
{
content = File.ReadAllText(string.Format(#"C:\temp\{0}.sql", name));
SqlConnection conn = new SqlConnection(ConnectionString);
SqlCommand command = new SqlCommand(content, conn);
command.Connection.Open();
command.ExecuteNonQuery();
command.Connection.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
The output that comes from the c# function (the console message):
A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Each batch (ended with GO) should be sent separately in one command.ExecuteNonQuery(). This method is not to be used for multiple batches.
Split your query into several pieces (where GO is) and execute it step by step.
The core issue with the script you are trying to run is wrapping a transaction around query batches (A query batch is terminated with a GO statement in SQL Server Management Studio).
To do the operations in C# you can execute both statements in one query batch. They do not require to be separate. If you wrap them in one query in a SqlCommand object you do not need to handle transactions as there is an "implicit" transaction created.
A last point to look out for is proper disposal of the objects implementing IDisposable. The easiest way to do this in C# is by wrapping them in a using clause. Once you did that, there is no more need to call the Close method on the command/connection objects.
Combining all these remarks gives you the following code:
try
{
using(var conn = new SqlConnection(ConnectionString))
using(var command = new SqlCommand(
#"ALTER TABLE dbo.tTest ADD NewColumn int NULL;
ALTER TABLE dbo.tTest SET (LOCK_ESCALATION = TABLE);", conn))
{
conn.Open();
command.ExecuteNonQuery();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
I'm attempting to integrate Red Gate's SQLBackup Pro software into my in-house backup software, written in C#. The natural way to do this is via their Extended Stored Procedure. The problems is that it's called in a format I've never seen before:
master..sqlbackup '-SQL "BACKUP DATABASE pubs TO DISK = [C:\Backups\pubs.sqb]"'
This works just fine when run via SSMS. Where I run into trouble is trying to call it from C# (using .NET 4 and Dapper Dot Net).
My first attempt doesn't work because it interprets the entire cmd string as the name of the stored procedure and throws the error "Cannot find stored procedure ''":
var cmd = "master..sqlbackup '-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'";
connection.Execute(cmd, commandType: CommandType.StoredProcedure, commandTimeout: 0);
My second attempt returns immediately and appears (to C#) to succeed, but no backup is actually taken (this also sucks for parameterization):
var cmd = "master..sqlbackup";
var p = new DynamicParameters();
p.Add("", "'-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'");
connection.Execute(cmd, p, commandType: CommandType.StoredProcedure, commandTimeout: 0);
My third attempt also appears to succeed, but no backup is actually taken:
var cmd = "master..sqlbackup '-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'";
connection.Execute(cmd, commandTimeout: 0);
What am I missing?
UPDATE 1:
I overlooked the Red Gate documentation that says the stored proc won't actually raise a SQL error, it just returns errors in an output table. Slick. This might explain why I was getting silent failures in the second and third tests above: some underlying problem and they're not collecting the output to show why.
Here's where I am now:
var cmd = "master..sqlbackup";
var p = new DynamicParameters();
p.Add("", "'-SQL \"BACKUP DATABASE pubs TO DISK = [C:\\Backups\\pubs.sqb]\"'");
p.Add("#exitcode", DbType.Int32, direction: ParameterDirection.Output);
p.Add("#sqlerrorcode", DbType.Int32, direction: ParameterDirection.Output);
connection.Execute(cmd, p, commandType: CommandType.StoredProcedure, commandTimeout: 0);
When I run this and check those output parameters, I get Exit Code 870:
No command passed to SQL Backup.
The command is empty.
So it's not seeing the empty-named paramater.
Update 2:
Capturing the above in a trace shows that the empty parameter string ends up replaced with #Parameter1= which explains why the stored procedure doesn't see it.
Your first attempt looks almost right. What I notice is that you failed to escape the backslashes. For this kind of thing, it's often easier to use the # prefix to disable escaping for the string. Also, you want to prepend with exec and make it CommandType.Text:
EDIT: fixed my own escaping bugs here
var cmd = #"exec 'master..sqlbackup -SQL ""BACKUP DATABASE pubs TO DISK = [C:\Backups\pubs.sqb]""'";
connection.Execute(cmd, commandType: CommandType.Text, commandTimeout: 0);
It's gross and not at all what I wanted, but this is what I've got working now:
var cmd = String.Format(#"
DECLARE #exitcode int;
DECLARE #sqlerrorcode int;
EXEC master..sqlbackup '-SQL \"BACKUP DATABASE [{0}] TO DISK = [{1}])\"', #exitcode OUTPUT, #sqlerrorcode OUTPUT;
IF (#exitcode >= 500) OR (#sqlerrorcode <> 0)
BEGIN
RAISERROR('SQLBackup failed with exitcode %d and sqlerrorcode %d ', 16, 1, #exitcode, #sqlerrorcode)
END
", myDbName, myBkpPath);
connection.Execute(cmd, commandTimeout: 0);
This executes the backup and actually returns failure status, which exposed an underlying issue that caused the failure part of the silent failures.
Before it runs, myDbName is checked against a list of known databases to ensure it exists and myBkpPath is generated by my code, so I'm not worried about injections. It's just...well, look at that. Hideous.
Have you tried creating a typical stored procedure that calls the extended stored procedure, and calling that from code? It looks like you have only a few parameters to deal with here.
You had several problems in your tests.
In the first one you set the CommandType.StoredProcedure. You should have set it to CommandType.Text so that it would be smart enough to just pass that string along to be exec'd.
In the subsequent examples, you didn't actually give the parameter a name. Go look at their SqlBackup procedure and see what the parameter names are. Then use it. Otherwise, nothing is going to be assigned to it.
I cannot figure out why the HasChanged value of my SqlCacheDependency object is coming back originally from the command execution as false, but somewhere almost immediately after it comes back from the database, the value changes to true.
Sometimes this happens before the item is even inserted into the cache, causing the cache to discard it immediately, sometimes it's after the insert, and I can grab an enumerator which sees the key in the cache but before I even loop to that item in the cache it's been deleted.
SPROC:
ALTER PROCEDURE [dbo].[ntz_dal_ER_X_Note_SelectAllWER_ID]
#ER_ID int
AS
BEGIN
SELECT
ER_X_Note_ID,
ER_ID,
Note_ID
FROM dbo.ER_X_Note e
WHERE
ER_ID = #ER_ID
END
The database is MS SQL Server 2008, broker service is enabled, and SOME output does cache and remain cached. For instance, this one works just fine:
ALTER PROC [dbo].[ntz_dal_GetCacheControllerByEntityName] (
#Name varchar(50)
) AS
BEGIN
SELECT
CacheController_ID,
EntityName,
CacheEnabled,
Expiration
From dbo.CacheController cc
WHERE EntityName = #Name
END
The code which calls the SPROC in question that fails:
DataSet toReturn;
Hashtable paramHash = new Hashtable();
paramHash.Add("ER_ID", _eR_ID.IsNull ? null : _eR_ID.Value.ToString());
string cacheName = BuildCacheString("ntz_dal_ER_X_Note_SelectAllWER_ID", paramHash);
toReturn = (DataSet)GetFromCache(cacheName);
if (toReturn == null)
{
// Set up parameters (1 input and 0 output)
SqlParameter[] arParms = {
new SqlParameter("#ER_ID", _eR_ID),
};
SqlCacheDependency scd;
// Execute query.
toReturn = _dbTransaction != null
? _dbConnection.ExecuteDataset(_dbTransaction, "dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms)
: _dbConnection.ExecuteDataset("dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms);
AddToCache(cacheName, toReturn, scd);
}
return toReturn;
Code that works
const string sprocName = "ntz_dal_GetCacheControllerByEntityName";
string cacheControlPrefix = "CacheController_" + CachePrefix;
CacheControl controller = (CacheControl)_cache[cacheControlPrefix];
if (controller == null)
{
try
{
SqlParameter[] arParms = {
new SqlParameter("#Name", CachePrefix),
};
SqlCacheDependency sqlCacheDependency;
// Execute query.
DataSet result = _dbTransaction != null
? _dbConnection.ExecuteDataset(_dbTransaction, sprocName, out sqlCacheDependency, arParms)
: _dbConnection.ExecuteDataset(sprocName, out sqlCacheDependency, arParms);
controller = result.Tables[0].Rows.Count == 0
? new CacheControl(false)
: new CacheControl(result.Tables[0].Rows[0]);
_cache.Insert(cacheControlPrefix, controller, sqlCacheDependency);
}
catch (Exception ex)
{
// if sproc retreival fails cache the result of false so we don't keep trying
// this is the only case where it can be added with no expiration date
controller = new CacheControl(false);
// direct cache insert, no dependency, no expiration, never try again for this entity
if (HttpContext.Current != null && UseCaching && _cache != null) _cache.Insert(cacheControlPrefix, controller);
}
}
return controller;
The AddToCache method is overloaded and has more tests in it; The direct _cache.Insert in the working method is to bypass those other tests. The working code helps determine if db caching should happen at all.
You can see that when the "non working" data is retrieved initially, all is OK:
But somewhere random beyond that point, in this instance, just stepping into the next method
And yet the data is NOT changing at all; I'm the only one touching this instance of the database.
It was really, really simple, so simple I completely overlooked it.
In this article Creating a Query for Notification, which I DID scour multiple times, it clearly states:
SET Option Settings
When a SELECT statement is executed under a notification request, the
connection that submits the request must have the options for the
connection set as follows:
ANSI_NULLS ON
ANSI_PADDING ON
ANSI_WARNINGS ON
CONCAT_NULL_YIELDS_NULL ON
QUOTED_IDENTIFIER ON
NUMERIC_ROUNDABORT OFF
ARITHABORT ON
Well, I read and re-read and RE-re-read the sproc, and I still didn't see that both ANSI_NULLS and QUOTED_IDENTIFIER were "OFF", not ON.
My dataset is now caching and retaining the data properly without false indicators of change.
I have a hunch that the issue is with your _eR_ID. I think that you should try adding a local variable to the failing procedure that uses an impossible value for _eR_ID, such as -1. I never trust what is going to happen when nulls are involved and I think this could be the source of your problem.
Here is the modified version that I recommend trying:
DataSet toReturn;
Hashtable paramHash = new Hashtable();
int local_er_ID = eR_ID.IsNull ? -1 : _eR_ID.Value;
paramHash.Add("ER_ID", local_eR_ID.ToString());
string cacheName = BuildCacheString("ntz_dal_ER_X_Note_SelectAllWER_ID", paramHash);
toReturn = (DataSet)GetFromCache(cacheName);
if (toReturn == null)
{
// Set up parameters (1 input and 0 output)
SqlParameter[] arParms = {
new SqlParameter("#ER_ID", local_eR_ID),
};
SqlCacheDependency scd;
// Execute query.
toReturn = _dbTransaction != null
? _dbConnection.ExecuteDataset(_dbTransaction, "dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms)
: _dbConnection.ExecuteDataset("dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms);
AddToCache(cacheName, toReturn, scd);
}
return toReturn;
Important
While creating the above code, I think I discovered the source of your problem: when setting the stored proc parameter, you are using _eR_ID but when you set the paramHash you are using _eR_ID.Value.
The code rewrite will solve this problem, but I suspect that this is the root of the problem.
Running into the same issue and finding the same answers online without any help, I was reasearching the xml invalid subscription response from profiler.
I found an example on msdn support site that had a slightly different order of code. When I tried it I realized the problem - Don't open your connection object until after you've created the command object and the cache dependency object. Here is the order you must follow and all will be good:
Be sure to enable notifications (SqlCahceDependencyAdmin) and run SqlDependency.Start first
Create the connection object
Create the command object and assign command text, type, and connection object (any combination of constructors, setting properties, or using CreateCommand).
Create the sql cache dependency object
Open the connection object
Execute the query
Add item to cache using dependency.
If you follow this order, and follow all other requirements on your select statement, don't have any permissions issues, this will work!
I believe the issue has to do with how the .NET framework manages the connection, specifically what settings are set. I tried overriding this in my sql command test but it never worked. This is only a guess - what I do know is changing the order immediately solved the issue.
I was able to piece it together from the following to msdn posts.
This post was one of the more common causes of the invalid subscription, and shows how the .Net client sets the properties that are in contrast to what notification requires.
https://social.msdn.microsoft.com/Forums/en-US/cf3853f3-0ea1-41b9-987e-9922e5766066/changing-default-set-options-forced-by-net?forum=adodotnetdataproviders
Then this post was from a user who, like me, had reduced his code to the simplest format. My original code pattern was similar to his.
https://social.technet.microsoft.com/Forums/windows/en-US/5a29d49b-8c2c-4fe8-b8de-d632a3f60f68/subscriptions-always-invalid-usual-suspects-checked-no-joy?forum=sqlservicebroker
Then I found this post, also a very simple reduction of the problem, only his was a simple issue - needing 2 part name for tables. In his case the suggestion resolved the issue. After looking at his code I noticed the main difference was waiting to open the connection object until AFTER the command object AND the dependency object were created. My only assumption is under the hood (I have not yet started reflector to check so only an assumption) the Connection object is opened differently, or order of events and command happen differently, because of this association.
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/bc9ca094-a989-4403-82c6-7f608ed462ce/sql-server-not-creating-subscription-for-simple-select-query-when-using-sqlcachedependency?forum=sqlservicebroker
I hope this helps someone else in a similar issue.