create or replace procedure naujas_veiksmai(
vartotojas number,
knyga number,
kiekis number,
grazinta VARCHAR2,
tipas varchar2,
error_msg out varchar2
)
is
begin
insert into veiksmai values(vt_id_seq.nextval,vartotojas,knyga,kiekis,tipas,sysdate,TO_DATE(grazinta,'YYYY-MM-DD'));
exception
when OTHERS THEN error_msg := 'Irasant ivyko klaida';
end;
This is procedure that must return custom error to program.
C# code:
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "naujas_veiksmai";
cmd.CommandType = CommandType.StoredProcedure;
OdbcParameter param = new OdbcParameter();
cmd.Parameters.Add("vartotojas", OracleType.Number).Value = vartotojas;
cmd.Parameters.Add("knyga", OracleType.Number).Value = knyga;
cmd.Parameters.Add("kiekis", OracleType.Number).Value = kiekis;
cmd.Parameters.Add("grazinta", OracleType.VarChar).Value = grazinti;
cmd.Parameters.Add("tipas", OracleType.VarChar).Value = tipas;
OracleParameter op = new OracleParameter("error_msg", OracleType.VarChar);
op.Direction = ParameterDirection.Output;
op.Size = 200;
cmd.Parameters.Add(op);
cmd.ExecuteNonQuery();
cmd.Parameters.RemoveAt(0);
Program not showing any errors from oracle, insert was not executed and I know that must be exception.
What is wrong in procedure or in oracle code ?
You've already told the stored procedure that, if any exception is thrown, write a message to the error_msg parameter.
Check the value of that parameter after executing the stored procedure:
cmd.ExecuteNonQuery();
var errorMessage = Convert.ToString(cmd.Parameters["error_msg"].Value);
It is not good idea to add additional out parameter for sending exception message to the back-end.
If you want to see your exception message in try/catch block , then you must use RAISE_APPLICATION_ERROR.
The RAISE_APPLICATION_ERROR built-in is used for just a single scenario: if you need to communicate an application-specific error back to the user.
RAISE_APPLICATION_ERROR (-20003, 'CUSTOM EXCEPTION MESSAGE');
and it will throw exactly same exception in this statement:
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
string customMessage = ex.Message;
}
Update:
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20003, 'CUSTOM EXCEPTION MESSAGE');
Related
I'm having problems executing a function in one of my oracle packages from c#. The following is my code for opening the connection and executing the function:
Decimal firstID = Decimal.Parse("2453699");// This values are just for testing
string secondID = "12345";
Decimal sec = Decimal.Parse("1");
string estatus = "TEXT";
OracleConnection con = new OracleConnection();
con.ConnectionString = "User Id=user;Password=the_pass;Data Source=Data_Source";
con.Open();
string sql = "Package.F_FUNCTION_1";
OracleCommand com = new OracleCommand(sql, con);
com.CommandType = System.Data.CommandType.StoredProcedure;
com.Parameters.Add("returnVal", OracleDbType.Varchar2, 32767);
com.Parameters["returnVal"].Direction = System.Data.ParameterDirection.ReturnValue;
com.Parameters.Add("v_firstID",OracleDbType.Decimal,10);
com.Parameters.Add("v_secondID", OracleDbType.Varchar2,200);
com.Parameters.Add("p_sec", OracleDbType.Decimal, 3);
com.Parameters.Add("p_estatus", OracleDbType.Varchar2,50);
com.Parameters["v_firstID"].Value = firstID;
com.Parameters["v_secondID"].Value = secondID;
com.Parameters["p_sec"].Value = sec;
com.Parameters["p_estatus"].Value = estatus;
com.ExecuteNonQuery();
string val = com.Parameters["returnVal"].Value.ToString();
con.Close();
And the following is the function in my package, which needs to call a second function in the same package:
FUNCTION F_FUNCTION_1(v_firstID IN NUMBER,
v_secondID IN VARCHAR2
,p_sec IN NUMBER DEFAULT NULL
,p_estatus IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
v_Return VARCHAR2(200) := '';
secuencia VARCHAR2(1000) := null;
secc VARCHAR2(1000) := NULL;
BEGIN
-- Some validations
v_Return := Package.F_FUNCTION_2(
v_secondID => v_secondID,
P_SEC => p_sec,
P_ESTATUS => p_estatus
);
return v_Return;
END F_FUNCTION_1;
FUNCTION F_FUNCTION_2(v_secondID IN VARCHAR2
,p_sec IN NUMBER DEFAULT NULL
,p_estatus IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
--some variables
v_URL VARCHAR2(500) := NULL;
BEGIN
--A lot of code here that works
RETURN v_URL;
END F_FUNCTION_2;
The problem is: when i run this code, i get the error:
ORA-01460: unimplemented or unreasonable conversion requested
I suppose it is related to the parameters types. I'v tried changing types, using ExecuteScalar instead of ExecuteNonQuery, changing the syntax of the parameters addition to the command... but they didn't work.
I know it's easy but I have a brain lock now.
Hope someone can help.
For this to work you need to add a parameter to your oracle function as an OUT parameter and then extract the parameter as you are doing after using ExecuteNonQuery. I would add the OUT parameter as the first one in your F_FUNCTION_1 function.
Trying to get Username by UserId - I have the following stored procedure:
ALTER PROCEDURE [dbo].[GET_UsernameByUserId_SP](
#UserId int,
#ExecutionResult nvarchar(64) OUTPUT
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #ExecutionResult = (SELECT TOP 1 Username FROM UserProfile WHERE UserId = #Userid);
END
Executed by the following method:
public string CallSpRetStr(String spName, SqlParameter[] sqlParams)
{
string sRet = null;
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.CommandText = spName;
myCommand.Parameters.Clear();
myCommand.Parameters.AddRange(sqlParams);
myCommand.Parameters.AddWithValue("#ExecutionResult", DbType.String);
myCommand.Parameters[myCommand.Parameters.Count - 1].Direction = ParameterDirection.Output;
try
{
if (myConnection.State == ConnectionState.Open)
{
myCommand.ExecuteNonQuery();
}
else
{
OpenConnection();
myCommand.ExecuteNonQuery();
CloseConnection();
}
sRet = myCommand.Parameters["#ExecutionResult"].Value.ToString();
}
catch (Exception ex)
{
CloseConnection();
}
return sRet;
}
Called by the following method:
public string GetUsernameByUserId(int UserId)
{
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#UserId", UserId);
return dal.CallSpRetStr("GET_UsernameByUserId_SP", parameters);
}
At runtime I get the following error message (caught by the try-catch in CallSpRetStr):
'Error converting datatype nvarchar to int'.
I've been banging my head in the wall for more than hour now, trying crazy things etc.
I have two questions:
1. Does anyone understand what is the problem in all the above?
2. Is anyone aware of a better way to get the username by the userid?
Thanks in advance.
This line is incorrect
myCommand.Parameters.AddWithValue("#ExecutionResult", DbType.String);
should be
myCommand.Parameters.AddWithValue("#ExecutionResult", new string(' ', 64);
The AddWithValue method expects, for its second parameter, the current value for the named parameter but you pass an enum (DbType.String == (int)16). AddWithValue then tries to build a parameter with the datatype corresponding to the value passed and thus creates an integer parameter. Of course this is not acceptable by your stored procedure that expects a nvarchar type
Also I would remove any possible misunderstanding on which parameter is the output one using the return value from the AddWithValue instead of an indexing on the parameter collection
SqlParameter p myCommand.Parameters.AddWithValue("#ExecutionResult", new string(' ', 64);
p.Direction = ParameterDirection.Output;
Notice that we need to create a string of the correct size because AddWithValue doesn't know the expected size of the parameter from the stored procedure and so it creates the parameter with the size equals to the length of the string passed.
I have a stored procedure in Oracle which receives an input parameter of type varchar2 varying array. The procedure works and if you invoke it from SQL, what I need is called from C#.
My script is this:
CREATE OR REPLACE PROCEDURE INTEGRATOR.PRC_TEST_PARAM_ARRAY (p_nros_moviles integrator.NROMOVIL_ARRAY) IS
BEGIN
FOR i IN 1..p_nros_moviles.count LOOP
IF p_nros_moviles(i) IS NOT NULL THEN
INSERT INTO INTEGRATOR.TEST_PARAM_ARRAY VALUES (p_nros_moviles(i));
END IF;
END LOOP;
END;
/
My user type:
CREATE OR REPLACE TYPE INTEGRATOR.NROMOVIL_ARRAY AS
VARYING ARRAY(100) OF VARCHAR2(15);
/
My invoke from PLSQL
DECLARE
v_array integrator.NROMOVIL_ARRAY;
BEGIN
v_array := integrator.NROMOVIL_ARRAY('9999999', '66666666');
integrator.prc_test_param_array(v_array);
END;
And I try this way from c#
try
{
using (OracleConnection connection = new OracleConnection())
{
connection.ConnectionString = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)" +
"(HOST=10.10.10.10)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)" +
"(SID=PORTANODE)));User Id=user;Password=*****;";
using (OracleCommand cmd = new OracleCommand("INTEGRATOR.PRC_TEST_PARAM_ARRAY", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter p = new OracleParameter();
p.ParameterName = "P_NROS_MOVILES";
p.OracleDbType = OracleDbType.Array;
p.Direction = ParameterDirection.Input;
p.UdtTypeName = "INTEGRATOR.NROMOVIL_ARRAY";
//p.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
p.Value = new string[] { "XXXX", "YYYY" };
cmd.Parameters.Add(p);
connection.Open();
cmd.ExecuteNonQuery();
MessageBox.Show("Ejecutado");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Someone could guide me I need to change to make it work
Be patient, Wait and wait.. it takes hell of a long time .. that is my experience
I'm not sure but I think that System.Data.OracleClient doesn't really support user defined arrays.
I'd try to write a helping stored function, which takes for example a comma separated string (these will be the values of your varray type), and splits it to values using WHILE LOOP and SUBSTR. Then in each iteration it adds the actual VARCHAR2 to a temporary integrator.NROMOVIL_ARRAY type variable using the EXTEND(1) to make place for the new value.
In the end the function returns the temporary integrator.NROMOVIL_ARRAY, and this value could be used in the stored procedure.
Here is my stored procedure:
[dbo].[DFW_Completed_Safety] (
#StartDate VARCHAR(10),
#Station VARCHAR(50),
#EmployeeID INT)
When I code the following:
SqlDataAdapter daAC_CSM = new SqlDataAdapter();
DataSet dsAC_CSM = new DataSet();
try
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlCmd = new SqlCommand();
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Connection = sqlConnection;
sqlCmd.CommandTimeout = 0;
sqlCmd.CommandText = "DFW_Completed_Safety";
sqlCmd.Parameters.AddWithValue("#StartDate", startdate);
sqlCmd.Parameters.AddWithValue("#Station", station);
sqlCmd.Parameters.AddWithValue("#EmployeeID", "0");
daAC_CSM.SelectCommand = sqlCmd;
daAC_CSM.Fill(dsAC_CSM);
}
return dsAC_CSM;
}
catch (Exception)
{
throw;
}
it throws the Exception: EmployeeID is received as a varchar.
Conversion failed when converting the varchar value 'd ' to data type int.
Things I tried:
1- Many others post on StackOverflow suggested that Convert.ToInt32(0); would do it. Since 0 is an Int32 by default, this isn't a solution.
2- Changing the method to receive varchar (send "0") and it doesn't work too.
Thanks for any ideas! (would be greater to keep the method signature to Int).
UPDATE: The question isn't answered yet, since changing my stored procedure to varchar didn't make it.. Any ideas?
Please rewrite your code like this:
try
{
sqlCon = new SqlConnection(connectionString);
sqlCmd = new SqlCommand();
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter daAC_CSM = new SqlDataAdapter();
DataSet dsAC_CSM = new DataSet();
sqlCmd.Connection = sqlCon;
sqlCmd.CommandTimeout = 0;
sqlCmd.CommandText = "DFW_Completed_Safety";
sqlCmd.Parameters.AddWithValue("#StartDate", startdate); //Using "#"
sqlCmd.Parameters.AddWithValue("#Station", station); //Using "#"
sqlCmd.Parameters.AddWithValue("#EmployeeID", 0); //Using "#"
foreach(SqlParameter p in sqlCmd.Parameters){
//Will print Name, Type and Value
System.Diagnostics.Trace.WriteLine("Name:" + p.ParameterName + "Type: " + p.DbType+" Value: "+p.Value);
}
sqlCon.Open();
daAC_CSM.SelectCommand = sqlCmd;
daAC_CSM.Fill(dsAC_CSM);
sqlCon.Close();
return dsAC_CSM;
}
catch (Exception ex)
{
throw ex;
}
What does it print? What error do you get?
When you run your procedure from SSMS you will most likely get the same error, as the error is most likely derived from the body of your procedure, rather than how you are calling it. If you have a value 'd ' in a column in the table that you're querying from - and you are comparing that column to an integer type, then you will receive that error. Also, a couple of asides:
You should be putting your SqlCommand and SqlConnection instances in a using clause or disposing of them manually since they are IDisposable.
You probably don't want throw ex in your catch block - you probably just want throw. By using throw ex you mess up the stack trace that was available in the original exception.
Finally it wasn't the first line. The FormName is a field that stores the FormID. The programmer that was here before was probably a noob or changed the Column datatype to int, making all queries not to work. Thanks anyways #Matt_Whitfield & #Luxspes. By the way Luxpes, you were right, it was written line 1 even on SSMS, but I did it using the same:
EXEC #return_value = [dbo].[DFW_Completed_Safety]
#StartDate = N'07-18-2012',
#Station = N'YHZ',
#EmployeeID = 0
And by doing Print #SqlStatement, I was able to copy & paste in a new Query and see that it was the Form*Name* that was an Int. Who knew that a Name could be an Int?
I have a oracle procedure which should return a concatenated string of all the parameters
create or replace procedure tin_builder (type in varchar2,
tin_serial in number, rand_digit in varchar2, tin out varchar2 ) is
BEGIN
tin := type || TO_CHAR(tin_serial) || rand_digit ;
END
Now i want to call the procedure from visual studio 2008 (C# code)
public void TinBuilder(string type, long tin_serial, string rand_digit)
{
OracleConnection connection = new OracleConnection("Data Source=xe;User ID=system;Password=******;");
OracleCommand cmd = new OracleCommand();
cmd.Connection = connection;
cmd.CommandText = "tin_builder";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("type", OracleDbType.Varchar2).Value = type;
cmd.Parameters.Add("tin_serial", OracleDbType.Decimal).Value = tin_serial;
cmd.Parameters.Add("rand_digit", OracleDbType.Varchar2).Value = rand_digit;
cmd.Parameters.Add("tin", OracleDbType.Varchar2).Direction = ParameterDirection.ReturnValue;
try
{
connection.Open();
cmd.ExecuteNonQuery();
TextBox1.Text = cmd.Parameters["tin"].Value.ToString();
}
catch (Exception ex)
{
}
finally
{
connection.Close();
}
}
Then called it with :
TinBuilder("1", 10000001, "37");
But it does not show any value in the text box :( . Please someone help me out.
"Type" is a reserved word in Oracle. Here's the link: http://www.cs.umbc.edu/help/oracle8/server.815/a42525/apb.htm
And as said by OMG Ponies change & try: SELECT type || TO_CHAR(tin_serial) || rand_digit INTO tin FROM DUAL;
And also make sure you always 'initiate' exception to catch these kind of errors
"Type" may be a reserved word. Are you sure the procedure compiled and is valid?
Also are you catching any error messages there, and hiding them with the catch clause? That seems like bad practice.