-edit- wrote a clearer explanation of what's not working down bottom. maybe just skip to that.
I'm having an issue with a stored procedure I wrote and getting the result in my c# application, yet when I execute it within MySQL benchmark the result returns fine.
Firstly the procedure is:
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetNextOpponent`(IN p_user_id INT, OUT p_target_id INT, OUT p_target_data MEDIUMBLOB, OUT p_target_rank INT)
BEGIN
DECLARE UserRank INT;
CALL DeleteOldSearches(); /* TODO remove and call on interval instead of every time*/
SET UserRank = (SELECT rank FROM world WHERE user_id = p_user_id);
IF UserRank IS NOT NULL
THEN
SELECT user_id, world_data, rank
INTO p_target_id, p_target_data, p_target_rank
FROM world
WHERE
user_id != p_user_id AND
user_id NOT IN (SELECT target_id FROM searches WHERE user_id = p_user_id) AND
shield < CURRENT_TIMESTAMP
ORDER BY ABS(UserRank - 3)
LIMIT 1;
END IF;
IF p_target_id IS NOT NULL
THEN
INSERT INTO searches (user_id, target_id)
VALUES (p_user_id, p_target_id);
END IF;
/*SELECT TargetID, TargetData, TargetRank;*/
END
Now if I call it in WorkBench with
call battlecraft_test.GetNextOpponent(1, #p_target_id, #p_target_data, #p_target_rank);
select #p_target_id, #p_target_data, #p_target_rank;
I have no issue, get a nice result
'3', BLOB, '2'
However if I execute it in my app with,
public static bool GetNextOpponent(int userID)
{
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(ConnectionString);
conn.Open();
using (var cmd = new MySqlCommand("GetNextOpponent", conn) {CommandType = CommandType.StoredProcedure})
{
cmd.Parameters.Add("#p_user_id", MySqlDbType.Int32).Value = userID;
cmd.Parameters.Add("#p_target_id", MySqlDbType.Int32).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_data", MySqlDbType.MediumBlob).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_rank", MySqlDbType.Int32).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
object a = cmd.Parameters["#p_target_id"].Value; // null
object b = cmd.Parameters["#p_target_data"].Value; // null
object c = cmd.Parameters["#p_target_rank"].Value; // null =(
return true;
}
}
catch (Exception ex)
{
Logger.LogError($"Unexpected exception of type {ex.GetType()}: {ex.Message}");
return false;
}
finally
{
conn?.Close();
}
}
The out params are all null.
There are a few interesting cases where I've got results out from my application, for instance most times if I step through each line of code while debugging it works fine, however most of the time nothing happens.
I'm really struggling with this, spent hours on it now - my database knowledge isn't as good as I'd like it to be at the moment and I'm out of ideas, so I'm hoping someone has an idea what it could be.
One of my attempts to fix this I tried not using OUT keywords and instead just returning the field and using ExecuteReader instead yet it still only sometimes works.
If I write another procedure to kind of wrap this procedure like so:
CREATE DEFINER=`root`#`localhost` PROCEDURE `test`(IN p_user_id INT)
BEGIN
call battlecraft_test.GetNextOpponent(p_user_id, #p_target_id, #p_target_data, #p_target_rank);
select #p_target_id, #p_target_data, #p_target_rank;
END
It works when I execute it using Reader, however the first row is always null and the second row has the result. It's a usable workaround but I'd rather get to the cause of it.
Thanks for any response in advance.
-edit-
Even stripping it back I have issues. If I reduce stored procedure to just
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetNextOpponent`(IN p_user_id INT)
BEGIN
SELECT user_id, world_data, rank
FROM world
WHERE
user_id != p_user_id AND
user_id NOT IN (SELECT target_id FROM searches WHERE user_id = p_user_id) AND
shield < CURRENT_TIMESTAMP
ORDER BY ABS((SELECT rank FROM world WHERE user_id = p_user_id) - rank)
LIMIT 1;
END
And then do it as a reader
public static bool GetNextOpponent(int userID)
{
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(ConnectionString);
conn.Open();
using (var cmd = new MySqlCommand("GetNextOpponent", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#p_user_id", MySqlDbType.Int32).Value = userID;
//System.Threading.Thread.Sleep(1000); // Un-commenting this makes it work...
using (var rdr = cmd.ExecuteReader())
{
if (!rdr.Read())
return false; // returns here
var r1 = rdr.GetValue(0); // null
var r2 = rdr.GetValue(1); // null
var r3 = rdr.GetValue(2); // null
return true;
}
}
}
catch (Exception ex)
{
Logger.LogError($"Unexpected exception of type {ex.GetType()}: {ex.Message}");
return false;
}
finally
{
conn?.Close();
}
}
While executing it in workbench still returns a nice result
call battlecraft_test.GetNextOpponent(1);
There are many things to be improved in your code
In the finally block of your try/catch validate and close the connection
Dont use generic Catch as the first level of catch use ("DatabaseSpecificExceptions" or "RunTimeExceptions" or any other Expected expection" and log that to a file or by email to debug furthers
public static bool GetNextOpponent(int userID)
{
MySqlConnection conn = null;
boolean resp ;
//we assume that if fails
resp = false ;
try
{
conn = new MySqlConnection(ConnectionString);
if(conn != null)
{
conn.Open();
using (var cmd = new MySqlCommand("GetNextOpponent", conn) {CommandType = CommandType.StoredProcedure})
{
//Addding parameters to the SQL commands - good comments never hurt
cmd.Parameters.Add("#p_user_id", MySqlDbType.Int32).Value = userID;
cmd.Parameters.Add("#p_target_id", MySqlDbType.Int32).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_data", MySqlDbType.MediumBlob).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#p_target_rank", MySqlDbType.Int32).Direction = ParameterDirection.Output;
//you are executing a NonQuery but expectin results ??
cmd.ExecuteNonQuery();
//see comments above
object a = cmd.Parameters["#p_target_id"].Value; // null
object b = cmd.Parameters["#p_target_data"].Value; // null
object c = cmd.Parameters["#p_target_rank"].Value; // null =(
//avoid returns in the middle of the flow until debug works
//return true;
resp = true ;
}
}
}
catch (Exception ex)
{
Logger.LogError($"Unexpected exception of type {ex.GetType()}: {ex.Message}");
}
finally
{
if(conn != null)
{
conn?.Close();
}
}
}
Related
I am having a problem returning an output parameter from a Sql Server stored procedure into a C# variable. I have read the other posts concerning this, not only here but on other sites, and I cannot get it to work. Here is what I currently have. Currently I am just trying to print the value that comes back. The following code returns a null value. What I an trying to return is the primary key. I have tried using ##IDENTITY and SCOPE_INDENTITY() (i.e. SET #NewId = SCOPE_IDENTITY()).
Stored Procedure:
CREATE PROCEDURE usp_InsertContract
#ContractNumber varchar(7),
#NewId int OUTPUT
AS
BEGIN
INSERT into [dbo].[Contracts] (ContractNumber)
VALUES (#ContractNumber)
Select #NewId = Id From [dbo].[Contracts] where ContractNumber = #ContractNumber
END
Opening the database:
pvConnectionString = "Server = Desktop-PC\\SQLEXPRESS; Database = PVDatabase; User ID = sa;
PASSWORD = *******; Trusted_Connection = True;";
try
{
pvConnection = new SqlConnection(pvConnectionString);
pvConnection.Open();
}
catch (Exception e)
{
databaseError = true;
}
Executing the command:
pvCommand = new SqlCommand("usp_InsertContract", pvConnection);
pvCommand.Transaction = pvTransaction;
pvCommand.CommandType = CommandType.StoredProcedure;
pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("#ContractNumber", contractNumber));
SqlParameter pvNewId = new SqlParameter();
pvNewId.ParameterName = "#NewId";
pvNewId.DbType = DbType.Int32;
pvNewId.Direction = ParameterDirection.Output;
pvCommand.Parameters.Add(pvNewId);
try
{
sqlRows = pvCommand.ExecuteNonQuery();
if (sqlRows > 0)
Debug.Print("New Id Inserted = ",
pvCommand.Parameters["#NewId"].Value.ToString());
}
catch (Exception e)
{
Debug.Print("Insert Exception Type: {0}", e.GetType());
Debug.Print(" Message: {0}", e.Message);
}
}
I slightly modified your stored procedure (to use SCOPE_IDENTITY) and it looks like this:
CREATE PROCEDURE usp_InsertContract
#ContractNumber varchar(7),
#NewId int OUTPUT
AS
BEGIN
INSERT INTO [dbo].[Contracts] (ContractNumber)
VALUES (#ContractNumber)
SELECT #NewId = SCOPE_IDENTITY()
END
I tried this and it works just fine (with that modified stored procedure):
// define connection and command, in using blocks to ensure disposal
using(SqlConnection conn = new SqlConnection(pvConnectionString ))
using(SqlCommand cmd = new SqlCommand("dbo.usp_InsertContract", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// set up the parameters
cmd.Parameters.Add("#ContractNumber", SqlDbType.VarChar, 7);
cmd.Parameters.Add("#NewId", SqlDbType.Int).Direction = ParameterDirection.Output;
// set parameter values
cmd.Parameters["#ContractNumber"].Value = contractNumber;
// open connection and execute stored procedure
conn.Open();
cmd.ExecuteNonQuery();
// read output value from #NewId
int contractID = Convert.ToInt32(cmd.Parameters["#NewId"].Value);
conn.Close();
}
Does this work in your environment, too? I can't say why your original code won't work - but when I do this here, VS2010 and SQL Server 2008 R2, it just works flawlessly....
If you don't get back a value - then I suspect your table Contracts might not really have a column with the IDENTITY property on it.
Before changing stored procedure please check what is the output of your current one. In SQL Server Management run following:
DECLARE #NewId int
EXEC #return_value = [dbo].[usp_InsertContract]
N'Gary',
#NewId OUTPUT
SELECT #NewId
See what it returns. This may give you some hints of why your out param is not filled.
I had a similar problem and first closed the connection and then read the parameters and it worked fine.
you can use pvConnection.Close(); before read the output parameter
try
{
pvConnection = new SqlConnection(pvConnectionString);
pvConnection.Open();
}
catch (Exception e)
{
databaseError = true;
}
pvCommand = new SqlCommand("usp_InsertContract", pvConnection);
pvCommand.Transaction = pvTransaction;
pvCommand.CommandType = CommandType.StoredProcedure;
pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("#ContractNumber", contractNumber));
SqlParameter pvNewId = new SqlParameter();
pvNewId.ParameterName = "#NewId";
pvNewId.DbType = DbType.Int32;
pvNewId.Direction = ParameterDirection.Output;
pvCommand.Parameters.Add(pvNewId);
try
{
sqlRows = pvCommand.ExecuteNonQuery();
pvConnection.Close();
if (sqlRows > 0)
Debug.Print("New Id Inserted = ",
pvCommand.Parameters["#NewId"].Value.ToString());
}
catch (Exception e)
{
Debug.Print("Insert Exception Type: {0}", e.GetType());
Debug.Print(" Message: {0}", e.Message);
}
}
Stored Procedure.........
CREATE PROCEDURE usp_InsertContract
#ContractNumber varchar(7)
AS
BEGIN
INSERT into [dbo].[Contracts] (ContractNumber)
VALUES (#ContractNumber)
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]
END
C#
pvCommand.CommandType = CommandType.StoredProcedure;
pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("#ContractNumber", contractNumber));
object uniqueId;
int id;
try
{
uniqueId = pvCommand.ExecuteScalar();
id = Convert.ToInt32(uniqueId);
}
catch (Exception e)
{
Debug.Print(" Message: {0}", e.Message);
}
}
EDIT: "I still get back a DBNull value....Object cannot be cast from DBNull to other types. I'll take this up again tomorrow. I'm off to my other job,"
I believe the Id column in your SQL Table isn't a identity column.
In your C# code, you are using transaction for the command.
Just commit the transaction and after that access your parameter value, you will get the value.
Worked for me. :)
I am trying to insert into a table while returning its identity value. But "Index was outside the bounds of the array" error is thrown. I can execute query in dbForge successfully but not when I try to execute query in C# with oracle managed data access .
Query is very simple . If I disable transaction, the row is inserted in the database but i get the error and cannot get the return value.
var query = #"insert into table1 VALUES (97,'Mondon') RETURNING Id INTO :id";
OracleTransaction transaction = null;
using (var connection = new OracleConnection(_conStr))
{
try
{
connection.Open();
var command = connection.CreateCommand();
transaction = connection.BeginTransaction();
command.Transaction = transaction;
command.CommandText = query;
command.CommandTimeout = 5 * 60;
command.Parameters.Add(new OracleParameter("id", OracleDbType.Int32, ParameterDirection.ReturnValue));
var result = command.ExecuteNonQuery();
transaction.Commit();
var id = Convert.ToInt32(command.Parameters["id"].Value);
}
catch (Exception ex)
{
transaction.Rollback();
Logger.LogError(ex);
}
}
you have too much code
you have misconception(s)
Let me know if you have questions, see comments inline
int newId = 0;
// NOTE, if you don't insert into field 'ID' you need to list fields
var sql = "insert into table1 (fld1, fd2) VALUES (97,'Mondon') RETURNING Id INTO :id";
try
{
using (var conn = new OracleConnection(_conStr))
{
using (var cmd = new OracleCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new OracleParameter("id", OracleDbType.Int32, ParameterDirection.Output)); // this is output, not return
conn.Open();
var count = cmd.ExecuteNonQuery();
if (count > 0) // table can have a trigger so number of rows changed can be more than 1
{
// YOUR BIG MISCONCEPTION HERE (FIXED)
OracleDecimal val = (OracleDecimal)cmd.Parameters["id"].Value; // this returns special oracle struct
int newId = val.ToInt32(); // you can use val.IsNull but here it is not possible
}
else
throw new Exception("Value not inserted");
}
}
}
catch (Exception ex)
{
Logger.LogError(ex);
}
Note that for insert of a single record explicit transaction is not needed
Old post, but I had this same problem today, and found a solution here: https://stackoverflow.com/a/29660204/210916
"You need to declare the variable as shown below. As a rule of thumb, always test your query on the Oracle server before you embed it into your code. Most, importantly use parametrized Store Procedures to avoid sql injection attacks. So Do not embed queries into your code." #Dan Hunex
In your query, you need to declare id before INSERT command
I think you have to encapsulate the insert into a function:
create function InsertTable1(n in integer, val in varchar2) return integer as
res integer;
begin
insert into table1 VALUES (n, val) RETURNING Id INTO res;
RETURN res;
end;
And then in your application:
var query = #"BEGIN :0 := InsertTable1(97,'Mondon'); END;";
It would be a good idea to define also the input values as bind-parameters, rather than static strings.
A full dynamic solution could be similar to this:
create function InsertTable(cmd in varchar2) return integer as
res integer;
begin
EXECUTE IMMEDIATE cmd USING OUT res;
RETURN res;
end;
var query = #"BEGIN :0 := InsertTable('insert into table1 VALUES (97,''Mondon'') RETURNING Id INTO :res'); END;";
I have an Oracle-stored procedure (SP) which returns a varchar parameter, but when I execute the SP and try to read the output, the string is cut. When I execute the SP on Toad and made an dbms.output, the string is well returned.
procedure testStringOutput(num in number, str out varchar2)
as
V_C NUMBER(38);
V_E VARCHAR2(2000);
begin
select '12345678910111213141516171819PPPPPPPPPPPPPPPPPPPPPPPPP'
into str
FROM DUAL;
exception
when others then
V_C := SQLCODE;
V_E:= SQLERRM;
dbms_output.put_line(V_C||'-> '||V_E);
end;
The way I call the SP as script:
declare
P2 VARCHAR2(2000) :='';
begin
PKGTEST.TESTSTRINGOUTPUT(1,P2);
dbms_output.put_line('output string - > '||P2);
end;
The output:
output string - > 12345678910111213141516171819PPPPPPPPPPPPPPPPPPPPPPPPP
The way the SP is called from c#:
public void TestOutputString()
{
string str = string.Empty;
using (connection)
{
OracleCommand objCmd = new OracleCommand();
objCmd.Connection = connection;
objCmd.CommandText = "PKGTEST.testStringOutput";
objCmd.CommandType = CommandType.StoredProcedure;
objCmd.Parameters.Add("num", OracleType.Number).Value = 0;
objCmd.Parameters.Add("str", OracleType.VarChar, 2000).Direction = ParameterDirection.Output;
foreach (OracleParameter parameter in objCmd.Parameters)
if (parameter.Value == null)
parameter.Value = DBNull.Value;
try
{
connection.Open();
objCmd.ExecuteNonQuery();
str = (objCmd.Parameters["str"].Value.ToString());
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
connection.Close();
}
}
But when I inspect the response string, it was split until 123456789101112131415161718, the another part (19PPPPPPPPPPPPPPPPPPPPPPPPP) was lost. See this image.
What am I doing wrong?
Update: I'm sure it happened, SP's return messages to WCF service, but when the client received the response, it was split.
Finally i have to use Oracle data provider odp.net instead of ado.net - System.Data.OracleClient (deprecated) and it works.
I have been trying to retrieve some information from my database, and also retrieve the return value. I know the Stored Procedure works fine.
The code I use is a modified piece I use for registering the user. It's going wrong at the cmd.ExecuteReader part of my code.
protected void btn_login_Click(object sender, ImageClickEventArgs e)
{
//Actions after Submit button is clicked
Page.Validate(((ImageButton)sender).ValidationGroup);
if (Page.IsValid)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DbConnectString"].ConnectionString))
{
SqlCommand cmd = new SqlCommand("usp_validateUsers", conn);
//Input Values
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("username", Uname.Text);
cmd.Parameters.AddWithValue("password", pwd.Text);
//Return Values
SqlParameter retParam = cmd.Parameters.Add("#RetVal", SqlDbType.Int);
retParam.Direction = ParameterDirection.ReturnValue;
SqlParameter acsParam = cmd.Parameters.Add("#ac_status", SqlDbType.Int);
acsParam.Direction = ParameterDirection.Output;
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar);
nikParam.Direction = ParameterDirection.Output;
try
{
// Open Connection and execute Stored Proc
conn.Open();
///////////SOMETHING GOES WRONG HERE///////////////
cmd.ExecuteReader();
//Retrieve Data
int retVal = (int)retParam.Value;
string nickname = nikParam.Value.ToString();
string ac_stats = acsParam.Value.ToString();
if (retVal != 0)
{
//Invalid Username or password
}
else
{
//Login User
}
}
catch (Exception Error)
{
lbl_login.Text = "An error occured, please try again later";
debug.Text = Error.Message;
}
finally
{
debug.Text = "\n Clossing Connection";
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close();
}
}
}
}
}
When I just want to receive the return value I simply use cmd.ExecuteScalar(); I know how to receive data when I'm passing the SQL query to the SQL database, but it seems to be different when using Stored Procedures..
EDIT
Probably could improve this code further but it really does what it should do.
ALTER PROCEDURE dbo.usp_validateUsers
#username varchar(10),
#password varchar(10),
#ac_status char(1) OUTPUT,
#memb_name varchar(15) OUTPUT
AS
IF EXISTS(SELECT * FROM MEMB_INFO WHERE (memb___id = #username))
BEGIN
SELECT #ac_status = ac_status, #memb_name = memb_name
FROM MEMB_INFO
WHERE (memb___id = #username) AND (memb__pwd = #password)
RETURN 0
END
ELSE
BEGIN
return 1
END
When I use break points to catch possible exceptions in Visual Studio, It gives me:
String[4]: The Size property has an invalid size of 0
The error you mentioned may be caused by the fact that you're not specifying the size of your VarChar parameters. Instead of having lines like this:
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar);
Try this:
SqlParameter nikParam = cmd.Parameters.Add("#memb_name", SqlDbType.VarChar, 15);
You need to create a SqlDataReader.
From Ins and Outs of using Stored Procedures in C#
The SqlDataReader class is used to
read a forward only stream of records
returned from the database. The
SqlDataReader object is not
instantiated directly through a
constructor (hence the lack of the New
key word) but rather through the
ExecuteReader method of the SqlCommand
object. Before calling the
ExecuteReader method the connection to
the database is established using the
Open method of the SqlConnection
object.
Try
SqlDataReader drLogins;
Conn.Open();
drLogins = cmd.ExecuteReader();
Your #ac_status is defined as integer in parameter. change it character or string.
SqlParameter acsParam = cmd.Parameters.Add("#ac_status", SqlDbType.Int);
This seems pretty trivial, but it is now frustrating me.
I am using C# with SQL Server 2005 Express.
I am using the following code. I want to check if a database exists before creating it. However, the integer returned is -1 and this is how MSDN defines what ExecuteNonQuery() will return as well. Right now, the database does exist but it still returns -1. Having said that, how can I make this work to get the desired result?
private static void checkInventoryDatabaseExists(ref SqlConnection tmpConn, ref bool databaseExists)
{
string sqlCreateDBQuery;
try
{
tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes");
sqlCreateDBQuery = "SELECT * FROM master.dbo.sysdatabases where name =
\'INVENTORY\'";
using (tmpConn)
{
tmpConn.Open();
tmpConn.ChangeDatabase("master");
using (SqlCommand sqlCmd = new SqlCommand(sqlCreateDBQuery, tmpConn))
{
int exists = sqlCmd.ExecuteNonQuery();
if (exists <= 0)
databaseExists = false;
else
databaseExists = true;
}
}
}
catch (Exception ex) { }
}
As of SQL Server 2005, the old-style sysobjects and sysdatabases and those catalog views have been deprecated. Do this instead - use the sys. schema - views like sys.databases
private static bool CheckDatabaseExists(SqlConnection tmpConn, string databaseName)
{
string sqlCreateDBQuery;
bool result = false;
try
{
tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes");
sqlCreateDBQuery = string.Format("SELECT database_id FROM sys.databases WHERE Name
= '{0}'", databaseName);
using (tmpConn)
{
using (SqlCommand sqlCmd = new SqlCommand(sqlCreateDBQuery, tmpConn))
{
tmpConn.Open();
object resultObj = sqlCmd.ExecuteScalar();
int databaseID = 0;
if (resultObj != null)
{
int.TryParse(resultObj.ToString(), out databaseID);
}
tmpConn.Close();
result = (databaseID > 0);
}
}
}
catch (Exception ex)
{
result = false;
}
return result;
}
This will work with any database name you pass in as a parameter, and it will return a bool true = database exists, false = database does not exist (or error happened).
Reading this a few years on and there's a cleaner way of expressing this:
public static bool CheckDatabaseExists(string connectionString, string databaseName)
{
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand($"SELECT db_id('{databaseName}')", connection))
{
connection.Open();
return (command.ExecuteScalar() != DBNull.Value);
}
}
}
shouldn't this
"SELECT * FROM master.dbo.sysdatabases where name = \'INVENTORY\'"
be this?
"SELECT * FROM master.dbo.sysdatabases where name = 'INVENTORY'"
Also According to MSDN
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.
You are doing a SELECT not an DML statement. Why don't you use a ExecuteReader method instead?
An alternative to querying the system views is to use the function db_id which returns the Id of the database if it exists, otherwise null. Example T-SQL below:
if (db_id('INVENTORY') is null)
begin
return 0
end
else
begin
return 1
end
Took Stephen Lloyd's code and added some async and sql injection mitigation.
public static async Task<bool> TestDatabase(string connectionString, string databaseName)
{
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT db_id(#databaseName)", connection))
{
command.Parameters.Add(new SqlParameter("databaseName", databaseName));
connection.Open();
return (await command.ExecuteScalarAsync() != DBNull.Value);
}
}
You can't use ExecuteNonQuery because it will always return -1 for SELECT, as the MSDN link shows.
You'll have to use process a resultset eg SELECT DB_ID('INVENTORY') AS DatabaseID or use a variable/parameter: SELECT #DatabaseID = DB_ID('INVENTORY')
For the benefit of searchers, if you are using Entity Framework, this will work:
using (var ctx = new MyDataModel())
{
dbExists = System.Data.Entity.Database.Exists(ctx.Database.Connection);
}
Use this Assembly: Microsoft.SqlServer.SqlManagementObjects => NuGet
using Microsoft.SqlServer.Management.Smo;
var dbExists = new Server(serverOrInstanceName).Databases.Contains(dataBaseName);