Unable to access table variable in stored procedure - c#

I am passing a datatable from Code to stored procedure in this way.
DataTable table = CommonFunctions.ToDataTable(request);
object[] spParams = new object[1];
spParams[0] = table;
DbCommand dbCommand =
db.GetStoredProcCommand("OS_UpdateOrgStructureDetails", spParams);
I am trying to access this parameter in stored proc.
CratePROCEDURE OS_UpdateOrgUnits
#table OS_RenameNodeTable READONLY
AS
BEGIN
UPDATE OrgUnit
SET DetailName = ut.NewValue
FROM #table ut
INNER JOIN OrgUnit ou ON ou.OrgUnitID = ut.OrgUnitID
END
But when the call is made to stored procedure it throws an error.
The incoming tabular data stream (TDS) remote procedure call (RPC) protocol
stream is incorrect. Table-valued parameter 1 ("#table"), row 0, column 0:
Data type 0xF3 (user-defined table type) has a non-zero length database name
specified. Database name is not allowed with a table-valued parameter, only
schema name and type name are valid.
Not able to resolve the error.

Because of a bug in the SqlCommandBuilder.DeriveParameters method, the TypeName property of the SqlParameter object for the table valued parameter contains the database name (see http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommandbuilder.deriveparameters.aspx, the comment "Table valued parameters not typed correctly").
The fix this you can add this general purpose code right after creating the command:
foreach (SqlParameter parameter in dbCommand.Parameters)
{
if (parameter.SqlDbType != SqlDbType.Structured)
{
continue;
}
string name = parameter.TypeName;
int index = name.IndexOf(".");
if (index == -1)
{
continue;
}
name = name.Substring(index + 1);
if (name.Contains("."))
{
parameter.TypeName = name;
}
}

If you only have one or two table parameters, you don't have to loop through all the parameters. I wrote a function instead and passed that parameter to that function so that it would fix the typename.
This is the function:
Private Sub SetTypeNameForTableParameter(ByRef parameter As System.Data.SqlClient.SqlParameter)
If parameter.SqlDbType = SqlDbType.Structured Then
Dim name As String = parameter.TypeName
Dim index As Integer = name.IndexOf(".")
If index <> -1 Then
name = name.Substring(index + 1)
If name.Contains(".") Then
parameter.TypeName = name
End If
End If
End If
End Sub
This is the piece of code where I'm making the call to the database:
'Get Parameters in stored proc
Dim cmd As System.Data.Common.DbCommand = db.GetStoredProcCommand("MyStoredProc")
db.DiscoverParameters(cmd)
'The first parameter is the return value. Remove it.
Dim returnValueParam As Data.Common.DbParameter = cmd.Parameters(0)
cmd.Parameters.Remove(returnValueParam)
'Set type name for every table parameter
SetTypeNameForTableParameter(cmd.Parameters(1))
'Assign values to the parameters
cmd.Parameters(0).Value = id
cmd.Parameters(1).Value = mydatatable
'Execute the command
db.ExecuteNonQuery(cmd)

Related

variable parameter for sql parameters

I'm accessing a stored procedure and I'm trying to pass some parameters to it. I have the following loop that adds all parameters:
List<string> paramNames = new List<string> {
"#EmployeeId",
"#Location",
"#StartYear",
"#EndYear",
"#Department",
"#Title",
"#FileName"
};
List<string> paramValues = new List<string> {
EmployeeId,
Location,
StartYear,
EndYear,
Department,
Title,
FileName
};
// Add the input parameter and set its properties.
SqlParameter parameter;
for (int i = 0; i < paramNames.Count(); i++)
{
parameter = new SqlParameter();
parameter.ParameterName = paramNames[i];
parameter.Value = paramValues[i];
command.Parameters.Add(parameter);
}
the first parameter is "EmployeeId" and this value is coming from the method arguments. This parameter can either be a string value "All" or it can be an int value with the employee id.
-How do I pass its value to the stored procedure so it accepts a string or an int.
-And how do I tell it that if its "All" then I want all employees, otherwise if its a number, I want the employee with that number ID?
Most likely your stored procedure has a condition similar to
WHERE EMPLOYEE_ID = #EmployeeId
Change it to
WHERE #EmployeeId = 'All' OR EMPLOYEE_ID = #EmployeeId
This is a 'catch all' condition. If you pass 'All' - first part will be true and condition passes. Otherwise value of the parameter will be verified against table field
Instead of using "All" you could simply just use a value of -1 or 0 for EmployeeId and then check for 0 or -1 in your stored procedure to return all users in that scenario.

How to set size for variable length datatype

I am working on sending parameters to a procedure and do some operations based on the output returned from the procedure. I have a common class that handles adding parameters and calling the procedure. The class will be called like:
obj.addparameter(parametername, parametervalue, oracletype.datatype, parameterdirection.input)
so lets say i have a procedure that takes customer id as parameter and outputs name.
PROCEDURE get_customer_name(
c_customer_id IN Number,
c_customer_name OUT VARCHAR)
IS
BEGIN
SELECT last_name || ' ' || middle_name || ' ' || first_name
INTO c_customer_name
FROM CUSTOMER WHERE CUSTOMER_ID = c_customer_id;
END;
To call the above procedure i need to call the class like:
obj.addparameter("customerId", 123, oracletype.number, parameterdirection.Input)
obj.addparameter("customerName", dbnull.value, oracletype.varchar, parameterdirection.output)
The class has the following code:
if (param.ParamDirection == ParameterDirection.Output)
{
command.Parameters.Add(c_customer_name,oracletype.varchar);
command.parameters["c_customer_name"].direction = parameterdirection.output
}
else
{
command.parameters.add("c_customer_id",oracletype.number).value = 123;
command.parameters["c_customer_id"].direction = parameterdirection.input
}
command.commandType = commandType.StoredProcedure;
command.executenonquery();//Error: c_customer_name "No size set for variable length data type: String."
When I run the above code, I am getting an error:
"No size set for variable length data type: String."
I had to add the size of the column in the line below to avoid the error.
command.Parameters.Add(c_customer_name,oracletype.varchar,50)//Add size to output parameter
Is there a way to avoid adding size property to output parameter. According to MSDN, it looks like size property needs to be added for output parameters. If thats the case then,how to handle the size for different output parameters. I might return "customer name", "address" as parameters but I don't want to specify size for different output parameters.
I am working on .NET4.0 with C# code-behind and Oracle database.

is using delimited parameter in stored procedure dangerous?

My C# side is like :
if(Request.QueryString["ValuesFromUser"]!=null)
{
ValuesFromUser_ = Request["ValuesFromUser"];
}
DataTable dtle = new DataTable();
SqlDataAdapter sqda;
sqda = new SqlDataAdapter("Checkforuserinput", Connection);
SqlParameter SQP = sqda.SelectCommand.Parameters.Add("#arg", SqlDbType.VarChar);
SQP.Direction = ParameterDirection.Input;
SQP.Value = "ValuesFromUser_";
sqda.Fill(dtle );
User will pass few arguments like "user1,user2,user3"
In my sql side :
Create PROC [dbo].[Checkforuserinput] #arg VARCHAR(50)= 'All'
As
Select *
from UserData
where User in (SELECT *
FROM SplitDelimiterString(#Arg, ','))
And SplitDelimiterString Function is like :
ALTER FUNCTION [dbo].[SplitDelimiterString] (#StringWithDelimiter VARCHAR(8000), #Delimiter VARCHAR(8))
RETURNS #ItemTable TABLE (Item VARCHAR(8000))
AS
BEGIN
DECLARE #StartingPosition INT;
DECLARE #ItemInString VARCHAR(8000);
SELECT #StartingPosition = 1;
--Return if string is null or empty
IF LEN(#StringWithDelimiter) = 0 OR #StringWithDelimiter IS NULL RETURN;
WHILE #StartingPosition > 0
BEGIN
--Get starting index of delimiter .. If string
--doesn't contain any delimiter than it will returl 0
SET #StartingPosition = CHARINDEX(#Delimiter,#StringWithDelimiter);
--Get item from string
IF #StartingPosition > 0
SET #ItemInString = SUBSTRING(#StringWithDelimiter,0,#StartingPosition)
ELSE
SET #ItemInString = #StringWithDelimiter;
--If item isn't empty than add to return table
IF( LEN(#ItemInString) > 0)
INSERT INTO #ItemTable(Item) VALUES (#ItemInString);
--Remove inserted item from string
SET #StringWithDelimiter = SUBSTRING(#StringWithDelimiter,#StartingPosition +
LEN(#Delimiter),LEN(#StringWithDelimiter) - #StartingPosition)
--Break loop if string is empty
IF LEN(#StringWithDelimiter) = 0 BREAK;
END
RETURN
END
Is this code safe or in risk for SQL injection?
Or should i use this :
Create PROC [dbo].[Checkforuserinput] #arg VARCHAR(50)= 'All'
As
declare #query nvarchar(max)
set #query = 'Select * from UserData where User in ('+#Arg+')'
EXECUTE sp_executesql #query
I don't see any forseeable problems with the split function, but passing a table valued parameter would be more efficient.
SQL
CREATE TYPE ArrayOfString AS TABLE
(
Item VARCHAR(50)
);
GO
CREATE PROC [dbo].[Checkforuserinput] #arg ArrayOfString READONLY
AS
SELECT *
FROM UserData
WHERE User IN (SELECT Item FROM #arg)
GO
C#
Write a helper function to write any IEnumerable(string) into a DataTable that the SQL Server will understand.
public static class SqlExtensions
{
public static DataTable ToSqlArray(this IEnumerable<string> collection)
{
var dt = new DataTable("ArrayOfString");
dt.Columns.Add(new DataColumn("Item", typeof(string)));
foreach(var item in collection)
{
var row = dt.NewRow();
row[0] = item;
dt.Rows.Add(row);
}
return dt;
}
}
Usage:
if(Request.QueryString["ValuesFromUser"]!=null)
{
ValuesFromUser_ = Request["ValuesFromUser"];
}
var values = ValuesFromUser_.Split(",");
using(var adapter = new SqlDataAdapter("Checkforuserinput", Connection))
{
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.SelectCommand.Parameters.AddWithValue("#arg", values.ToSqlArray());
adapter.Fill(dtle);
}
Depending on your version of SQL Server, you may want to consider passing a table-valued parameter to your stored procedure.
Table-valued parameters provide an easy way to marshal multiple rows of data from a client application to SQL Server without requiring multiple round trips or special server-side logic for processing the data. You can use table-valued parameters to encapsulate rows of data in a client application and send the data to the server in a single parameterized command. The SqlParameter is populated by using the AddWithValue method and the SqlDbType is set to Structured.
The following link provides a good overview:
http://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx

Get the value of a stored procedure and pass that as a parameter to another stored procedure

I need to get the value of this stored procedure and pass it as a parameter to another stored procedure.
SqlCommand GetName = new SqlCommand("usp_GetName", sqlcon);
GetName.CommandType = CommandType.StoredProcedure;
GetName.Parameters.AddWithValue("#printer", printerguid);
This stored procedure has a simple select statement that returns only one row at any given point. I need to get that data and pass tat as a parameter elsewhere... Please tell me how to get the value from this stored procedure and store it in a variable
If it only returns one value use this and cast it to the type it is (string, int, etc)
var returnedValue = GetName.ExecuteScalar();
If you have more than 1 fields you should use:
using(IDataReader reader = GetName.ExecuteReader())
{
if (reader.Read())
{
// Populate any object using reader.GetString(reader.GetOrdinal("FieldName"))
// Replace GetString for any data type you need.
}
else
{
// No rows returnes
}
}

Error while running procedure without parameter: PLS-00306: wrong number or types of arguments in call

I have this update statement in oracle procedure
Spec:
PROCEDURE Update_G;
Body:
PROCEDURE Update_G
AS
BEGIN
UPDATE group SET check_flag = 0 WHERE check_flag = 1;
UPDATE employee SET check_flag = 0 WHERE check_flag = 1;
END Update_G;
This procedure when i call from .net its throwing error:
PLS-00306: wrong number or types of arguments in call to Update_G
It runs from oracle but from .net its throwing error. If input parameter is added it works, but i don't need any parameter.
Dot Net Code:
Database db = GetOracleDbInstance();
object[] spParams = new object[0];
string spName = "Update_G";
db.ExecuteDataSet(spName, spParams);
//There are no parameters here, i also tried this:
Database db = GetOracleDbInstance();
db.ExecuteNonQuery(CommandType.StoredProcedure, "Update_G");
//parameters required for procedure,if no parameter then pass Null
Database db = GetOracleDbInstance();
string spName = "Update_G";
db.ExecuteDataSet(spName, Null); //or db.ExecuteDataSet(spName); try both
answer reference

Categories