I have a stored procedure with an OUTPUT parameter:
CREATE PROCEDURE [dbo].[myStoredProcedure]
(#myValue NVARCHAR(100) OUTPUT)
AS
BEGIN
SET #myValue = (SELECT myValue FROM myTable WHERE something = 1)
SElECT #myValue;
END
My C# class:
public string getOutPut()
{
string shortFooter;
string sql = "myStoredProcedure";
string connStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection DBConn = new SqlConnection(connStr))
{
using (SqlCommand DBCmd = new SqlCommand(sql, DBConn))
{
try
{
DBConn.Open();
DBCmd.CommandType = CommandType.StoredProcedure;
SqlParameter newSqlParam = new SqlParameter();
newSqlParam.ParameterName = "#myValue";
newSqlParam.SqlDbType = SqlDbType.NVarChar;
newSqlParam.Direction = ParameterDirection.Output;
DBCmd.Parameters.Add(newSqlParam);
DBCmd.ExecuteNonQuery();
shortFooter = DBCmd.Parameters["#myValue"].Value.ToString();
}
catch (Exception ex)
{
string blah = ex.ToString();
shortFooter = "";
}
}
}
return shortFooter;
}
This is the exception that I'm getting:
System.InvalidOperationException: String[0]: the Size property has an
invalid size of 0.
What I don't understand is, I have used this code before, except that in the stored procedure I'm getting the value of the output with IDENTITY() when I create a new record in the database, so the parameter type in the class is INT. But in concept it should be the same, but I guess I'm wrong, just don't know why.
What am I missing?
Thanks!!
You need to specify the size required to hold the output value. From MSDN:
For output parameters with a variable length type (nvarchar, for example), the size of the parameter defines the size of the buffer holding the output parameter.
Set the Size of the output parameter in your code to match what's in the stored procedure:
newSqlParam.Size = 100;
The default value is 0, which is what's causing the exception.
Related
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.
I have a following stored procedure:
create or replace PROCEDURE PRODUCT_DETAILS(p_code IN VARCHAR2,
cursorParam OUT SYS_REFCURSOR)
IS
BEGIN
OPEN cursorParam FOR
select str_auth_code, str_name
from strs
where str_auth_code = p_code;
END;
How can I call it with OrmLite? I've tryied:
connection.SqlList<Product>(#"EXEC PRODUCT_DETAILS #p_code", new { p_code = code });
but it throws an exception ORA-01036: illegal variable name/number
I just tried to do it with plain old ADO.NET and it worked:
using (var conn = new OracleConnection(connectionString))
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "PRODUCT_DETAILS";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("p_code", OracleType.NVarChar).Value = redemptionCode;
cmd.Parameters.Add("cursorParam", OracleType.Cursor);
cmd.Parameters["cursorParam"].Direction = ParameterDirection.Output;
conn.Open();
OracleDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Console.WriteLine(dr["Name"]);
}
conn.Close();
}
But I can't figure out how to do the same task with OrmLite.
What you have looks good. If you were concerned about the verbosity of the code, and were using a number of stored procedures, then you could use this extension method to remove some of the repeated code:
Extension Method:
public static class StoredProcExtensions
{
public static List<T> ExecStoredProcedure<T>(this IDbConnection connection, string procedureName, object parameters = null, string outputCursor = "cursorParam")
{
return connection.Exec(c => {
c.CommandText = procedureName;
c.CommandType = CommandType.StoredProcedure;
// Create the parameters from the parameters object
if(parameters != null)
foreach(var property in parameters.GetType().GetPublicProperties())
c.Parameters.Add(new OracleParameter(property.Name, property.GetValue(parameters)));
// Add the output cursor
if(outputCursor != null)
c.Parameters.Add(new OracleParameter(outputCursor, OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
// Return the result list
return c.ExecuteReader().ConvertToList<T>();
});
}
}
Usage:
var download = connection.ExecStoredProcedure<ProductDownloads>(
"PRODUCT_DETAILS",
new { p_code = redemptionCode }
);
foreach (var productDownload in download)
{
Console.WriteLine(productDownload.Name);
}
So the arguments are:
Stored procedure name i.e. PRODUCT_DETAILS
Optional An object of input parameters i.e new { p_code = redemptionCode, other = "value" }
Optional The name of the output cursor - defaults to cursorParam
Note: this code is untested, because I don't have Oracle setup, but it does compile, and hopefully goes some way to simplifying your stored procedures.
So far ended up with following code:
using (var connection = factory.Open())
{
var download =
connection.Exec(c =>
{
c.CommandText = "PRODUCT_DETAILS";
c.CommandType = CommandType.StoredProcedure;
c.Parameters.Add(
new Oracle.DataAccess.Client.OracleParameter("p_code", Oracle.DataAccess.Client.OracleDbType.NVarchar2) { Value = redemptionCode });
c.Parameters.Add(
new Oracle.DataAccess.Client.OracleParameter("cursorParam", Oracle.DataAccess.Client.OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
return c.ExecuteReader().ConvertToList<ProductDownloads>();
});
foreach (var productDownload in download)
{
Console.WriteLine(productDownload.Name);
}
}
But I think there should be a better way for doing this.
Late to the party, but this problem can be solved by simply adding BEGIN and END to the statement... it will work.
In my case, I was trying to refresh a materialized view, only after adding BEGIN and END will it work, otherwise it will throw OracleException ORA-00900: invalid SQL statement...
This should work:
db.ExecuteSql("BEGIN DBMS_SNAPSHOT.REFRESH('" + materializedViewName + "'); END;");
Here is my code in C#:
float r_discountValue = 0;
SqlConnection con = Constant.GetConnection();
SqlCommand cmd = new SqlCommand("Coupon_GetDiscountFromValidCouponCode", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#PKCouponCode", SqlDbType.VarChar);
cmd.Parameters["#PKCouponCode"].Value = "DIS_77";
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if(reader.Read()){
r_discountValue = float.Parse(reader[0].ToString());
}
reader.Close();
}
catch(Exception exception)
{
throw exception;
}
finally{
con.Close();
}
return r_discountValue;
The stored procedure:
ALTER PROCEDURE [dbo].[Coupon_GetDiscountFromValidCouponCode]
#PKCouponCode varchar(50)
AS
SELECT *
FROM Coupon
WHERE CouponCode = #PKCouponCode AND Valid = 1
Here is how the DB looks like:
I encounter an error
Input string was not in a correct format
I don't know what's thing is going wrong, any ideas?
If you want the discount value, then you should return only the discount from the SP (since it is named GetDiscountfrom...)
SELECT CouponDiscount FROM Coupon WHERE CouponCode = #PKCouponCode AND Valid = 1
This will make it a one-column resultset, which matches the access reader[0] from C#.
The other option is of course to change the C# side to read the second item (index 1) or reference the column by name, e.g.
r_discountValue = float.Parse(reader[1].ToString());
r_discountValue = float.Parse(reader["CouponDiscount"].ToString());
You would have got Input string was not in a correct format. because it was reading "DIS_77" which float.parse cannot process.
You are using first column i.e.CouponCode for fetching discount. instead of that you need to use second column ie. couponDiscount
So try something like this
r_discountValue = float.Parse(reader["CouponDiscount"].ToString());
I'm using visual studios 2010 to create a c# web application with a database. My goal is to have default.aspx call a c# class which runs a stored procedure that selects an entry from the table and returns it. Here's the code:
'The stored procedure. I want it to send back the name it gets from doing
'the query to the c# class.
ALTER PROCEDURE getName (#id int)
AS
BEGIN
SET NOCOUNT ON;
--
SELECT name FROM tableA where id = #id;
END
Return
//Here's the c# class I'm using.
public class student
{
public string name;
public int id;
public student()
{ }
public String doQuery(int id)
{
SqlConnection conn = null;
try
{
conn = new SqlConnection("Server =(local); Database = Database1.mdf;
Integrated Security = SSPI");
conn.Open();
SqlCommand cmd = new SqlCommand("getName", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#id", SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.Value = id;
cmd.Parameters.Add(param);
//This is some code from when I tryed return value
//SqlParameter reVal = cmd.Parameters.Add("#name", SqlDbType.VarChar);
//reVal.Direction = ParameterDirection.ReturnValue;
//before using ExecuteScalar I tried ExcuteNonQuery with the commented
//out code
name = (string)cmd.ExecuteScalar();
//name = (String)cmd.Parameters["#name"].Value;
conn.Close();
}
catch(Exception)
{}
return name;
}
}
Running my program does not return errors it simply doesn't place any value in name. What am I missing to get the name that is selected in the sql procedure into the name variable in my c# class. I hope I'm conveying my problem clearly.
edit1:I didn't put anything in the catch cause hadn't decided what to use to see that it had errored out. I changed it to make name = "error" when it fails the try and that's what I get and that's what I get.
I also tried running "exec getName 5, otherstuff"in sql server management. I'm a little unclear about what to use as the second parameter when running exec getName since the second parameter is suppose to be just output but still seems to be required to run it. It just says the commands are executed successfully but doesn't display the name that goes with id 5
I would recommend using the async/await pattern for SQL statements. Fortunately, it doesn't require much refactoring.
See if this works for you:
public async Task<string> QueryGetNameAsync(int id)
{
using (var dbConn = new SqlConnection("..."))
using (var command = new SqlCommand("getName", dbConn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#id", id);
await dbConn.OpenAsync();
var result = await command.ExecuteScalarAsync();
dbConn.Close();
var name = result as string;
return name;
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
You'd call it with something like:
private async void DoLookup_Clicked(object sender, EventArgs e)
{
var id = int.Parse(idText.Text);
var name = await QueryGetNameAsync(id);
}
Alternatively, can use OUTPUT parameters in SQL but you would have to adjust your stored procedure to something like this:
ALTER PROCEDURE getName
(
#id int,
#name varchar(100) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
SELECT #name = name FROM tableA where id = #id;
END
Then your C# function would be something like:
public async Task<string> QueryGetNameAsync(int id)
{
using (var dbConn = new SqlConnection("..."))
using (var command = new SqlCommand("getName", dbConn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#id", id);
command.Parameters.Add("#name", SqlDbType.VarChar, 100);
command.Parameters["#name"].Direction = ParameterDirection.Output;
await dbConn.OpenAsync();
await command.ExecuteNonQueryAsync();
dbConn.Close();
var name = command.Parameters["#name"].Value as string;
return name;
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
The problem is in your connection string: unless you have strange naming conventions, you are specifying the database file name and not the name of the database itself.
Try changing this part of the connection string: Database = Database1.mdf; to Database = Database1;.
If you are confused about what is or is not valid in the connection string, you can always use the SqlConnectionStringBuilder which will create the appropriate connection string for you after you have set the correct properties.
You can also use the list of properties specified in the SqlConnection.ConnectionString documentation as a reference that contains examples.
Finally, I strongly recommend the following best practices:
1) Use using blocks with the connection and commands to ensure they are properly closed and disposed.
2) Do not assign name directly to the result of ExecuteScalar in case it is return as DBNull.Value
3) Never ignore exceptions unless you have documented why you are doing so in the code.
Here is a quick rewrite with all of the above recommendations:
try
{
using (var conn = new SqlConnection("Server =(local); Database = Database1; Integrated Security = SSPI"))
{
conn.Open();
using (var cmd = new SqlCommand("getName", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter("#id", SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.Value = id;
cmd.Parameters.Add(param);
var oResult = cmd.ExecuteScalar();
if ((oResult != null) && (oResult != DBNull.Value))
{
name = (string)oResult;
}
}
conn.Close();
}
}
catch (Exception)
{
// Do something with the exception here, don't just ignore it
}
For the past few hours I am trying to do the simplest of the simple things (at least for SQL SERVER) in an Oracle Data Base, through a .NET application using ADO.NET. It seems impossible.
For SQL SERVER I would do this simple task, supposing I have an SqlCommand object
comm.CommandText = #"
DECLARE #next_id INT
SET #next_id = (SELECT ISNULL(MAX(id_col),0) FROM TABLE_1) + 1
INSERT INTO TABLE_1 (id_col, col1, ...) VALUES (#next_id, val1, ...)
SELECT #next_id";
int id = Convert.ToInt32(comm.ExecuteScalar());
That would insert a new record to table TABLE_1 and I would take back the new id in the "id" variable in c# code.
Four simple steps
Declare a variable
Set it to the next available id
Insert the record with the new variable
Return the variable's value
Ok I managed to declare the variable in Oracle query. Also I (think) I managed to give it a value (With SELECT INTO)
How can I get back this variable's value back in c#? How can i SELECT a variable's value to the output stream in Oracle SQL?
I know that there are better ways to achieve getting back an identity column, but that's not the question here. It could be a totally different example. The question is simple.: I have declared a variable inside an oracle sql script that will be executed from within .net app. How can i get the variable's value back to c#, from an oracle query? What is the above code's equivalent with Oracle ADO.NET query?
You'll want to use ODP.NET (Oracle's Oracle Data Access Components):
An example of this is below. Note that in ODP.NET, you can establish a parameters direction (input, inputoutput, output, returnvalue) to correspond with the parameters of the procedure or statement you're running. In this example, I'm grabbing a returnvalue, which is an ID that is generated by the db via a sequence and trigger (its created automagically as far as the .NET app is concerned):
int event_id = 0;
using (OracleConnection oraConn = new OracleConnection(connStr))
{
string cmdText = #"insert into EVENT
(EVENT_NAME, EVENT_DESC)
values
(:EVENT_NAME, :EVENT_DESC)
RETURNING EVENT_ID INTO :EVENT_ID
";
using (OracleCommand cmd = new OracleCommand(cmdText, oraConn))
{
oraConn.Open();
OracleTransaction trans = oraConn.BeginTransaction();
try
{
OracleParameter prm = new OracleParameter();
cmd.BindByName = true;
prm = new OracleParameter("EVENT_NAME", OracleDbType.Varchar2);
prm.Value = "SOME NAME"; cmd.Parameters.Add(prm);
prm = new OracleParameter("EVENT_DESC", OracleDbType.Varchar2);
prm.Value = "SOME DESC"; cmd.Parameters.Add(prm);
prm = new OracleParameter( "EVENT_ID"
, OracleDbType.Int32
, ParameterDirection.ReturnValue);
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
// return value
event_id = ConvertFromDB<int>(cmd.Parameters["EVENT_ID"].Value);
}
catch
{
trans.Rollback();
throw;
}
finally
{
trans.Dispose();
}
oraConn.Close();
}
}
The ConvertFromDB is just a generic to cast the return value to its .NET equivalent (an int in this case).
Hope that helps.
EDIT:
You can easily bind an array of values (and retrieve an array of return values) in ODP.NET:
using (OracleConnection oraConn = new OracleConnection(connStr))
{
string cmdText = #"insert into TEST_EVENT
(EVENT_NAME, EVENT_DESC)
values
(:EVENT_NAME, :EVENT_DESC)
RETURNING EVENT_ID INTO :EVENT_ID
";
using (OracleCommand cmd = new OracleCommand(cmdText, oraConn))
{
oraConn.Open();
OracleTransaction trans = oraConn.BeginTransaction();
try
{
string[] event_names = new string[2];
string[] event_descs = new string[2];
int[] event_ids = new int[2];
event_names[0] = "Event1";
event_descs[0] = "Desc1";
event_names[1] = "Event2";
event_descs[1] = "Desc2";
OracleParameter prm = new OracleParameter();
cmd.Parameters.Clear();
cmd.ArrayBindCount = 2;
cmd.BindByName = true;
prm = new OracleParameter("EVENT_NAME", OracleDbType.Varchar2);
prm.Value = event_names; cmd.Parameters.Add(prm);
prm = new OracleParameter("EVENT_DESC", OracleDbType.Varchar2);
prm.Value = event_descs; cmd.Parameters.Add(prm);
prm = new OracleParameter( "EVENT_ID"
, OracleDbType.Int32
, ParameterDirection.ReturnValue);
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
trans.Commit();
// get return values
event_ids = (int[])(cmd.Parameters["EVENT_ID"].Value);
}
catch
{
trans.Rollback();
throw;
}
finally
{
trans.Dispose();
}
oraConn.Close();
}
}