i have a table test
CREATE TABLE TEST
(
ID NUMBER,
NAME VARCHAR2(30 BYTE)
)
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON test
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT test_sequence.nextval INTO :NEW.ID FROM dual;
END;
ALTER TABLE TEST ADD (
PRIMARY KEY
(ID)
Now I want to insert from a page with values from textbox.
The code is given below
protected void ASPxButton1_Click(object sender, EventArgs e)
{
//string name = Convert.ToString(ASPxTextBox1.Text);
int retVal = 0;
string providerName = "Oracle.DataAccess.Client";
string constr =
#"User Id=scott;Password=tiger;Data Source=orcl;enlist=true";
// Get the provider factory.
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
try
{
using (scope txn = new scope())
{
using (DbConnection conn1 = factory.CreateConnection())
{
conn1.ConnectionString = constr;
conn1.Open();
// Create a command to execute the sql statement.
DbCommand cmd1 = factory.CreateCommand();
cmd1.Connection = conn1;
cmd1.CommandText = #"insert into test ( [NAME] ) values (:name)";
//OracleParameter Nameprm = cmd1.Parameters.Add("name" , OracleDbType.Varchar2, 10 );
//Nameprm.Direction = ParameterDirection.Input;
//Nameprm.Value = ASPxTextBox1;
var name = Convert.ToString(ASPxTextBox1);
var parameter = factory.CreateParameter();
parameter.DbType = System.Data.DbType.String;
parameter.ParameterName = "#name";
parameter.Value = name;
cmd1.Parameters.Add(parameter);
retVal = cmd1.ExecuteNonQuery();
//Console.WriteLine("Rows to be affected by cmd1: {0}", retVal);
// Close the connection and dispose the command object.
conn1.Close();
conn1.Dispose();
cmd1.Dispose();
}
txn.SaveChanges();
}
}
catch (Exception ex)
{
throw;
}
}
}
But i am always getting ORA-00928: missing SELECT keyword. but if i give values directly then its inserting data. I am trying to solve it in many ways still no luck. Your help will be very much appreciated as i am stuck here. Thank you in advance.
#competent_tech 's answer looks good, as an additional thought:
In my last job-but-one we used a massive Oracle database for all our persistence, and I had no end of grief with this sort of problem. Most of them were solved when I stopped using the built-in ADO.Net drivers and used the Oracle provided drivers (as recommended by Microsoft here).
You can get the Oracle Data Provider for .Net (ODP.Net) for your version of Oracle here.
If you're already using the ODP.Net my sincere apologies, it's a little hard to tell from the code snippet :)
If the name isn't the problem, then it may be with the parameter type (OracleDBType.VarChar2, although this should be inferred for you by DbType) or the parameter direction (ParameterDirection.InputOnly parameter?)
I hope that might help :)
I don't think that your parameter name is correct.
Try changing:
parameter.ParameterName = "#name";
to
parameter.ParameterName = "name";
Related
I have a web service in C#, I use it to consults from tables, but I want to create a WebMethod to call a stored procedure and get back multiples output parameters. I can execute it with output parameters, it doesn't work when I try to call it whit outputs parameters.
This is a sample, I want to get back more that 2 parameters.
Stored procedure:
CREATE OR REPLACE PROCEDURE O_CAPEREZ.GIO_SP (
VNOMBRE IN VARCHAR2,
SALUDO OUT VARCHAR2 )
AS
BEGIN
INSERT INTO G_PRUEBA_SP(NOMBRE)
VALUES (vNOMBRE);
SALUDO:= ('Hello: ' || vNOMBRE);
END;
And this is my code in the web service, when I execute it using output variables I get this error
[HYC00] [Oracle][ODBC]Optional feature not implemented
C# code:
[WebMethod]
public string AP_Data(string curp)
{
string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
using (OdbcConnection con = new OdbcConnection(constr))
{
OdbcCommand cmd = new OdbcCommand("{CALL GIO_SP(?,?)}", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#vNOMBRE", (curp));
cmd.Parameters.Add("#vNOMBRE", OdbcType.VarChar, 18);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
cmd.Parameters["#SALUDO"].Direction = ParameterDirection.ReturnValue;
cmd.Connection.Close();
string ret = Convert.ToString(cmd.Parameters["#SALUDO"].Value);
return ret;
}
}
You have to add the parameter to the list even if you're not going to set a value there:
cmd.Parameters.Add("#SALUDO", OdbcType.VarChar, 18).Direction = ParameterDirection.Output;
I don't know the the Oracle flavor is different, but in SQL I use ParameterDirection.ReturnValue rather than ParameterDirection.Output.
here's how i do it in MS SQL server 2008 But notice the data type and the lenth of the variables your create must be the same in your table
the stored proc create code
USE DATABASE DATABASE_NAME
GO
CREATE PROC SP_METHOD
#ID_CATIGORY INT,
#NAME VARCHAR (50),
#DESCRIPTION VARCHAR (50)
AS
INSERT INTO TABLE_NAME
([ID_CAT]
,[NAME_PRODUCT]
,[DESC_PRODUCT]
)
VALUES
( #ID_CATIGORY
,#NAME
,#DESCRIPTION )
GO
in the c# code
// Create SqlConnection
SqlConnection conn= new SqlConnection(#"Server=server_name;
DataBase=your_data_base_name;Integrated Security=false;User
Id=user_id;Password=password");
// Open the Connection
if (sqlconnection.State != ConnectionState.Open)
{
conn= .Open();
}
// execute stored_procedure method don't change this
public void ExecuteCommand(string stored_procedure, SqlParameter[] param)
{
SqlCommand sqlcomd = new SqlCommand();
sqlcomd.CommandType = CommandType.StoredProcedure;
sqlcomd.CommandText = stored_procedure;
sqlcomd.Connection = sqlconnection;
if (param !=null)
{
sqlcomd.Parameters.AddRange(param);
}
sqlcomd.ExecuteNonQuery();
}
// close connection method
public void close_conn()
{
if (sqlconnection.State == ConnectionState.Open)
{
sqlconnection.Close();
}
}
// execute and retrieving data Method
public void Add_product(int ID_cat ,string Name_Product,string
Des_Product)
{
SqlParameter[] param = new SqlParameter[3];
param[0] = new SqlParameter("#ID_CAT", SqlDbType.Int);
param[0].Value = ID_cat;
param[1] = new SqlParameter("#NAME_PRODUCT", SqlDbType.VarChar, 50);
param[1].Value = Name_Product;
param[2] = new SqlParameter("#DESC_PRODUCT", SqlDbType.VarChar, 50);
param[2].Value = Des_Product;
ExecuteCommand("StoredProcedure_name", param);
close_conn();
}
and finally you can call this function
Add_product(Convert.ToInt32(ComboBox.SelectedValue),txt_name.Text,
txt_desc.Text);
if there is any part you don't understand lemme know
I've seen many ways to accomplish this.
One way is to Pipe Delimit your select statement in your stored procedure and then use "Value1|Value2".Split('|')[0] to get Value1.
You could also return a table instead of using multiple parameters
DataTable table = new DataTable();
DataAdapter adapter = new DataAdapter(cmd);
adapter.fill(table);
return table.Rows[0]["Greeting"] + table.Rows[0]["Name"];
In the second example you can return as many 'Parameters' as you want, but you will have to assign them to their rightful spots later in your code.
I've also seen an XML way to do this same feature but I won't provide the code here since I don't personally think it is a very good way to do it. The way I've seen done was adding a bunch of XML attributes to a parent tag, and then coming back later and finding the value of each tag later in the code.
In MYSQL it would go like this
CREATE PROCEDURE O_CAPEREZ.GIO_SP (
#vNOMBRE VARCHAR(50))
AS
BEGIN
INSERT INTO G_PRUEBA_SP(NOMBRE)
VALUES (#vNOMBRE);
select 'Hola' as Greeting, #vNOMBRE as Name
END
Also note what Marc_s commented
You need to set the .Direction of the parameter BEFORE making the call to .ExecuteNonQuery()
I've spent about 7 hours trying to figure this out by trial and error. All the online examples I have seen either don't work, or dont apply, or only show half of what Im looking for.
Here is what I'm asking for:
1. An example of a simple stored procedure in MYSQL using one IN parameter and one OUT parameter.
2. An example of a FUNCTIONING (really important, cause online examples havent worked sometimes...) call from Visual Studio, using C#. Either a text call or stored procedure command type work.
3. AddWithValue has been deprecated.
4. I would love to see the out parameter actually work.
If this is impossible with MYSQL and visual studio, that would be nice to know as well.
MYSQL documentation is not thorough enough for this particular example. And pls, no Visual Studio or C# hatred.
Thanks in advance! :)
EDIT:
This is what I have managed to do so far, and it DOES NOT WORK!!!
MYSQL side, using HeidiSQL:
CREATE DEFINER=`root`#`localhost` PROCEDURE `login`(IN `stuff` VARCHAR(50), IN `pass` VARCHAR(50), OUT `param3` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
set param3 = 0;
set param3 = (select count(*) from users where username=stuff and userpassword=pass);
select #param3;
END
And in C# side, I attempt to get this OUT parameter. Now, this is after multiple iterations, where I have gutted what the function used to be, and boiled it down to two issues: 1. The OUT parameters won't work, and 2. Even though Visual studio passes IN parameters, SQL refuses to recognize them.
protected void Login_Authenticate(object sender, AuthenticateEventArgs e)
{
using (MySqlConnection con = new MySqlConnection(strcon))
{
con.Open();
MySqlCommand com = new MySqlCommand("CALL login(#stuff, #pass, #param3);", con);
com.CommandType = CommandType.Text;
com.Parameters.Add("#stuff", MySqlDbType.VarChar);
com.Parameters["#stuff"].Value = Login.UserName;
com.Parameters.Add("#pass", MySqlDbType.VarChar);
com.Parameters["#pass"].Value = Login.Password;
try
{
obj = com.ExecuteScalar();
objparam = com.Parameters["param3"].Value;
if (Convert.ToInt32(obj) != 0)
{
Response.Redirect("Welcome.aspx");
}
else
{
Login.PasswordRequiredErrorMessage = "invalid user name and password";
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
con.Close();
}
}
I believe the code and the pictures say more than I ever will.
C# DB Layer (DB Layer has conn as a connection string):
// Note: this is an instance (myDB in terms of the GUI Object)
using System.Data;
using MySql.Data.MySqlClient;
...
...
public long MultBySeven(long theNum)
{ // Call a Mysql Stored Proc named "multBy7"
// which takes an IN parameter, Out parameter (the names are important. Match them)
// Multiply the IN by 7 and return the product thru the OUT parameter
long lParam = 0;
using (MySqlConnection lconn = new MySqlConnection(connString))
{
lconn.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = lconn;
cmd.CommandText = "multBy7"; // The name of the Stored Proc
cmd.CommandType = CommandType.StoredProcedure; // It is a Stored Proc
// Two parameters below. An IN and an OUT (myNum and theProduct, respectively)
cmd.Parameters.AddWithValue("#myNum", theNum); // lazy, not specifying ParameterDirection.Input;
cmd.Parameters.AddWithValue("#theProduct", MySqlDbType.Int32);
cmd.Parameters["#theProduct"].Direction = ParameterDirection.Output; // from System.Data
cmd.ExecuteNonQuery(); // let it rip
Object obj = cmd.Parameters["#theProduct"].Value;
lParam = (Int32)obj; // more useful datatype
}
}
return (lParam);
}
C# GUI Test Layer:
private void btnTestInOut_Click(object sender, EventArgs e)
{ // This GUI Layer call thru the use of a business object or data layer object (`myDB`)
long localHere = myDB.MultBySeven(11);
}
Stored Procedure (take a number, multiply by 7):
DROP PROCEDURE IF EXISTS multBy7;
DELIMITER $
CREATE PROCEDURE multBy7
( IN myNum INT,
OUT theProduct INT
)
BEGIN
SET theProduct=myNum*7;
END$
DELIMITER ;
Debug View (read: it works. 11x7=77):
MySQL Connector 6.9.9.0 / Visual Studio 2015:
See also 5.10.1 Using Stored Routines from Connector/Net, age unknown.
You should set up a reference to the parameter
var param3 = new MySqlParameter();
param3.Direction = ParameterDirection.Output;
param3.DbType = // whatever the dbtype for int is or whatever you need.
param3.ParameterName = "param3";
com.Parameters.Add(param3);
in your try block, insert
var result = com.ExecuteReader(); // or com.ExecuteScalar();
after you execute that, your parameter should have the value populated and you should be able to also read the SP results (select).
var paramResult = param3.Value;
Reading the results of the SP can be done as reader or scalar.
// execute reader
while (result.Read()) {
int value = result.GetInt32(0));
} /* read returned values in result */
// execute scalar
int value;
if (int.TryParse($"{result}", out value)) {
/* do something with value */
}
/************************************************/
This block should get you where you need to go
const string strcon = "whatevs";
using (MySqlConnection con = new MySqlConnection(strcon))
{
const string sql = "login";
MySqlCommand com = new MySqlCommand(sql, con);
com.CommandType = CommandType.StoredProcedure;
var stuffParam = new MySqlParameter("stuff", stuffValue);
var passParam = new MySqlParameter("pass", passValue);
var param3Param = new MySqlParameter();
param3Param.ParameterName = "param3";
param3Param.DbType = DbType.Int32;
param3Param.Direction = ParameterDirection.Output;
com.Parameters.Add(stuffParam);
com.Parameters.Add(passParam);
com.Parameters.Add(param3Param);
try
{
var scalarResult = com.ExecuteScalar();
// because you used select #param3 in your sp.
int value;
if (int.TryParse($"{scalarResult}", out value))
{
//do something with value
}
//// because you used select #param3 in your sp.
//var readerResult = com.ExecuteReader();
//if (readerResult.Read())
//{
// //
// value = readerResult.GetInt32(0);
//}
int param3Returned;
if(int.TryParse($"{param3Param.Value}", out param3Returned))
{
// do something with param3Returned
}
}
catch (Exception ex)
{
// do something with ex
}
}
How to properly do the following update:
using (OracleConnection conn = new OracleConnection())
using (selCmd)
{
string sql1 = "update Table1 set name = joe where id = 10;"
string sql2 = "update Table2 set country = usa where region = americas;"
string sql3 = "update Table3 set weather = sunny where state = CA;"
string sql4 = "update Table4 set engine = v8 where maker = benz;"
cmdUpdate.CommandText = sql(#);
cmdUpdate.Connection = conn;
recs = cmdUpdate.ExecuteNonQuery();
}
I am aware of all or nothing if it's a transaction but I just to see how it works with correct approach.
I'm thinking iterate an array of items [sql1,sql2,sql3,sql4] and pass sql(#) in the CommandText and perform ExecuteNonQuery each time.
If I remember correctly, it is possible to concatenate multiple SQL statements in one string separated by semi-colons (;). Otherwise, there is nothing wrong with executing multiple ExecuteNonQuery() calls.
string sql1 = "BEGIN update Table1 set name = 'joe' where id = 10;",
sql2 = "update Table2 set country = 'usa' where region = 'americas';",
sql3 = "update Table3 set weather = 'sunny' where state = 'CA';",
sql4 = "update Table4 set engine = 'v8' where maker = 'benz'; END;";
string sql = string.Format("{0}{1}{2}{3}",sql1,sql2,sql3,sql4);
using (OracleConnection conn = new OracleConnection())
using (OracleCommand cmdUpdate = new OracleCommand(sql, conn))
{
conn.Open();
recs = cmdUpdate.ExecuteNonQuery();
}
I recently came across this issue in some old code. We dynamically build chain of SQL calls (with support for Oracle and Sql Server). Since there is no current Oracle production implementation, nobody tested Oracle operation and customer bugs are not coming in. I found a code that builds chain of commands and then, for Oracle it uses String.Split(';'). Then, it uses a loop to execute each statement in transaction: rowsAffecter += ExecuteNonQuery....
I don't like this idea because without parameterization it is dangerous approach, since some data can contain ;. But even if parameterization is in place...
... one of the issues of making anonymous block for Oracle ("begin... end;") is that ExecuteNonQuery will not return number of rows (returns -1), which is sometimes needed to judge if something got updated or not.
to solve this issue I've done this
private string AppendOracleCountOrNothing(StringBuilder sql)
{
if (_myProvider == Providers.Oracle)
sql.AppendLine("rowCnt := rowCnt + SQL%ROWCOUNT;");
}
public void SomeMethod()
{
var longSqlChain = new StringBuilder(2000);
longSqlChain.Append("Insert into table...;");
AppendOracleCountOrNothing(longSqlChain);
if (someCondition)
{
longSqlChain.AppendLine("Update anotherTable...;");
AppendOracleCountOrNothing(longSqlChain);
}
// may be, add some more sql to longSqlChain here....
int rowsAffected;
if (_myProvider == Providers.Oracle)
{
longSqlChain.Insert(0, #"DECLARE
rowCnt number(10) := 0
BEGIN
").AppendLine(#":1 := rowCnt;
END;");
// Now, here we have some abstract wrappers that hide provider specific code.
// But the idea is to prepare provider specific output parameter and then parse its value
IDataParameter p = ParameterWrapper.PrepareParameter(":1", 0, ParameterDirection.Output, myProvider); // note IDataParameter
SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, new[]{p});
rowsAffected = p.GetParameterValue(); // GetParameterValue is an extension on IDataParameter
}
else // sql server
{
rowsAffected = SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, null);
}
}
This way we make one trip to DB and get the return number of rows affected by this call. and queries can be parameterized as well. Again, better to develop abstraction layer, so, you can call something like parameterizer.CreateParameter(10), which will add parameter to collection and generate :1, :2, :3, etc. (oracle) and #1, #2, #3, etc. (sql server), in your sql statement.
Another approach is to create a simple extension method (ExecuteMultipleNonQuery) that simply splits the string on all semicolons and executes each statement in a loop:
public static class DbCommandExtensions {
public static void ExecuteMultipleNonQuery(this IDbCommand dbCommand)
{
var sqlStatementArray = dbCommand.CommandText.Split(new string[] {";"}, StringSplitOptions.RemoveEmptyEntries);
foreach (string sqlStatement in sqlStatementArray)
{
dbCommand.CommandText = sqlStatement;
dbCommand.ExecuteNonQuery();
}
}
}
Situation:
I'm trying to run a stored procedure that has an output parameter, which I need to catch.
I use C# 3.5 and the OracleClient with an OleDbConnection.
Research:
I've been looking around for other ways, but as far as I can tell I'm doing it correct. Microsoft support and various other forums.
Problem:
When I do the cmd.ExecuteNonQuery() it just gets stuck. No error or anything, it just stops there and holds the Thread.
When I try via OleDbDataReader or the Scalar it's nothing better.
If I change the CommandText (remove package name) it gives error that it can't find the stored procedure, so I know that is correct at least.
Code:
Oracle:
PROCEDURE deleteThemakaart
(an_seqthemakaart IN NUMBER, an_retval OUT NUMBER)
....
C#:
double InputValue = 777;
try
{
OleDbConnection con = new OleDbConnection(...);
con.Open();
OleDbCommand cmd = new OleDbCommand()
{
CommandText = "thema.pckg_themakaarten.deleteThemakaart",
Connection = con,
CommandType = CommandType.StoredProcedure,
};
OleDbParameter input = cmd.Parameters.Add("an_seqthemakaart", OleDbType.Double);
OleDbParameter output = cmd.Parameters.Add("an_retval", OleDbType.Double);
input.Direction = ParameterDirection.Input;
output.Direction = ParameterDirection.Output;
input.Value = InputValue;
cmd.ExecuteNonQuery();
return (double)output.Value;
}
catch (Exception ex)
{
...
}
finally
{
con.Close();
}
Any help is very welcome :)
Edit: some code is below in comment, but hasn't gotten me any further so far :(
Greetings
I found the trouble maker... one of the tables that the procedure used was locked.
I was hoping to find an easy way to get a parameter list of a stored procedures parameters. If the procedure has 3 paramaters, I want a list like this:
param1
param2
param3
It would be best to be able to do this in C# Code, but SQL would suffice as well. Ideas?
select * from information_schema.parameters
where specific_name='your_procedure_name'
Also refer this post to know more methods
https://exploresql.com/2016/10/14/different-methods-to-get-parameter-list-of-a-stored-procedure/
For SQL Server this should work.
private void ListParms()
{
SqlConnection conn = new SqlConnection("my sql connection string");
SqlCommand cmd = new SqlCommand("proc name", conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
SqlCommandBuilder.DeriveParameters(cmd);
foreach (SqlParameter p in cmd.Parameters)
{
Console.WriteLine(p.ParameterName);
}
}
You can do this without ever touching SqlConnection, which I find is a bonus.
This uses the SqlServer.Management.Smo namespace, so you need a reference to Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Management.Sdk, and Microsoft.SqlServer.Smo in your project.
Then use the following code:
Server srv = new Server("serverNameHere");
srv.ConnectionContext.AutoDisconnectMode = AutoDisconnectMode.NoAutoDisconnect;
srv.ConnectionContext.LoginSecure = false; //if using username/password
srv.ConnectionContext.Login = "username";
srv.ConnectionContext.Password = "password";
srv.ConnectionContext.Connect();
Database db = srv.Databases["databaseNameHere"];
foreach(StoredProcedure sp in db.StoredProcedures)
{
foreach(var param in sp.Parameters)
{
string paramName = param.Name;
var dataType = param.DataType;
object defaultValue = param.DefaultValue;
}
}
If you're familiar with Enterprise Library, there's a good method which allows to DiscoverParameters(), using the Data Access Application Block.
DbCommand command = new DbCommand();
command.CommandText = #"myStoredProc";
command.CommandType = CommandType.StoredProcedure;
Database database = new SqlDatabase(myConnectionString);
database.DiscoverParameters(command);
// ...
Some links that might help:
DiscoverParameters Method;
Microsoft.Practices.EnterpriseLibrary.Data Namespace.
The above links refers to EntLib 3.1. Depending on the .NET Framework version you're using, you might also consider downloading the correct EntLib version for you following this link.