How can I call a SQL function in C#? - c#

I have created a function in SQL, now I need to use that function in my C# application.
I tried using something like this, but it seems I'm doing it wrong since I'm getting:
Must declare the scalar value '#2064734117'
...when I give 2064734117 as the first parameter and 1 as the second parameter. Here is the code I'm talking about:
SqlConnection con = new SqlConnection(clsDb.connectionString);
string query = string.Format("select Function1(#{0},#{1}) ",
int.Parse(e.CurrentRow.Cells["CodeMeli"].Value.ToString()),1);
con.Open();
SqlCommand cmd = new SqlCommand(query,con);
SqlDataAdapter READER = new SqlDataAdapter();
READER.SelectCommand = cmd;
DataTable table = new DataTable();
READER.Fill(table);
radGridView1.DataSource = table;
con.Close();
And my function takes two integer parameters and returns a table. I checked it in Visual Studio and it worked, but I couldn't get it to work in my application.
And this is my function declaration:
ALTER FUNCTION dbo.Function1
(
/*
#parameter1 int = 5,
#parameter2 datatype
*/
#ID int,
#clsTypeID int
)
RETURNS TABLE/* #table_variable TABLE (column1 datatype, column2 datatype) */
AS
/*BEGIN */
/* INSERT INTO #table_variable
SELECT ... FROM ... */
RETURN SELECT * FROM tblCLASS2
WHERE STNID = #ID AND CLASSTYPEID = #clsTypeID
/*END */
/*GO*/

Your SQL is a bit off, it should be:
string query = string.Format("select * from dbo.Function1({0},{1});", int.Parse(e.CurrentRow.Cells["CodeMeli"].Value.ToString()),1);
You might want to use SqlParameter-objects to prevent sql injections:
string query = "select * from dbo.Function1(#pa1,#par2);";
cmd.Parameters.Add("#par1", SqlDbType.Int).Value = int.Parse(e.CurrentRow.Cells["CodeMeli"].Value.ToString());
cmd.Parameters.Add("#par2", SqlDbType.Int).Value = 1;

At a glance, the first thing I can see is that you aren't specifying the object owner / schema; that is required for functions, so it should be select dbo.Function1(...
Second: look at what your call to string.Format generates; that is generating #1 and #n for n another integer, but that is not a valid parameter name. Which is handy, because
Third: you didn't add any parameters
Fourth: for a table UDF (rather than a scalar UDF), you must select * from dbo.Function1(..., not just select dbo.Function1(...

You can do something like this:
myConn.Open();
//generating the new command for our database
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT OBJECTID_1, NDNT as theAddress, MIN(ABS(x - " + double.Parse(x.ToString()) + ") + ABS(y - " + double.Parse(y.ToString()) +")) from dbo.DWH_OUTPUT GROUP BY OBJECTID_1,NDNT HAVING (MIN(ABS(x - " + double.Parse(x.ToString()) + ") + ABS(y - " + double.Parse(y.ToString()) + ")) = (Select MIN(ABS(a.x - " + double.Parse(x.ToString()) + ") + ABS(a.y - " + double.Parse(y.ToString()) + ")) from dbo.DWH_OUTPUT a ) )";
cmd.Connection = myConn;
//getting some more ado.net objects
SqlDataAdapter da = new SqlDataAdapter();
DataSet ds = new DataSet();
da.SelectCommand = cmd;
da.Fill(ds, #"Addresses");
if (ds.Tables[0].Rows.Count > 0)
{
theAddress = ds.Tables[0].Rows[0][#"theAddress"] + #" (proximity address)";
}
myConn.Close();
Note how in this example, you set the SqlCommand's CommandType to CommandType.Text. Specify your command parameters (i.e. the select function in your code snippet), and then populate the dataset with the Fill method. Then you can pluck out the values from the rows as you ordinarily would with standard ado.net.
If you need to call a stored proc, have a look at this:
How do I call a TSQL function from ado.net

You need fully qualified name of function with owner/schema name
A working sample available at following link:

Related

I have stored procedure created in the database, in my C# code I want to convert the insert query into that stored procedure

Please find the C# code where right now I'm inserting as a normal query, but I have created a stored procedure for insert which takes 7 parameters
try
{
string query = #"insert into dbo.PhotoContest values('" +
photo.contestTitle + #"' , '" +
photo.contestLocation + #"' , '" +
photo.contestDescription + #"' , '" +
photo.votingType + #"' , '" +
photo.voteDate + #"' , '" +
photo.voteEndDate + #"' , '" +
photo.partDate + #"' , '" +
photo.partEndDate + #"' , '" +
photo.uploads + #"' , '" +
photo.photoOption + #"' , '" +
photo.caption + #"')";
// method to execute the query
DataTable table = new DataTable();
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["PhotoContestDB"].ConnectionString))
using (var cmd = new SqlCommand(query, con))
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandType = CommandType.Text;
da.Fill(table);
}
return "Added successfully!";
}
And this is my stored procedure which I created in my database:
DROP PROCEDURE IF EXISTS dbo.InsertPhotoContest;
GO
CREATE PROCEDURE dbo.InsertPhotoContest
(#PhotoContestID int OUTPUT,
#contestTitle varchar(500),
#contestLocation varchar(500),
#contestDescription varchar(500),
#votingType varchar(255),
#voteDate datetime,
#voteEndDate datetime,
#partDate datetime,
#partEndDate datetime,
#uploads varchar(500),
#photoOption varchar(500),
#caption varchar(500))
AS
BEGIN;
INSERT INTO dbo.PhotoContest (contestTitle, contestLocation, contestDescription,
votingType, voteDate, voteEndDate,
partDate, partEndDate, uploads, photoOption, caption)
VALUES (#contestTitle, #contestLocation, #contestDescription,
#votingType, #voteDate, #voteEndDate,
#partDate, #partEndDate, #uploads, #photoOption, #caption);
SELECT #PhotoContestID = SCOPE_IDENTITY();
SELECT *
FROM dbo.PhotoContest
WHERE PhotoContestID = #PhotoContestID;
END;
I just need to know how to convert my query into stored procedure. As you can see in my code 11 parameters which I need to insert. In my photo contest table, there is photoContestID which is identity (which means it auto increments), first is my stored procedure correct, also wanted to know how would I use my stored procedure in my C# backend code?
You need something like this - and I would not really return the whole inserted row from the stored procedure (since you've already passed in all the input parameter values anyway) - just read the return new ID value from your OUTPUT parameter.
// set up your connection and command
using (var con = new SqlConnection(.....))
using (var cmd = new SqlCommand("dbo.InsertPhotoContest", con))
{
// define it as stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// add input parameters and set their value
cmd.Parameters.Add("#contestTitle", SqlDbType.VarChar, 500).Value = photo.contestTitle;
cmd.Parameters.Add("#contestLocation", SqlDbType.VarChar, 500).Value = photo.contestLocation;
// and so forth, for all 11 input parameters
// separate output parameter
cmd.Parameters.Add("#PhotoContestID", SqlDbType.Int).Direction = ParameterDirection.Output;
// open connection, execute stored procedure, close connection
con.Open();
cmd.ExecuteNonQuery();
// get newly assigned ID
int newPhotoContestID = Convert.ToInt32(cmd.Parameters["#PhotoContestID"].Value);
con.Close();
}
All you need to do is this
DataTable table = new DataTable();
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["PhotoContestDB"].ConnectionString))
using (var cmd = new SqlCommand("dbo.InsertPhotoContest", con)) // SP NAME
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandType = CommandType.StoredProcedure; // CHANGE THIS
cmd.Parameters.Add(new SqlParameter("contestTitle", . . . . ));
// ADD MORE PARAMETERS HERE
con.Open();
da.Fill(table);
} // this will dispose all objects and close conn inside dispose
In your SP you don't need parameter #PhotoContestID int OUTPUT, because you are returning the entire row and you can do int id = (int)table.Rows[0]["YOUR-COLUMN-NAME"];
You could also invest into how to use OUTPUT INSERTED with your INSERT and SP return value

How do I execute a SqlDataReader after sqlInsertCmd.ExecuteNonQuery

I am inserting a data row into my SQL Server database and then I want to query the data to get the unique identifier from the inserted row but my SqlDataReader is returning an empty dataset. I am thinking it maybe that the transaction hasn't been committed or something like that but I am not sure. I do not get an error.
Here is my code:
try
{
strQuery = "INSERT INTO clientnames VALUES(NEWID(),'" + txtACLastName.Text + "','" + txtACFirstName.Text + "'," + 1 + ")";
using (SqlCommand sqlInsertCmd = new SqlCommand(strQuery, sqlConn))
{
intQueryResult = sqlInsertCmd.ExecuteNonQuery();
if (intQueryResult == 0)
{
blnSuccess = false;
goto InsertClientNamesError;
}
else
{
blnSuccess = true;
}
sqlInsertCmd.Dispose();
}
if (blnSuccess)
{
strQuery = "select clientID from clientnames where firstname = '" + txtACFirstName.Text + "' and lastname = '" + txtACLastName.Text + "'";
using (SqlCommand sqlSelectCmd = new SqlCommand(strQuery, sqlConn))
{
SqlDataReader sqlDataRead = sqlSelectCmd.ExecuteReader();
while (sqlDataRead.Read())
{
strClientID = sqlDataRead.ToString();
}
sqlDataRead.Close();
sqlSelectCmd.Dispose();
}
}
}
catch (Exception exQuery)
{
System.Windows.MessageBox.Show("InsertClientNames: Error, " + exQuery.Message + ", has occurred.");
}
You are not getting the desired result because perhaps the SqlConnection is not opened explicitly (just a guess hard to tell without having full code). But this link shows you how to read from reader --> https://msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx
But I suggest that you Please do not do it this way. Reason is you are making Two round trips to the DB Server when only one would have done the job for you IF you were using stored procedures. Also you are exposing yourselves to SQL Injection attacks as you are not parameterizing your queries.
Stored procedure:
CREATE PROCEDURE dbo.INS_clientnames
(
#FirstName varchar(100),
#LastName varchar(100),
#NewID int out
)
AS
BEGIN
Declare #Err int
set #NewID = NewID() -- Get the New ID and store it in the variable ( #NewID ) that the SP will return back to the caller
INSERT INTO clientnames values (#NewID , #FirstName , #LastName)
SET #Err = ##ERROR
IF #Error <> 0 -- Check If there was an error
Begin
SET #NewID = -1 -- Indicates that there was an error. You could log this into a Log Table with further details like error id and name.
END
RETURN
END
C# code to execute the above stored procedure and get the NewID:
using(SqlConnection conn = new SqlConnection(connectionString ))
{
using(SqlCommand cmd = new SqlCommand("dbo.INS_clientnames", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// set up the parameters that the Stored Procedure expects
cmd.Parameters.Add("#FirstName", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#LastName" , SqlDbType.VarChar, 100);
cmd.Parameters.Add("#NewId" , SqlDbType.Int).Direction = ParameterDirection.Output;
// set parameter values that your code will send to the SP as parameter values
cmd.Parameters["#FirstName"].Value = txtACFirstName.Text ;
cmd.Parameters["#LastName"].Value = txtACLastName.Text ;
// open connection and execute stored procedure
conn.Open();
cmd.ExecuteNonQuery();
// read output value from #NewId
int NewID = Convert.ToInt32(cmd.Parameters["#NewId"].Value);
}
}
Add the following line to your stored procedure that inserts the record
SELECT SCOPE_IDENTITY()
This will return the last identity value inserted in that table.
And use cmd.ExecuteScalar() instead of ExecuteNonQuery()
ExecuteScalar() executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored. [More info][1]
I see two approaches to do this:
either you generate the new GUID on the client side in your C# code and pass it into the query - then you already know what the new id is going to be, so you don't need to do a second query to get it:
you create your GUID on the server side and return it to the caller using the OUTPUT clause in your query
Approach #1:
// define connection string and query
string connStr = "--your connection string here--";
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) VALUES(#ID, #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// create the GUID in C# - this is the ID - no need to go get it again - this *IS* the id
Guid id = Guid.NewGuid();
// set the parameters
cmd.Parameters.Add("#ID", SqlDbType.UniqueIdentifier).Value = id;
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Peter";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Miller";
// open connection, execute query, close connection
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
Approach #2:
// define connection string and query
string connStr = "--your connection string here--";
// query has an "OUTPUT" clause to return a newly inserted piece of data
// back to the caller, just as if a SELECT had been issued
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) OUTPUT Inserted.ClientID VALUES(NEWID(), #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// set the parameters - note: you do *NOT* send in a GUID value - the NEWID() will create one automatically, on the server
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Frank";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Brown";
// open connection
conn.Open();
// execute query and get back one row, one column - the value in the "OUTPUT" clause
object output = cmd.ExecuteScalar();
Guid newId;
if (Guid.TryParse(output.ToString(), out newId))
{
//
}
conn.Close();
}

C# mysql parameterized query

I have this peace of code where I need to retrieve data from Mysql. If I use parameterized query it does not take actual parameter value instead it takes parameter name as value.
Error: #choise must be defined
MySqlConnection connection = new MySqlConnection("");
MySqlDataAdapter mySqlDataAdapter;
DataSet DS;
private string columnValue = xxx;
private string Choise = yyy;
MySqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM table2 WHERE " + columnValue + " = #choise";
command.Parameters.Add(new MySqlParameter("#choise", Choise));
DS = new DataSet();
connection.Open();
mySqlDataAdapter = new MySqlDataAdapter(command.CommandText, connection);
mySqlDataAdapter.Fill(DS);
connection.Close();
when I run this i get query like:
SELECT * FROM table2 WHERE xxx = #choise
instead of
SELECT * FROM table2 WHERE xxx = yyy.
Where is the problem?
I have tried:
command.Parameters.Add(new MySqlParameter("#choise", Choise));
command.Parameters.AddWithValue("#choise", Choise);
It works fine when I'm using actual variables instead of parameters.
I think you need to run Prepare() on the command before adding parameters:
command.CommandText = "select * from table2 where " + columnValue + " = #choise";
command.Prepare();
command.Parameters.AddWithValue("#choise", Choise);
Try this instead:
command.CommandText = "SELECT * FROM `table2` WHERE `" + columnValue + "` = #choise";
command.Parameters.AddWithValue("#choise", Choise);

SqlCommand Parameter not working

When I execute the following command in SSMS I get the results I expect -
SELECT *
FROM [Event]
WHERE Active = 'True'
AND [Name] LIKE '%P%'
i.e. All the Events whose Name contains a P are displayed
However, when I build this command in my WinForms app written in C# I always get an empty datatable.
I am using the following code -
string sqlText = "SELECT * " +
"FROM Event " +
"WHERE Active = #Active";
SqlCommand sqlCom = new SqlCommand();
sqlCom.Parameters.Add("#Active", SqlDbType.Bit).Value = active;
if (txtSearchName.Text != null && txtSearchName.Text.Length > 0)
{
sqlText += " AND [Name] LIKE '#Name'";
string value = "%" + txtSearchName.Text + "%";
sqlCom.Parameters.Add("#Name", SqlDbType.VarChar).Value = value;
}
sqlText += " ORDER BY [Date] DESC;";
sqlCom.CommandText = sqlText;
DataTable table = Express.GetTable(sqlCom);
DataView view = new DataView(table);
DataTable show = view.ToTable(true, "ID", "Date", "Name");
dataEventsFound.DataSource = show;
Please note Express.GetTable is a method that simply adds a SQLConnection and uses a DataReader to fill and return a DataTable. I do not believe the fault lies in that Method as it is used hundreds of times throughout this and other applications I have written.
I think the error is something to do with the Command Text and the #Name Parameter, but I can't determine exactly what the problem is, and why my DataTable is always empty.
Remove single quote from between #Name parameter.
sqlText += " AND [Name] LIKE #Name";
After this line : sqlCom.CommandText = sqlText;
You should add this line: sqlCom.ExecuteNonQuery();

Dynamically passing a value inside a query?

I have two columns syntax and query in my table Table1. Syntax contains data called po and a query called select * from po_pomas_pur_order_hdr where pomas_pono =. I got this query value by using
SqlDataAdapter da = new SqlDataAdapter("select query from Table1 where syntax = '" + textBox1.Text + "'", conn);
And my problem is that I need to dynamically pass another value inside the query which I retrived using dataadapter like this:
SqlDataAdapter da1 = new SqlDataAdapter(da.tostring() +"'"+ textBox1.Text +"'", conn)
The resulting query should be like this:
select * from po_pomas_pur_order_hdr where pomas_pono = '2PO/000002/09-10'
But it is not possible. How to get a query like this? Any suggestion?
SqlDataAdapter is used to fill datasets and datatables. You cannot obtain the result of a query with ToString(). I think you want to use SqlCommand to execute your first query to retrieve the actual query to run from the database like this:
string query = null;
using (var command = new SqlCommand("select query from Table1 where syntax = #Syntax", conn))
{
command.Parameters.AddWithValue("#Syntax", textBox1.Text);
query = command.ExecuteScalar(); // this assumes only one query result is returned
}
Then you can use the data adapter to fill it:
SqlDataAdapter da1 = new SqlDataAdapter(query +"'"+ textBox1.Text +"'", conn);
Although I would suggest to use parameters for that as well.
in this way is more safe: dotnetperls
He check the "'" and the "\", check the type of the fields etc...
Code from the example above (is the same for insert delete and update):
using (SqlCommand command = new SqlCommand("SELECT * FROM Dogs1 WHERE Name LIKE #Name", connection))
{
//
// Add new SqlParameter to the command.
//
command.Parameters.Add(new SqlParameter("Name", dogName));
//
// Read in the SELECT results.
//
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int weight = reader.GetInt32(0);
string name = reader.GetString(1);
string breed = reader.GetString(2);
Console.WriteLine("Weight = {0}, Name = {1}, Breed = {2}", weight, name, breed);
}
}
I suggest you to use SqlParameters. Here is example how to use DataAdapter and parameters.
Provided that you have a DataSet you intend to fill using the adapter and that you adjust the queries to use parameters in order to avoid sql injection you should be able to use something like this:
string query;
using(var sqlCommand = new SqlCommand(
"select query from Table1 where syntax=#syntax", conn))
{
sqlCommand.Parameters.AddWithValue("syntax", textBox1.Text);
query = (string)sqlCommand.ExecuteScalar();
}
using(var dataAdapter = new SqlDataAdapter())
using(var dataCommand = new SqlCommand(query, conn))
{
dataCommand.Parameters.AddWithValue("parameter", poNumber);
dataAdapter.SelectCommand = dataCommand;
dataAdapter.Fill(myDataSet);
}

Categories