I am trying to pass parameters to my stored procedure. They are like this
#p_latitude='',#p_longitude='',#p_miles='',#p_searchtext='',#p_maptownid=182,#p_regionid=0
From code I am passing the parameters like
cmd.Parameters.Add("#p_latitude", SqlDbType.NVarChar).Value="''";
cmd.Parameters.Add("#p_longitude", SqlDbType.NVarChar).Value="''";
cmd.Parameters.AddWithValue("#p_miles",0);
cmd.Parameters.Add("#p_searchtext",SqlDbType.NVarChar).Value="''";
cmd.Parameters.AddWithValue("#p_maptownid",maptownid);
cmd.Parameters.AddWithValue("#p_regionid",0);
I'm getting an error cannot convert navarchar to float.
I tried in different ways sending null, string.empty. But couln't find it.
Empty strings should be passed as "" not "''". The apostrophes (') will be passed in as the string value and not as string delimiters.
The error message says you have a float column that you pass a nvarchar. I suspect that the #p_latitude and #p_longtitude params is the problem. Try to set the value directly as DBNull.Value.
I'm guessing that latitude, longitude and miles are floating point numbers. If so, you need to pass them as such:
cmd.Parameters.AddWithValue("#p_latitude", 0.0);
cmd.Parameters.AddWithValue("#p_longitude", 0.0);
cmd.Parameters.AddWithValue("#p_miles", 0.0);
Try using DbType.String instead of SqlDbType.NVarChar
Well actually, this is how I do it (they were all in different layers so I suggest you to do the same):
List<SqlParameter> parameters = new List<SqlParameter> { };
string status = "dummystatus";
string date = Datetime.Now;
parameters.Add(new SqlParameter("#Status", status));
parameters.Add(new SqlParameter("#date", date));
SqlCommand cmd = new SqlCommand();
command.Connection = connection;
//set the command text (stored procedure name or SQL statement)
command.CommandText = commandText;
command.CommandType = commandType;
foreach (SqlParameter p in parameters)
{
command.Parameters.Add(p);
}
SqlDataAdapter da = new SqlDataAdapter(command);
DataSet ds = new DataSet();
//fill the DataSet using default values for DataTable names, etc.
da.Fill(ds);
// detach the SqlParameters from the command object, so they can be used again.
cmd.Parameters.Clear();
Related
Scenario
I'm working with SQL Server 2017 (not possible to change)
I'm using Visual Studio 2019 in C# console and .NET Framework 4.5 (possible to change)
I'm using ADO.NET because several years before we couldn't use Entity Framework, as the system is made to work with a stored procedure that returns at least 100k rows (possible to change)
Situation
I have an USP that returns a table that is at least 100k of rows by 20 fields. I need to add an output parameter in order to get also an ID created by the USP itself. So, the situation is that I need return a table and an ID (called ProcMonitorId). I don't know if this is even so possible (See workarounds section)
At the SQL level is seems to be so far so good:
CREATE PROCEDURE [myschema].[mystore]
#ProcMonitorId BIGINT OUTPUT
AS
BEGIN
BEGIN TRANSACTION
(...)
SELECT fields FROM myTable
SELECT #ProcMonitorId = #internalVariable
SQL execution:
And at repository layer (only relevant lines, someone were surprised for health of example):
var command = new SqlCommand("myStoreProcedure", mycon);
command.CommandType = CommandType.StoredProcedure;
SqlParameter outPutParameter = new SqlParameter();
outPutParameter.ParameterName = "#ProcMonitorId";
outPutParameter.SqlDbType = System.Data.SqlDbType.BigInt;
outPutParameter.Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(outPutParameter);
// Open connection etc-etc that works
SqlDataAdapter da = new SqlDataAdapter(command);
DataTable dt = new DataTable();
string ProcMonitorId = outPutParameter.Value.ToString();
da.Fill(dt);
Everything worked fine until the addition of the output at C# level. It returns in the line:
string ProcMonitorId = outPutParameter.Value.ToString();
it returns NullReferenceException because Value is null (that can't be) and of course, can't convert to String. I would solve this situation by adding a ? but if that's situation happens for real, I need catch it any way as error. The main idea is that Value can not be null.
As I don't have any ORM map, (and my expertise is not ADO.NET but Entity Framework) I can't understand why is null (No, is not null at SQL layer, always return a value)
Question
How can I solve this error or how can I return a BIGINT parameter and ALSO a table result?
Workarounds
As I first glance I have to solve it quickly, I made a:
SELECT 1 as type, #procID as procid, null as data1, null as data2
UNION ALL
SELECT 2 as type, null as procid, data1, data2
in order to simulate a "header" and "data" rows on one single table.
But I don't like this solution and is not very elegant and flexible. I've to parse the header every time.
Thanks in advance and please comment anything, tip, help, workaround, I will be glade to update my answer if more information is needed.
Also I can make my Framework to .NET Core or change to Entity Framework. That I can't change is my SQL version
Update #2
No changes in SQL - Still working as screenshot
In C# - Hangs out for ever
SqlConnection connection = new SqlConnection(ConfigurationManager.AppSettings["DbConnection"]);
connection.Open();
var command = new SqlCommand("myUSP", connection);
command.CommandType = CommandType.StoredProcedure;
command.CommandTimeout = Convert.ToInt16(ConfigurationManager.AppSettings["DataBaseTimeOut"]);
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
SqlParameter r = command.Parameters.Add("#ProcMonitorId", SqlDbType.BigInt);
r.Direction = ParameterDirection.Output;
DataTable dt = new DataTable();
using (var rdr = command.ExecuteReader())
{
dt.Load(rdr);
long id = (long)r.Value;
}
SqlDataAdapter da = new SqlDataAdapter(command);
da.Fill(dt);
The parameter value won't be available until after you consume the resultset, eg
var cmd0 = new SqlCommand("create or alter procedure pFoo #id int output as begin select * from sys.objects; set #id = 12; end", con);
cmd0.ExecuteNonQuery();
var cmd = new SqlCommand("pFoo", con);
cmd.CommandType = CommandType.StoredProcedure;
var p1 = cmd.Parameters.Add("#id", SqlDbType.Int);
p1.Direction = ParameterDirection.Output;
var dt = new DataTable();
using (var rdr = cmd.ExecuteReader())
{
dt.Load(rdr);
var id = (int)p1.Value;
}
You should use a Parameter with the Direction property set to ReturnValue, and, inside the sp, declare an internal variable and set it to the value you want.
Then call the RETURN statement before leaving the StoredProcedure.
As an example, see this SP:
ALTER PROCEDURE [GetTimeZoneGMT]
#TimeZone NVARCHAR(128)
AS
BEGIN
DECLARE #timeZoneNumber as INT = -20;
IF #TimeZone ='Pacific/Midway'
SET #timeZoneNumber = -11
ELSE IF #TimeZone ='Pacific/Niue'
SET #timeZoneNumber = -11
ELSE IF #TimeZone ='Pacific/Pago_Pago'
SET #timeZoneNumber = -11
SELECT 1 -- or whatever you need to have as result set
RETURN #timeZoneNumber;
END
The stored procedure ends with a (bogus) SELECT statement but also has a RETURN statement with the parameter set inside the SP logic.
Now from the C# side you could call it in this way (LinqPad example)
using (var connection = new SqlConnection("Data Source=(LOCAL);Initial Catalog=LinqPADTest;Integrated Security=True;"))
{
connection.Open();
SqlCommand cmd = new SqlCommand("GetTimeZoneGMT", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#TimeZone", SqlDbType.NVarChar).Value = "Asia/Kuala_Lumpur";
SqlParameter r = cmd.Parameters.Add("#p2", SqlDbType.BigInt);
r.Direction = ParameterDirection.ReturnValue;
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
r.Value.Dump(); // Prints -20
dt.Dump(); // Prints a row with a single column with 1 as value
}
I am mostly new to c# so i am looking for some guidance here. I am testing a method that i need to pass a list of guids to and run a stored procedure that returns values based on the guids i pass to it which i can then print to the console. I can get the method to work when i pass only one guid but when i pass a list of guids it seems to not work.
I feel like i am lacking some understanding here around how i should pass the list of guids and return it. I get conversion errors trying to return List.
Here is how far i have got but i feel like i am stuck now and cant progress anymore from any info i have found online.
class Program
{
static void Main(string[] args)
{
List<Guid> tempguid = new List<Guid>();
tempguid.Add(Guid.Parse("472USFA0-B705-9A73-ABD4-3B1870AF1409"));
tempguid.Add(Guid.Parse("FA97E6BB-0875-5UB9-967A-87ECC396F9F0"));
GetValue(tempguid);
Console.WriteLine(GetValue);
}
public void GetValue(List<Guid> tempguid)
{
using (SqlConnection conn = new SqlConnection("connection string here"))
{
conn.Open();
SqlCommand cmd = new SqlCommand("stored procedure here", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#id", tempguid));
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((string)rdr["value"]);
}
Console.ReadLine();
}
}
}
}
Should i be passing the list like this GetValue(List tempguid)?
EDIT
ok so if i use a TVP.
Something like:
CREATE TYPE [dbo].[Identity] AS TABLE(
[Id] [uniqueidentifier] NOT NULL
)
GO
Then my procedure will look something along the lines of:
CREATE OR ALTER PROCEDURE [dbo].[procedure_name]
#id dbo.Identity READONLY
as
SELECT t.[id]
,t.[value]
FROM [dbo].[table1] t
Inner Join #id i on i.Id = t.id
How do i use this TVP in c# for my stored procedure?
you need a foreach loop on GUID list. Try like:
foreach (var g in tempguid)
{
cmd.Parameters.Add(new SqlParameter("#id", g));
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((string)rdr["value"]);
}
Console.ReadLine();
}
cmd.Parameteres.Clear();
}
You can't pass a list to sp. You need to convert your guids into a csv string example:
var param = string.Join(", ", tempguid);
Then
cmd.Parameters.Add(new SqlParameter("#id", param));
Then after receiving your parameter on the sp pass into String_Split. Goodluck!
Passing values to TVPs from ADO .NET is very straightforward, and requires very little extra code compared to passing data to regular parameters.
For the data type you specify SqlDbType.Structured.
You specify the name of the table type in the TypeName property of the parameter.
You set the Value property of the parameter to something suitable.
As mentioned in the above link System.Data.SqlClient supports populating table-valued parameters from DataTable, DbDataReader or IEnumerable<T> \ SqlDataRecord objects.
If you already have the list of Guids coming from other sources in your case tempguid then you could use a datatable to pass the details to stored procedure.
DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("Id", typeof(Guid)));
// populate DataTable from your List here
foreach (var id in tempguid)
tvp.Rows.Add(id);
And change the ADO.NET code like below -
conn.Open();
SqlCommand cmd = new SqlCommand("dbo.tvpProcedure", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#id";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "dbo.testTVP";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((string)rdr["value"]);
}
Console.ReadLine();
}
Side notes:
Looks like the GUIDs you have shown in the code seems invalid as they contain non-
hexadecimal values.
472USFA0-B705-9A73-ABD4-3B1870AF1409
^^
FA97E6BB-0875-5UB9-967A-87ECC396F9F0
^
Change the Type name something meaningful instead of dbo.Identity in my case I used
dbo.testTVP
Further information -
http://www.sommarskog.se/arrays-in-sql-2008.html
I want to change SQL statement in DataSet for adding where query
where LogTime between #parameter1 and #parameter2
how can I pass parameter DateStr and DateEnd to #parameter1 and #parameter2
from c# method
DataSet1 ds = new DataSet1();
I guess We should Using DataTable1TableAdapter from DataSet1
Is any suggested way to Implement it ?
We got these query in DataTable1TableAdapter already
SELECT IPassLog.ParkID, ParkInfo.ParkName, IPassLog.UserID, IPassLog.LogTime, IPassLog.LogInTime, IPassLog.InitBalance,
IPassLog.TranAmount, IPassLog.TranBalance, IPassLog.CheckDone, IPassLog.TranSN, IPassLog.PackName, IPassLog.ResultName, IPassLog.SettlementDate,
IPass_Result_Hex.Result_Desc, IPass_ErrorCode.Explanation
FROM IPassLog INNER JOIN ParkInfo ON IPassLog.ParkID = ParkInfo.ParkID
FULL JOIN IPass_Result_Hex ON IPassLog.VerifyResult = IPass_Result_Hex.Result_Hex FULL JOIN IPass_ErrorCode ON IPassLog.VerifyFlag = IPass_ErrorCode.Code;
and I want add where statement into these query
You can make use of the SqlDataAdapter class's constructor that accept SqlCommand as parameter. So that you can pass the parameterized command to this class to create the instance of SqlDataAdapter
I prefer you to use like thi:
string connectionString = "your connection string here";
string query = "select blah, foo from mytable where LogTime >= #parameter1 and LogTime<= #parameter2";
DataSet ds = new DataSet();
using (SqlCommand cmd = new SqlCommand(query, new SqlConnection(connectionString)))
{
cmd.Parameters.Add("parameter1", SqlDbType.DateTime).Value = dateInput1;
cmd.Parameters.Add("parameter2", SqlDbType.DateTime).Value = dateInput2;
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd)) {
adapter.Fill(ds);
}
}
follow the post : Tutorial 1: Creating a Data Access Layer which talks about how to deal with parameter in table adapter query. it also provide code example for the same.
if you can to pass parameter to your query then create parameter like this
SqlParameter param= new SqlParameter();
param.ParameterName = "#parameter1";// Defining Name
param.SqlDbType = SqlDbType.Int; // Defining DataType
param.Direction = ParameterDirection.Input; // Setting the direction
param.Value = inputvalue;
and add created parameter to your command object
// Adding Parameter instances to sqlcommand
comm.Parameters.Add(param);
From C# Code, I'm trying to call a PACKAGE.PROCEDURE() from Oracle. In this simple example I should get one value from the procedure call, but all I get is error:
wrong number or types of arguments in call to 'RETURN_NUM'
The procedure is declared as follows:
PROCEDURE return_num(xNum OUT NUMBER) AS
BEGIN
xNum:= 50;
dbms_output.put_line('hello world ' || xNum);
END;
C# code:
Oraclecon.Open();
OleDbCommand myCMD = new OleDbCommand("TEST.return_num", Oraclecon);
myCMD.CommandType = CommandType.StoredProcedure;
myCMD.Parameters.Add("xNum", OleDbType.Numeric);
OleDbDataReader myReader;
myReader = myCMD.ExecuteReader();
Can some one please point out what I'm doing wrong. Then in a real scenario I would like to call a procedure that returns a set of values from a custom Type, such as:
TYPE r_interface_data IS RECORD
(
object_id VARCHAR2(16),
obj_type VARCHAR2(32)
);
TYPE t_interfase_data IS TABLE OF r_interface_data;
How can I approach that. Thanks!
UPDATE: In my particular case I ended-up doing the following approach
using (OleDbCommand cmd = new OleDbCommand("PACKAGE.procedure_name"))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlManager sqlManager = new SqlManager();
return sqlManager.GetDataSet(cmd);
}
I don't think you're that far off... try this:
OracleCommand cmd = new OracleCommand("return_num", Oraclecon);
cmd.Parameters.Add(new OracleParameter("xNum", OracleDbType.Decimal,
ParameterDirection.Output));
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
OracleDecimal d = (OracleDecimal)cmd.Parameters[0].Value;
double result = d.ToDouble();
result now contains the out parameter from the procedure.
I think your problem is you were attempting to use a DbDataReader on a stored procedure. DbDataReader is for queries.
Also, I used ODP.net -- that may or may not have contributed to your issue, that you were using Ole.
I'm trying something new (for me, at least) and, rather than having specific functions for calling individual stored procedures (1-to-1 ratio of data access functions to stored procedures), I'm trying to write generalized functions that are passed a stored procedure name, as well as string arrays of parameter names and parameter values as arguments.
For example:
public DataTable CallQuery(string spName, string[] paramNames, string[] paramValues, string connString)
{
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(connString);
try
{
//create a command and assign it to the passed stored procedure name
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn; // new SqlConnection(connString); ;
cmd.CommandText = spName;
//add any and all parameters to the command
for(int i = 0; i < paramNames.Length; i++)
{
SqlParameter temp = new SqlParameter(paramNames[i], paramValues[i]);
cmd.Parameters.Add(temp);
//cmd.Parameters.AddWithValue(paramNames[i], paramValues[i]);
}
//get the data and return it
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dt);
return dt;
}
catch (Exception)
{
return dt;
}
}
Unfortunately, for some reason, when I call this function with parameter values (i.e. paramNames[0] = "#Provider" and paramValues[0] = "AT&T") and make the database call, I catch an exception saying that the stored procedure was expecting the parameter #Provider.
I've stepped through and verified that the parameters are being added with their values, but I'm still getting the same exception. Am I missing something simple here?
The reason I'm passing in the string arrays is because there could be anywhere from 0 to 5 parameters per stored procedure (so far...).
Try specifying a type for your parameters. However, the most important thing is make sure you do:
cmd.CommandType = CommandType.StoredProcedure;