How to call a stored procedure (with user parameter) via code? - c#

From my C# program, I need to call a stored procedure in an Oracle database, which has the following contract:
PKG_ENTITY.ALTER_ENTITY (VARCHAR2 NAME, VARCHAR2 FULLNAME, ATTRS_TYPE ATTRS, VARCHAR2 STATUS, INTEGER OUT RESULT, VARCHAR2 OUT ERRORMSG).
The RESULT and ERRORMSG parameters are OUT parameters.
I know about the ATTRS_TYPE type that it is specified:
TYPE ATTRS_TYPE IS TABLE OF VARCHAR2(2000) INDEX BY VARCHAR2(30);
I used to call this stored procedure as follows:
private void ExecuteNonQuery(string query, params OracleParameter[] parameters)
{
using (var connection = new OracleConnection(_connectionString))
{
var command = new OracleCommand(query, connection) { CommandType = CommandType.Text };
connection.Open();
command.Parameters.AddRange(parameters);
command.ExecuteNonQuery();
}
}
where query =
DECLARE
tNAME varchar2(100);
tATTRS PKG_ENTITY.ATTRS_TYPE;
tRESULT INTEGER;
tERRORMSG varchar2(100);
BEGIN
tNAME := :pEntityId;
tATTRS(:pPropId) := :pPropValue;
PKG_ENTITY.ALTER_ENTITY(tUSERNAME,NULL,tATTRS,NULL,tRESULT,tERRORMSG);
END;
Parameter values: pEntityId, pPropId and pPropValue are defined in the code.
Everything was fine, but then I received the requirement that it is necessary to log out values of the tRESULT and tERRORMSG parameters and with this I had great difficulties. I wanted to modify the query by adding a SELECT after calling the stored procedure. Like that:
DECLARE
tNAME varchar2(100);
tATTRS PKG_ENTITY.ATTRS_TYPE;
tRESULT INTEGER;
tERRORMSG varchar2(100);
BEGIN
tNAME := :pEntityId;
tATTRS(:pPropId) := :pPropValue;
PKG_USER.ALTER_USER(tUSERNAME,NULL,tATTRS,NULL,tRESULT,tERRORMSG);
SELECT tRESULT, tERRORMSG FROM DUAL;
END;
But such a query is not correct from the point of view of the pl/sql language. Therefore, I came to the conclusion that I need to use the stored procedure call directly and the code should look something like this:
private ProcedureResult ExecuteStoredProcedure(string procedureName)
{
using (var connection = new OracleConnection(_connectionString))
{
var command = new OracleCommand(procedureName, connection) { CommandType = CommandType.StoredProcedure };
connection.Open();
command.Parameters.Add("NAME", OracleDbType.Varchar2, "New name", ParameterDirection.Input);
command.Parameters.Add("FULLNAME", OracleDbType.Varchar2, "New fullname", ParameterDirection.Input);
var attr = new EntityAttribute() { attribute1 = "id", attribute2 = "value"};
command.Parameters.Add("ATTRS", EntityAttribute, "New fullname", ParameterDirection.Input);
command.Parameters.Add("STATUS", OracleDbType.Varchar2, "Status", ParameterDirection.Input);
command.Parameters.Add("RESULT", OracleDbType.Int32).Direction = ParameterDirection.Output;
command.Parameters.Add("ERRORMSG", OracleDbType.Varchar2).Direction = ParameterDirection.Output;
command.ExecuteNonQuery();
return new ProcedureResult()
{
StatusCode = int.Parse(command.Parameters["RESULT"].Value.ToString()),
Message = command.Parameters["ERRORMSG"].Value.ToString()
};
}
}
And here I had difficulties with the PKG_ENTITY.ATTRS_TYPE type definition.
TYPE ATTRS_TYPE IS TABLE OF VARCHAR2 (2000) INDEX BY VARCHAR2 (30);
I know that there is an IOracleCustomType interface, but I don’t understand how to implement it correctly.
For example
[OracleCustomTypeMapping("PKG_ENTITY.ATTRS_TYPE")]
public class EntityAttribute : INullable, IOracleCustomType
{
[OracleObjectMapping("ATTRIBUTE1")]
public string attribute1 { get; set; }
[OracleObjectMapping("ATTRIBUTE2")]
public string attribute2 { get; set; }
public bool IsNull => throw new System.NotImplementedException();
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
throw new NotImplementedException();
}
public void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
throw new NotImplementedException();
}
}
What should be the names of the fields of this class? I understand that 'ATTRIBUTE1' and 'ATTRIBUTE2' are not valid names.

This answer states that you cannot pass an INDEX BY VARCHAR2 associative array in C#. Instead, you can build the associative array in an anonymous PL/SQL block and call the procedure from there (as you were doing originally).
So you could use:
DECLARE
tATTRS PKG_ENTITY.ATTRS_TYPE;
BEGIN
tATTRS(:pPropId) := :pPropValue;
PKG_USER.ALTER_USER(
NAME => :pEntityId,
USERNAME => NULL,
ATTRS => tATTRS,
STATUS => NULL,
RESULT => :pResult,
ERRORMSG => :pErrorMsg
);
END;
Then pass in the parameters pPropId, pPropValue and pEntityId with the direction of ParameterDirection.Input as you were doing and pass pResult and pErrorMsg with the direction of ParameterDirection.Output.

The signature of the PL stored procedure should be
PKG_ENTITY.ALTER_ENTITY (VARCHAR2 NAME, VARCHAR2 FULLNAME, ATTRS_TYPE ATTRS, VARCHAR2 STATUS, INTEGER OUT RESULT, VARCHAR2 OUT ERRORMSG)
Do note that OUT was added to the parameter declaration.
And in the c#code you would need to make sure the parameters are marked as output
.Parameters.Add("RESULT", OracleDbType.Int32).Direction = ParameterDirection.Output;
Once done, you would just need to assign the desired value to the parameters in your PL/SQL stored procedure.
RESULT:= 0;

Calling a PL/SQL procedure is much simpler (not tested, but I assume you will get the idea):
var cmd = new OracleCommand("BEGIN PKG_ENTITY.ALTER_ENTITY(:NAME, :FULLNAME, :ATTRS, :STATUS, :RESULT, :ERRORMSG); END;"), connection);
cmd.CommandType = CommandType.Text;
// should work as well
// var cmd = new SqlCommand("PKG_ENTITY.ALTER_ENTITY(:NAME, :FULLNAME, :ATTRS, :STATUS, :RESULT, :ERRORMSG)"), connection);
// cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("NAME", OracleDbType.Varchar2, ParameterDirection.Input).Value = "New name"
cmd.Parameters.Add("FULLNAME", OracleDbType.Varchar2, ParameterDirection.Input).Value = "New fullname"
par = cmd.Parameters.Add("ATTRS", OracleDbType.Varchar2, ParameterDirection.Input);
par.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
var arr string[] = new string[] {"id" , "value"};
par.Value = arr;
par.Size = arr .Count;
cmd.Parameters.Add("STATUS", OracleDbType.Varchar2, ParameterDirection.Input).Value = "Status";
cmd.Parameters.Add("RESULT", OracleDbType.Int32, ParameterDirection.Output);
cmd.Parameters("RESULT").DbType = DbType.Int32;
cmd.Parameters.Add("ERRORMSG", OracleDbType.Varchar2, 100, null, ParameterDirection.Output);
cmd.Parameters("ERRORMSG").DbType = DbType.String;
cmd.ExecuteNonQuery();
var result = System.Convert.ToInt32(cmd.Parameters("RESULT").Value);
var errmsg = cmd.Parameters("ERRORMSG").Value.ToString();
Maybe this one helps: PL/SQL Associative Array Binding
Actually, I don't remember anymore why I do cmd.Parameters("RESULT").DbType = DbType.Int32;. Perhaps it was needed to make my code compatible for ODP.NET Provider 1.x and 2.0, see Mandatory Migration of .NET 1.x Stored Procedures to .NET 2.0 or Later

Related

Passing a List of objects to SQL Server stored procedure and retrieving an output value

I'm trying to pass list of objects and some other numbers and strings parameters to a SQL Server stored procedure using ado.net and execute it, and retrieve a value from an output parameter.
To capture the list of objects from the SQL Server stored procedure, I used a user-defined table type as shown here:
And this is the stored procedure:
ALTER PROCEDURE [dbo].[UpdateMailjetDetails]
#listCode VARCHAR(1000),
#listName VARCHAR(1000),
#mailjetListId BIGINT,
#mailjetListStatus INT,
#autoAsync BIT,
#contacts Contact READONLY,
#companyId INT,
#tblContactCompanyStatus INT,
#espListContactStatus INT,
#outputMessage VARCHAR(1000) OUT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION [Tran1]
BEGIN TRY
-- logic
--
--
SET #outputMessage = 'success';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1];
SELECT
'Error - Rollbacked -' AS CustomMessage,
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
SET #outputMessage = 'error - ' + ERROR_MESSAGE();
END CATCH
END
This is the C# code which calls the stored procedure.
public string SaveAndPassToMailjetWindowsService(string listCode, string listName, long mailjetListId, MailjetListStatus mailjetListStatus, bool autoSync, List<Contact> contacts, int companyId, TblContactCompanyStatus tblContactCompanyStatus, EspListContactStatus espListContactStatus)
{
try
{
string result;
var conString = GetMailCoreConnectionString();
using (var conn = new SqlConnection(conString))
{
var command = new SqlCommand("UpdateMailjetDetails", conn)
{ CommandType = CommandType.StoredProcedure };
command.Parameters.Add(new SqlParameter("#listCode", listCode));
command.Parameters.Add(new SqlParameter("#listName", listName));
command.Parameters.Add(new SqlParameter("#mailjetListId", mailjetListId));
command.Parameters.Add(new SqlParameter("#mailjetListStatus", (int) mailjetListStatus));
command.Parameters.Add(new SqlParameter("#autoAsync", autoSync));
var contactsParam =
new SqlParameter("#contacts", SqlDbType.Structured)
{
TypeName = "dbo.Contact",
Value = GetSqlDataRecordsContactsList(contacts)
};
command.Parameters.Add(new SqlParameter("#contacts", contactsParam));
command.Parameters.Add(new SqlParameter("#companyId", companyId));
command.Parameters.Add(new SqlParameter("#tblContactCompanyStatus", (int) tblContactCompanyStatus));
command.Parameters.Add(new SqlParameter("#espListContactStatus", (int) espListContactStatus));
var outputParameter = new SqlParameter
{
ParameterName = "#outputMessage",
SqlDbType = SqlDbType.VarChar,
Direction = ParameterDirection.Output
};
command.Parameters.Add(outputParameter);
conn.Open();
command.ExecuteNonQuery(); // throws exception
result = outPutParameter.Value.ToString();
conn.Close();
}
return result;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
And below is the C# method which converts the contacts list to a data table.
private List<SqlDataRecord> GetSqlDataRecordsContactsList(List<Contact> contacts)
{
try
{
List<SqlDataRecord> datatable = new List<SqlDataRecord>();
SqlMetaData[] sqlMetaData = new SqlMetaData[5];
sqlMetaData[0] = new SqlMetaData("Email", SqlDbType.NVarChar, 512);
sqlMetaData[1] = new SqlMetaData("Name", SqlDbType.NVarChar, 512);
sqlMetaData[2] = new SqlMetaData("TblContactId", SqlDbType.BigInt);
sqlMetaData[3] = new SqlMetaData("CompanyId", SqlDbType.Int);
sqlMetaData[4] = new SqlMetaData("TblContactCompanyId", SqlDbType.BigInt);
foreach (var contact in contacts)
{
SqlDataRecord row = new SqlDataRecord(sqlMetaData);
row.SetValues(contact.Email, contact.Name, contact.TblContactId ?? (object) DBNull.Value,
contact.CompanyId ?? (object) DBNull.Value,
contact.TblContactCompanyId ?? (object) DBNull.Value);
datatable.Add(row);
}
return datatable;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
Now, when I run the code it does all the logic in stored procedure properly and completes all the changes I'm trying to do. I can see that data has been added via the stored procedure properly, but SaveAndPassToMailjetWindowsService methods
command.ExecuteNonQuery();
throws an exception in the C# side (even though the stored procedure executed its logic properly).
No mapping exists from object type System.Data.SqlClient.SqlParameter to a known managed provider native type.
Any guidance to fix this is much appreciated. Thanks.
Here you are adding the parameter as a value for a parameter:
var contactsParam =
new SqlParameter("#contacts", SqlDbType.Structured)
{
TypeName = "dbo.Contact",
Value = GetSqlDataRecordsContactsList(contacts)
};
command.Parameters.Add(new SqlParameter("#contacts", contactsParam)); //Problem is here
It should just be:
command.Parameters.Add(contactsParam);

How to get return values and output values from a stored procedure with EF Core?

ALTER PROCEDURE [dbo].[SearchMovies]
--#Year int = null,
#CategoryIds varchar(50) = null,
#Keywords nvarchar(4000) = null,
#PageIndex int = 1,
#PageSize int = 2147483644,
#TotalRecords int = null OUTPUT
As ...
EF Repository:
public class EFRepository<T> : IRepository<T> where T : BaseEntity
{
private readonly ApplicationDbContext _ctx;
private DbSet<T> entities;
string errorMessage = string.Empty;
public EFRepository(ApplicationDbContext context)
{
this._ctx = context;
entities = context.Set<T>();
}
...
public IQueryable<T> ExecuteStoredProcedureList(string commandText, params object[] parameters)
{
_ctx.Database.ExecuteSqlCommand(commandText, parameters);
return entities.FromSql(commandText, parameters);
}
}
I call this like:
var pCategoryIds = new SqlParameter()
{
ParameterName = "#CategoryIds",
Value = commaSeparatedCategoryIds,
DbType = DbType.String
};
var pKeywords = new SqlParameter()
{
ParameterName = "#Keywords",
DbType = DbType.String,
Value = name
};
var pPageIndex = new SqlParameter()
{
ParameterName = "#PageIndex",
DbType = DbType.Int32,
Value = pageIndex
};
var pPageSize = new SqlParameter()
{
ParameterName = "#PageSize",
DbType = DbType.Int32,
Value = pageSize
};
var pTotalRecords = new SqlParameter();
pTotalRecords.ParameterName = "#TotalRecords";
pTotalRecords.Direction = ParameterDirection.Output;
pTotalRecords.DbType = DbType.Int32;
var query1 = _ctx.Database.ExecuteSqlCommand("dbo.[SearchMovies] " +
"#CategoryIds, #Keywords, #PageIndex, #PageSize, #TotalRecords OUTPUT",
pCategoryIds, pKeywords, pPageIndex, pPageSize, pTotalRecords);
var query2 = _ctx.Set<MovieItem>.FromSql("dbo.[SearchMovies] " +
"#CategoryIds, #Keywords, #PageIndex, #PageSize, #TotalRecords OUTPUT",
pCategoryIds, pKeywords, pPageIndex, pPageSize, pTotalRecords);
query1 does get the output pTotalRecords fine, but no return values, and the second query2 gets the return values but no output parameter.
In EF 6, we used to have SqlQuery to do both actions in one command, how can I do the same in EF core ?
UPDATED:
Temporarily, I run 2 query, one to get the output param and one for result set.
public IQueryable<T> ExecuteStoredProcedureList(string commandText, params object[] parameters)
{
_ctx.Database.ExecuteSqlCommand(commandText, parameters);
return entities.FromSql(commandText, parameters);
}
I did something like this :-
-- My stored procedure:
CREATE PROCEDURE p1
AS
BEGIN
RETURN 29
END
GO
C# code
SqlParameter[] #params =
{
new SqlParameter("#returnVal", SqlDbType.Int) {Direction = ParameterDirection.Output}
};
_stagingContext.Database.ExecuteSqlCommand("exec #returnVal=" + storedProcName, #params);
var result = #params[0].Value; //result is 29
Hope this helps
I am working to convert some EF6 code to EF Core, and ran into this same issue. In Microsoft.EntityFrameworkCore version 2.1.0 the following use of FromSql() does return a result set and set the output parameter. You have to use .ToList() to force the proc to execute immediately, thereby returning both the results and the output param.
This is a sample from my DbContext class:
private DbQuery<ExampleStoredProc_Result> ExampleStoredProc_Results { get; set; }
public IEnumerable<ExampleStoredProc_Result> RunExampleStoredProc(int firstId, out bool outputBit)
{
var outputBitParameter = new SqlParameter
{
ParameterName = "outputBit",
SqlDbType = SqlDbType.Bit,
Direction = ParameterDirection.Output
};
SqlParameter[] parameters =
{
new SqlParameter("firstId", firstId),
outputBitParameter
};
// Need to do a ToList to get the query to execute immediately
// so that we can get both the results and the output parameter.
string sqlQuery = "Exec [ExampleStoredProc] #firstId, #outputBit OUTPUT";
var results = ExampleStoredProc_Results.FromSql(sqlQuery, parameters).ToList();
outputBit = outputBitParameter.Value as bool? ?? default(bool);
return results;
}
The stored proc returns the following entity:
public class ExampleStoredProc_Result
{
public int ID { get; set; }
public string Name { get; set; }
}
Here's the stored proc:
CREATE PROCEDURE ExampleStoredProc
#firstId int = 0,
#outputBit BIT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET #outputBit = 1;
SELECT #firstId AS [ID], 'first' AS [NAME]
UNION ALL
SELECT #firstId + 1 AS [ID], 'second' AS [NAME]
END
GO
Hopefully anyone that comes across this post in the future finds this useful.
map into context as keyless entity
modelBuilder
.Entity<yourentity>(
eb =>
{
eb.HasNoKey();
eb.ToView("procname");
});
add a dbset
public DbSet<yourentity> yourentity { get; set; }
exclude it from migrations
modelBuilder.Entity<yourentity>().ToTable("procname", t => t.ExcludeFromMigrations());
then in context can execute it into the mapped type
var values = context.yourentity.FromSqlRaw("EXECUTE dbo.procname");
Late response, but may be useful for someone:
My stored procedure:
create procedure GetSomething(#inCode varchar(5), #outValue int OUTPUT)
as
Set #outValue = 1
end
In C#,
SqlParameter[] parameters =
{
new SqlParameter("#inCode", SqlDbType.Varchar { Direction = ParameterDirection.Input, Value = "InputValue" }),
new SqlParameter("#outValue", SqlDbType.Int { Direction = ParameterDirection.Output })
}
dbContext.Database.ExecuteSqlRaw("exec GetSomething #inCode, #outValue OUTPUT", parameters);
var result = parameters[1].Value;
The keyword OUTPUT is important while calling from .net
var results = ExampleStoredProc_Results.FromSql(sqlQuery, parameters).ToList();
DbSet.FromSql Enables you to pass in a SQL command to be executed against the database to return instances of the type represented by the DbSet.
This answer will done the trick for me.
While trying to call a stored procedure with one input and one output parameter using ExecuteSqlCommandAsync EF Core. I was avoiding using FromSql on the entity object. Below code worked great from me so just wanted to share on this old post.
SqlParameter Param1 = new SqlParameter();
Param1 .ParameterName = "#Param1";
Param1 .SqlDbType = SqlDbType.VarChar;
Param1 .Direction = ParameterDirection.Input;
Param1 .Size = 50;
Param1 .Value = "ParamValue";
SqlParameter Param2 = new SqlParameter();
Param2 .ParameterName = "#Param2";
Param2 .SqlDbType = SqlDbType.VarChar;
Param2 .Size = 2048;
Param2 .Direction = ParameterDirection.Output;
var sprocResponse = await _dbContext.Database.ExecuteSqlCommandAsync("[dbo].[StoredProcName] #Param1= #Param1, #Param2 = #Param2 OUTPUT", new object[] { Param1, Param2});
string outPutVal= urlParam.Value.ToString();

Stored procedure's output parameters not updating

I have created a linq2sql project in which I have an extension method for calling SPs.
This extension method has few features-
It can call SP with Table Valued Parameters.
It can accepts both input and output parameters
It will handle multiple result set
Extension Method -
public partial class TestLinq2SqlDataContext : DataContext
{
public IMultipleResults ExceuteProcedure(string spName, IEnumerable<SqlParameter> parameters, SqlConnection sqlConnection, out SqlDataReader reader)
{
reader = null;
try
{
sqlConnection.Open();
var cmd = new SqlCommand
{
Connection = sqlConnection,
CommandText = spName,
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddRange(parameters.ToArray());
reader = cmd.ExecuteReader();
return Translate(reader);
}
catch (Exception)
{
}
return null;
}
}
I am calling below SP -
CREATE PROCEDURE USP_OutPutParameterCheck(
#Id int OUTPUT,
#Name nvarchar(50) OUTPUT)
AS
BEGIN
SET #Id = 12 SET #Name = N'NameSet for OutputParameter'
END
My C# code is
public static void Main(){
context = new TestLinq2SqlDataContext();
#region USP_OutPutParameterCheck
var connection1 = context.Connection as SqlConnection;
SqlDataReader dataReader1;
var outParam1 = new SqlParameter
{
Direction = ParameterDirection.Output,
Value = "Abc",
DbType = DbType.String,
ParameterName = "#Name"
};
var outParam2 = new SqlParameter
{
Direction = ParameterDirection.Output,
Value = 1,
DbType = DbType.Int32,
ParameterName = "#Id"
};
var parameters11 = new[]
{
outParam1,
outParam2
};
var data21 = context.ExceuteProcedure("USP_OutPutParameterCheck", parameters11, connection1, out dataReader1);
}
Now When I check the values of output parameters in debug mode I am getting the #Id's value perfect
but for #Name parameter I'm only getting 'N' value instead of 'NameSet for OutputParameter'
Can anyone help me out where I am going wrong in this?
Thanks
UPDATE :
Adding Screenshot when seeing the values of parameters in debug mode -
I think You must specifcy the Size of the outParam1.
See: https://msdn.microsoft.com/en-us/library/system.data.common.dbparameter.size(v=vs.110).aspx
For bidirectional and output parameters, and return values, you must
set the value of Size. This is not required for input parameters, and
if not explicitly set, the value is inferred from the actual size of
the specified parameter when a parameterized statement is executed.

Why doesn't my C# code get a return value from my Oracle function?

I am trying to check if a user is in my database, my return value from my function is either a 't' or an 'f'. as below:
CREATE OR REPLACE FUNCTION LOGIN
(p_Naam in varchar2
,p_Wachtwoord in varchar2)
return varchar2
is
v_count number;
BEGIN
select count(*) into v_count
from Lid
where Naam = p_Naam
and Wachtwoord = p_Wachtwoord;
if v_count > 0 then
return 't';
end if;
return 'f';
END LOGIN;
now i call this function with my C# code as below:
public bool LogIn(string gebruikersnaam, string wachtwoord)
{
string s;
var cmd = new OracleCommand
{
Connection = conn,
CommandText = "Login",
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("p_Naam", OracleDbType.Varchar2).Value = gebruikersnaam;
cmd.Parameters.Add("p_Wachtwoord", OracleDbType.Varchar2).Value = wachtwoord;
cmd.Parameters.Add("return_value", OracleDbType.Varchar2, ParameterDirection.ReturnValue);
try
{
conn.Open();
cmd.ExecuteNonQuery();
s = cmd.Parameters["return_value"].Value.ToString();
}
finally
{
conn.Close();
}
return s == "t";
}
when i try this funcion within my oracle developer i get an output.
only in my C# code, s always comes out as ""
in my sql developer the following gives me 't'
BEGIN
dbms_output.put_line(LOGIN('Willem Koonings', 'willem'));
END;
I haven't been working with OracleCommands for a while, but i would suggest this change:
CommandText = "Login(:p_Naam, :p_Wachtwoord)";
The long solution which I bets works (change type to text though):
CommandText = "select Login(:p_Naam, :p_Wachtwoord) return_value from dual";
When you have this in your code:
s = cmd.Parameters["return_value"].Value.ToString();
It implies, to me, that your command had somethink akin to this:
insert into test (user_name, create_date)
values (:USERNAME, sysdate)
returning user_id into :return_value
I've never seen a stored procedure use a "returning" as such. That doesn't automatically mean it can't be done, but it doesn't seem consistent with how I've used it and seen it used.
Since you are running a stored proc that returns a scalar value, this would probably suit you better:
string result = (string)cmd.ExecuteScalar();
You need to place the "return value" parameter first in the index, as far as i can tell from working with Oracle it doesn't pass the names of the parameters to the Oracle call, it is absolute placing.
Hope this solves your problems :)
Dom
ParameterDirection.ReturnValue could return only a numeric value, not a string or single char.
(At least in Sql Server). Try to change your function to return an integer and your parameters collection to receive an integer. (I don't have Oracle to test, please correct me if there are some syntax errors)
See this reference (for Sql Server)
CREATE OR REPLACE FUNCTION LOGIN
(p_Naam in varchar2
,p_Wachtwoord in varchar2)
return INTEGER
is
v_count number;
BEGIN
select count(*) into v_count
from Lid
where Naam = p_Naam
and Wachtwoord = p_Wachtwoord;
if v_count > 0 then
return 1;
end if;
return 0;
END LOGIN;
....
cmd.Parameters.Add("p_Naam", OracleDbType.Varchar2).Value = gebruikersnaam;
cmd.Parameters.Add("p_Wachtwoord", OracleDbType.Varchar2).Value = wachtwoord;
cmd.Parameters.Add("return_value", OracleDbType.Int32, ParameterDirection.ReturnValue);
....
int result = Convert.ToInt32(cmd.Parameters["return_value"].Value);
if(result == 0)
// fail
else
// success...
Instead of 'Return' statement why don't you declare a out parameter .Let me know if iam not clear
Just found out this and tested with VS2017 Community edition + 11g.
OracleCommand chkCmd = null;
try
{
chkCmd = new OracleCommand();
chkCmd.CommandText = "login";
chkCmd.CommandType = CommandType.StoredProcedure;
chkCmd.Connection = conn;
OracleParameter mobParam1 = new OracleParameter("p_Naam", OracleDbType.Varchar2, 2000);
mobParam1.Direction = ParameterDirection.Input;
mobParam1.Value = gebruikersnaam;
OracleParameter mobParam2 = new OracleParameter("p_Wachtwoord", OracleDbType.Varchar2, 2000);
mobParam2.Direction = ParameterDirection.Input;
mobParam2.Value = wachtwoord;
OracleParameter retValue = new OracleParameter("returnVal", OracleDbType.Varchar2, 2000);
retValue.Direction = ParameterDirection.ReturnValue;
chkCmd.Parameters.Clear();
chkCmd.Parameters.Add(retValue);
chkCmd.Parameters.Add(mobParam1);
chkCmd.Parameters.Add(mobParam2);
con.Open();
chkCmd.ExecuteScalar();
string retmsg = Convert.ToString(retValue.Value);
return retmsg=="t";
}
finally
{
con.Close();
chkCmd.Dispose();
con.Dispose();
}
Make Return_Value your first parameter:
cmd.Parameters.Add("Return_Value", OracleDbType.Boolean, ParameterDirection.ReturnValue);

Calling one stored procedure from another in C#

I have error 'Error converting data type nvarchar to decimal'
when call stored procedure call another stored procedure from C# as
cmd = new SqlCommand("tax_Base_emp", con);
cmd.CommandType = CommandType.StoredProcedure;
parm1 = new SqlParameter("#emp_code", SqlDbType.BigInt);
parm1.Value = emp_code;
parm1.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm1);
parm2 = new SqlParameter("#co_id", SqlDbType.BigInt);
parm2.Value = Settings.Default.comp_id;
parm2.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm2);
parm3 = new SqlParameter("#d", SqlDbType.DateTime);
parm3.Value = Convert.ToDateTime("31/1/2010");
parm3.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm3);
parm4 = new SqlParameter("#y", SqlDbType.Int);
parm4.Value =int.Parse(textBox2.Text);
parm4.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm4);
parm5 = new SqlParameter("#check_month", SqlDbType.Int);
parm5.Value =1;
parm5.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm5);
parm6 = new SqlParameter("#month", SqlDbType.Int);
parm6.Value =8;
parm6.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm6);
SqlParameter parm7 = new SqlParameter("#indate", SqlDbType.DateTime);
parm7.Value = Convert.ToDateTime("8/5/2010");
parm7.Direction = ParameterDirection.Input;
cmd.Parameters.Add(parm7);
SqlParameter parm8 = new SqlParameter("#Sumtotal", SqlDbType.Decimal);
parm8.Scale = 2;
parm8.Direction = ParameterDirection.Output;
cmd.Parameters.Add(parm8);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
decimal tax_value = Convert.ToDecimal(cmd.Parameters["#Sumtotal"].Value);
And the stored proecdure called:
ALTER PROCEDURE Tax_Base_emp
#emp_code bigint,
#co_id bigint,
#d datetime,
#y int,
#check_month int,
#month int,
#indate datetime,
#Sumtotal decimal(8,2) output
AS
declare #tax_main_sal decimal(8,2)
declare #tax_var_sal decimal(8,2)
declare #salary decimal(8,2)
declare #insh_varsalary decimal(8,2)
declare #insh_value decimal(8,2)
declare #vacation_value decimal(8,2)
declare #vacation_varsalary decimal(8,2)
declare #ded_value decimal(8,2)
declare #ben_value decimal(8,2)
exec Taxable_mainsalary #emp_code,#co_id,#tax_main_sal output
exec taxable_varsalary #emp_code,#co_id, #tax_var_sal output
----taxableSalary---------------
set #salary=#tax_main_sal+#tax_var_sal
----insurance-------------------
exec varsalary_insh #emp_code,#co_id,#d,#y, #insh_varsalary output
exec insh_no #emp_code,#co_id,#insh_varsalary,#check_month, #insh_value output
----vacation--------------------
exec vacation_varsalary #emp_code,#co_id,#vacation_varsalary output
exec vacation_value #emp_code,#co_id,#y,#month,#vacation_varsalary,output
---------deduction---------------
exec deduction_for_tax #emp_code,#co_id,#indate,#ded_value output
-------------benifit------------
exec benfit_for_tax #emp_code,#co_id,#indate,#ben_value output
-----------------------------------NetSalary--------------------------------------------------------
set #Sumtotal=(isnull(#salary,0)+isnull(#ben_value,0))-(isnull(#insh_value,0)+isnull(#vacation_value,0)+isnull(#ded_value,0))
return
I don't see anything wrong with your C# code - it's really hard to tell what would be causing the problem. Your C# code is just simply calling a single stored proc - that shouldn't be a problem, really.
However, I do have a few recommendations for your coding style:
put your SqlConnection and SqlCommand into using(....) { .... } blocks to make your code more reliable
try to avoid specifying default property values, like Direction = ParameterDirection.Input; over and over again; the .Input is the default - only specify it when you deviate from that default
if you do the same steps over and over and over again - why don't you put this in a method and call that method a couple of times?? This also saves you from having to create a gazillion of SqlParameter objects that you then just throw away .....
You'd end up with something like:
public void CallStoredProc()
{
using(SqlConnection con = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand("tax_Base_emp", con))
{
cmd.CommandType = CommandType.StoredProcedure;
AddParameter(cmd.Parameters, "#emp_code", SqlDbType.BigInt, emp_code);
AddParameter(cmd.Parameters, "#co_id", SqlDbType.BigInt, comp_id);
AddParameter(cmd.Parameters, "#d", SqlDbType.DateTime, Convert.ToDateTime("31/1/2010"));
AddParameter(cmd.Parameters, "#y", SqlDbType.Int, int.Parse(textBox2.Text));
AddParameter(cmd.Parameters, "#check_month", SqlDbType.Int, 1);
AddParameter(cmd.Parameters, "#month", SqlDbType.Int, 8);
AddParameter(cmd.Parameters, "#indate", SqlDbType.DateTime, Convert.ToDateTime("8/5/2010"));
AddOutputParameter(cmd.Parameters, "#Sumtotal", SqlDbType.Decimal, 8, 2);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
decimal tax_value = Convert.ToDecimal(cmd.Parameters["#Sumtotal"].Value);
}
}
public void AddParameter(SqlParameterCollection params, string name, SqlDbType type, object value)
{
SqlParameter tmpParam = new SqlParameter(name, type);
tmpParam.Value = value;
params.Add(tmpParam);
}
public void AddOutputParameter(SqlParameterCollection params, string name, SqlDbType type, int precision, int scale)
{
SqlParameter tmpParam = new SqlParameter(name, type);
tmpParam.ParameterDirection = Direction.Output;
tmpParam.Precision = precision;
tmpParam.Scale = scale;
params.Add(tmpParam);
}
I can't see anything in the code you have posted that would cause that.
I suspect that the error is in one of the 8 stored procedures you are calling where you are assigning an nvarchar to a decimal.
I'd comment these all out temporarily and just return a dummy number if that fixes it uncomment half and try again. If the error reoccurs then you know it is in one of the ones you just uncommented. i.e. do a binary search to find the offending procedure.

Categories

Resources