Does anyone know of a way to call an Oracle 19 stored procedure in C# without the command object requiring an implicit Ref Cursor be created?
I have no need for any output from this procedure.
It just deletes records that are more than 2 hours old from a table - maybe there's some there - maybe not. There never is any output that I need back from this procedure.
When I try to execute it:
OraCommand.CommandText = sql;
OraCommand.CommandType = CommandType.StoredProcedure;
for (var i = 0; i < args.Length; i += 2)
{
OraCommand.Parameters.Add(args[i], OracleDbType.Varchar2).Value = args[i+1];
}
try
{
OraConn.Open();
}
catch (Exception ex)
{
return ex.Message;
}
int RowsAffected = OraCommand.ExecuteNonQuery();
I get this error:
Operation is not valid due to the current state of the object.
The command object contains this error:
OraCommand.ImplicitRefCursors' threw an exception of type 'System.NullReferenceException'
Does anybody know why?
Thanks
Related
I have a method that calls a Stored Procedure in MySQL database:
public DataTable ExecuteSP_ReturnDataTable(DynamicGridAction pAction)
{
DataTable ret = new DataTable();
MySqlCommand wCmd = new MySqlCommand();
try
{
wCmd = new MySqlCommand(pAction.SPName);
wCmd.CommandType = CommandType.StoredProcedure;
wCmd.Parameters.Add(new MySqlParameter(pAction.SPParameterName, MySqlDbType.LongText)).Value = pAction.SPParameterViewColumn;
MySqlParameter outMessage = new MySqlParameter("pmessage", MySqlDbType.LongText);
outMessage.Direction = ParameterDirection.Output;
wCmd.Parameters.Add(outMessage);
if (base.mSqlTransaction == null)
wCmd.Connection = base.GetOpenedConection();
else
{
wCmd.Connection = (MySqlConnection)base.mSqlTransaction.Connection;
wCmd.Transaction = (MySqlTransaction)base.mSqlTransaction;
}
ret.Load(wCmd.ExecuteReader());
return ret;
}
catch (MySqlException exp)
{
throw new FunctionalException(exp.Number, exp);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (base.mSqlTransaction == null)
base.CloseConection();
}
}
The StoredProcedure name and Parameter name are dinamics.
When I execute that method at the first time, it works. After that, I changed the name of the storedprocedure parameter in mysql (newParamName), and I change the "pAction.SPParameterName" value with the same (newParamName).
I execute that method after that and, surprisingly, the wCmd.ExecuteReader() method throws me:
System.ArgumentException: 'Parameter 'oldParamName' not found in the collection.'
I debug the code step by step and the name of the parameter is correct (the new one), but i get that exception every time I call that method.
But, if I restart the application (.net6 webapi), it works.
I think that there is some kind of "cache" for the StoredProcedure definition in my aplication.
The problem is that I need to change the parameter's name (I took it from other table in database), but the method continue internally calling the old one until I restart the aplication.
If you're using MySql.Data, set Procedure Cache Size = 0; in your connection string.
From Connection Options Reference:
ProcedureCacheSize, Procedure Cache Size, procedure cache, procedurecache
Default: 25
Sets the size of the stored procedure cache. By default, Connector/NET stores the metadata (input/output data types) about the last 25 stored procedures used. To disable the stored procedure cache, set the value to zero (0).
I've got an ASP.NET MVC site with an admin page where I need to merge two records. I'm passing in two values, #old and #new. After adding those to my SqlCommand object, I call the ExecuteNonQuery(). This is generating an exception with the message saying
stored procedure 'FixDuplicate' expects parameter '#old' which was
not supplied
As you can see below, I'm definitely adding the parameter. All 3 versions that I've tried are there.
What's wrong with this code?
Version 1: (please ignore the syntax of this one, I already removed this code, but I did try it and confirmed that the parameters existed when it reached the ExecuteNonQuery() call)
var sql = "FixDuplicate";
comm.Parameters.Add(new System.Data.SqlClient.SqlParameter("#old", model.Duplicate));
comm.Parameters.Add(new System.Data.SqlClient.SqlParameter("#new", model.Primary));
_dataAccessService.ExecuteSql(conn, comm);
Version 2:
comm.Parameters.AddWithValue("#old", model.Duplicate);
comm.Parameters.AddWithValue("#new", model.Primary);
Version 3:
comm.Parameters.Add("#old", System.Data.SqlDbType.Int);
comm.Parameters[0].Value = model.Duplicate;
comm.Parameters.Add("#new", System.Data.SqlDbType.Int);
comm.Parameters[1].Value = model.Primary;
Lastly, here's the code in the _dataAccessService.ExecuteSql(conn, comm) call:
public void ExecuteSql(SqlConnection connection, SqlCommand command, bool closeConnection = true)
{
if (connection == null)
throw new ArgumentNullException("connection");
if (command == null)
throw new ArgumentNullException("command");
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
try
{
command.ExecuteNonQuery();
}
catch
{
throw;
}
finally
{
if (closeConnection)
command.Dispose();
}
}
catch (Exception ex)
{
throw new Exception("Sorry, an error occurred ExecuteSql: " + ex.Message, ex);
}
finally
{
if (closeConnection)
connection.Dispose();
}
}
Note: I have confirmed that the values for both '#old' and '#new' are set when it gets to the ExecuteNonQuery() line.
Edit: Here's the full code, as it exists:
// Here we need to execute the "FixDuplicate" stored procedure
var sql = "FixDuplicate";
var conn = _dataAccessService.GetConnection("");
var comm = _dataAccessService.GetCommand(conn, sql, System.Data.CommandType.StoredProcedure);
//comm.Parameters.AddWithValue("#old", model.DuplicateWrestler);
//comm.Parameters.AddWithValue("#new", model.PrimaryWrestler);
comm.Parameters.Add("#old", System.Data.SqlDbType.Int);
comm.Parameters[0].Value = model.DuplicateWrestler;
comm.Parameters.Add("#new", System.Data.SqlDbType.Int);
comm.Parameters[1].Value = model.PrimaryWrestler;
_dataAccessService.ExecuteSql(conn, comm);
Probably your model.Duplicate is null. In this case value of parameter will not be set.
If you need to pass null to SqlParameter.Value, use DBNull.Value.
See SqlParameter.Value for reference.
I believe you need to specify a command type:
command.CommandType = CommandType.StoredProcedure;
I suggest you to check with Profiler if old parameter is set or not.
And also try to run this procedure from SQL Server Management Studio to be sure it's working fine.
I am coding a Sql-Server-ce application in C#.
Recently I have been converting my code to use using statements, as they are much cleaner. In my code I have a GetLastInsertedID function which is very simple - it returns the last inserted ID. The working version is as follows:
public static int GetLastInsertedID()
{
int key = 0;
try
{
SqlCeCommand cmd = new SqlCeCommand("SELECT CONVERT(int, ##IDENTITY)", DbConnection.ceConnection);
key = (int)cmd.ExecuteScalar();
}
catch (Exception ex)
{
MessageBox.Show("Could not get last inserted ID. " + ex.Message);
key = 0;
}
return key;
}
Below is the code that does NOT work once I wrap it in using statements:
public static int GetLastInsertedID()
{
int key = 0;
try
{
using (SqlCeConnection conn = new SqlCeConnection(DbConnection.compact))
{
conn.Open();
using (SqlCeCommand cmd = new SqlCeCommand("SELECT CONVERT(int, ##IDENTITY)", conn))
key = (int)cmd.ExecuteScalar();
}
}
catch (Exception ex)
{
MessageBox.Show("Could not get last inserted ID. " + ex.Message);
key = 0;
}
return key;
}
The error that I'm getting is specified cast is not valid. Although this error is usually self-explanatory, I cannot see why I would be getting it inside the second block of code, but not the first. This error occurs on the line key = (int)cmd.ExecuteScalar();.
What am I doing wrong with the second block of code?
From the ##IDENTITY documentation:
##IDENTITY and SCOPE_IDENTITY will return the last identity value generated in any table in the current session.
I think your change now starts a new session for each using statement. Therefore ##IDENTITY is null.
First of all, ##Identity will return any last generated ID from anywhere in SQL Server. Most probably you need to use SCOPE_IDENTITY() instead.
This shows your actual problem and design issue - you need to keep Connection and Command separate. Connection embeds transaction and though SCOPE_IDENTITY() will work until connection is closed; Command can be created, used and disposed.
So you need method which accept connection and use it to obtain identity - something like this (didn't check it but think idea should be clear):
public static int GetLastInsertedID(DbConnection connection)
{
try
{
string query = "SELECT CONVERT(int, SCOPE_IDENTITY())";
using (SqlCeCommand cmd = new SqlCeCommand(query, conn)) {
return (int)cmd.ExecuteScalar();
}
}
catch (Exception ex)
{
MessageBox.Show("Could not get last inserted ID. " + ex.Message);
return 0;
}
}
For working with connection you can create helper method like this:
public static SqlCeConnection OpenDefaultConnection()
{
SqlCeConnection conn = new SqlCeConnection(DbConnection.compact);
conn.Open();
return conn;
}
And use it like this:
...
using (SqlCeConnection conn = OpenDefaultConnection()) {
//... do smth
int id = GetLastInsertedID(conn);
//... do smth
}
...
in my opinion, the reason that it doesn't work is not related to the using statement.
If you use a static class to do the operation of connecting database, like DBHelper. The problem will be caused by that you close the connection of database before you execute the select ##identity and when you execute select ##identity, you open it again. This executing sequence will cause that the return result of select ##identity is NULL. That is, you can not use DBHelper.xxx() twice for getting the automated ID, because every time you call DBHelper.xxx(), the process of the opening database and the closing database will be done.
I have a solution but it maybe not the best one. Instead of using select ##identity, you can use select count(*) from xxx to get the same result.
Hope that it can help you
This is going to take some explaining. I'm new to ASP, having come from PHP. Completely different world. Using the MySql Connecter/Net library, I decided to make a database wrapper which had a fair amount of fetch methods, one being a "FetchColumn()" method which simply takes a string as its parameter and uses the following implementation:
public object FetchColumn(string query)
{
object result = 0;
MySqlCommand cmd = new MySqlCommand(query, this.connection);
bool hasRows = cmd.ExecuteReader().HasRows;
if (!hasRows)
{
return false;
}
MySqlDataReader reader = cmd.ExecuteReader();
int count = 0;
while(reader.HasRows)
{
result = reader.GetValue(count);
count++;
}
return result;
}� return result;
}public object FetchColumn(string query)
What I'm looking for is a way to return false IF and only IF the query attempts to fetch a result which doesn't exist. The problem is that, with my implementation, it throws an error/exception. I need this to "fail gracefully" at run time, so to speak. One thing I should mention is that with this implementation, the application throws an error as soon as the boolean "hasRows" is assigned. Why this is the case, I have no idea.
So, any ideas?
It's hard to say for sure, since you didn't post the exact exception that it's throwing, but I suspect the problem is that you're calling ExecuteReader on a command that is already in use. As the documentation says:
While the MySqlDataReader is in use, the associated MySqlConnection is busy serving the MySqlDataReader. While in this state, no other operations can be performed on the MySqlConnection other than closing it. This is the case until the MySqlDataReader.Close method of the MySqlDataReader is called.
You're calling cmd.ExecuteReader() to check to see if there are rows, and then you're calling ExecuteReader() again to get data from the rows. Not only does this not work because it violates the conditions set out above, it would be horribly inefficient if it did work, because it would require two trips to the database.
Following the example shown in the document I linked, I'd say what you want is something like:
public object FetchColumn(string query)
{
MySqlCommand cmd = new MySqlCommand(query, this.connection);
MySqlDataReader reader = cmd.ExecuteReader();
try
{
bool gotValue = false;
while (reader.Read())
{
// do whatever you're doing to return a value
gotValue = true;
}
if (gotValue)
{
// here, return whatever value you computed
}
else
{
return false;
}
}
finally
{
reader.Close();
}
}
I'm not sure what you're trying to compute with the HasRows and the count, etc., but this should get you pointed in the right direction.
you need to surround the error throwing code with a try clause
try {
//The error throwing Code
}
catch (exception e)
{
//Error was encountered
return false
}
If the error throwing code throws and error the catch statement will execute, if no error is thrown then the catch statement is ignored
First of all do a try and catch
try
{
//code
}
catch (Exception exp)
{
//show exp as message
}
And the possible reason of your error is that your mysql query has errors in it.
try executing your query directly in your mysql query browser and you'll get your answer.
If its working fine then double check your connection string if its correct.
NOTE:mark as answer if it solves your issue
I have a very simple Update statement that will update mail server settings and network credentials info... Query works fine when I run it in Access but C# keeps giving me the error stating that my SQL Syntax is wrong ... I have a dataaccess layer (dal class) and Update instance method pasted belows ... But the problem must be sth else cuz I have updated lots of stuff this way but this time it just won't do .. any clues will be greatly appreciated. Thx in advance.
Update instance method in DAL class .. (this is supposed to be a Data Access Layer :) I'm just a management graduate :P
public int UpdateRow(string Query, bool isSP, params OleDbParameter[] args)
{
int affectedRows = -1;
using (con = new OleDbConnection(connStr))
{
using (cmd = con.CreateCommand())
{
cmd.CommandText = Query;
if (isSP)
{
cmd.CommandType = CommandType.StoredProcedure;
}
if (args != null)
{
foreach (OleDbParameter prm in args)
{
cmd.Parameters.Add(prm);
}
}
try
{
con.Open();
affectedRows = cmd.ExecuteNonQuery();
}
catch(OleDbException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
}
}
return affectedRows;
}
And the ASP.NEt codebehind that will do the updating =
protected void Update_Click(object sender, EventArgs e) {
DAL dal = new DAL();
string upt = string.Format("UPDATE [MailConfig] SET Server='{0}', Username='{1}', Password='{2}', AddressFrom='{3}', DisplayName='{4}'",server.Text,username.Text,password.Text,replyto.Text,displayname.Text);
dal.UpdateRow(upt,false,null);
LoadData();
}
peace!
Trying wrapping your field names in [ ]. I have had problems in the past with certain field names such as a username and password and count, etc, being recognized as reserved words and screwing up the sql giving me an error.
First off - don't use string.Format here. Use parameters, and add parameters to the command. Right now, you are wide open to SQL injection attacks. Think "Bobby Tables".
Re "stating that my SQL Syntax is wrong" - can you please quote the exact error?
First of all, you have no where clause in your Update, so it will update all rows, and violate key constraints causing an error, if you have any.
Second, running that kind of code makes you very vunerable to SQL Injection, if someone enters a username that has a sql command embedded in it, you could lose all your data.
You should use parameterized queries. You specify your parameters in the sql command with #paramname instead of using {4}, and then with the command object do accessCommand.parameters.AddWithValue("#paramname", value)
You are using a CommandType of StoredProcedure, but your query is not a stored procedure name, its a sql query without a where clause.
UPDATE [MailConfig]
SET Server='{0}',
Username='{1}',
Password='{2}',
AddressFrom='{3}',
DisplayName='{4}'"
So you need to remove the command type line, or change it to a correct command type CommandType.Text, and add a Where clause specifying what rows are to be affected.
I don't think Access even has Stored Procedures, so there's no using to use that command type with it.
An example of a command that does use stored procedures would be something like:
string sqlCommString = "QCApp.dbo.ColumnSeek";
SqlCommand metaDataComm = new SqlCommand(sqlCommString, sqlConn);
metaDataComm.CommandType = CommandType.StoredProcedure;
The command string for that type is just the name of the stored proc.