How can I implement a simple cursor fetch on a basic SELECT statement like 'SELECT * FROM Employees' using ODP.NET?
So it's quite straightforward.
First create OracleConnection class like this
OracleConnection con = new OracleConnection(ConfigurationManager.ConnectionStrings["connectionstring"].ConnectionString);
con.Open(); //opens connection
Then you define and OracleCommand instance first by passing either raw query/stored procedure as first argument like
So in your particular case it would be OracleCommand cmd = new OracleCommand("SELECT * FROM Employees", con
if (con.State == ConnectionState.Open)
{
using (OracleCommand cmd = new OracleCommand(<query>/<stored proc>, con))
{
cmd.CommandType = CommandType.StoredProcedure; //in case of stored proc
cmd.BindByName = true;
OracleDataReader reader;
try
{
reader = cmd.ExecuteReader();
while(reader.Read())
{
Console.WriteLine("field: {0}", reader.GetDecimal(0));
}
}
catch (OracleException e)
{
foreach (OracleError err in e.Errors)
{
//print errors
}
}
con.Close();
con.Dispose();
}
}
Here is the example http://www.oracle.com/technetwork/articles/dotnet/williams-refcursors-092375.html
Related
In the project I call a method to query additional information with a SqlConnection block, but then I validate if exists in a second table using another sqlconnection block, but it is supposed to be disposed (closed) after getting back to the method InsertNewData, but when calling to Open the connection for the Insert, I'm getting the following message:
The connection was not closed. The connection's current state is open.
My code is like this:
public void InsertNewData(string operation)
{
DataTable dt = new DataTable();
try
{
if (operation!= string.Empty)
{
using (SqlConnection oconn = new SqlConnection(myDBone))
{
SqlCommand cmd = new SqlCommand();
SqlDataAdapter da = new SqlDataAdapter();
string query = "SELECT * FROM operations "+
"WHERE idoper=#id";
oconn.Open();
cmd = new SqlCommand(query, oconn);
cmd.Parameters.Add(new SqlParameter("#id", operation.ToString()));
da = new SqlDataAdapter(cmd);
da.Fill(dt);
}
if (dt.Rows.Count > 0)
{
using (SqlConnection con = new SqlConnection(myDBtwo))
{
SqlCommand com = new SqlCommand();
string query= "";
foreach (DataRow x in dt.Rows)
{
if (ValidateData(x) == false)
{
query= "INSERT INTO history(iddata,description, datehist ) "+
" VALUES(#id,#descrip,GETDATE())";
con.Open(); //Here throws the Exception error
com = new SqlCommand(query, con);
com.Parameters.Add(new SqlParameter("#id", x["idoper"].ToString()));
com.Parameters.Add(new SqlParameter("#descrip", x["description"] ));
com.ExecuteNonQuery();
}
}
}
}
}
}
catch (Exception x)
{
throw x;
}
}
public bool ValidateData(DataRow row)
{
bool exists= false;
string operation= row["idoper"].ToString();
string descrip= row["description"].ToString();
if (operation!= string.Empty && descrip!= string.Empty)
{
using (SqlConnection oconn = new SqlConnection(sqlrastreo))
{
SqlCommand cmd = new SqlCommand();
string query = "SELECT COUNT(*) FROM history "+
"WHERE iddata=#id AND description=#descrip";
oconn.Open();
cmd = new SqlCommand(query, oconn);
com.Parameters.Add(new SqlParameter("#id", operation));
com.Parameters.Add(new SqlParameter("#descrip", descrip));
int count = (int) cmd.ExecuteScalar();
if (count > 0)
exists= true;
}// Here it should be Disposed or closed the SqlConnection
}
return exists;
}
What I'm doing wrong, because it's suppose to be closed the other connection and the other hasn't been opened ? or Should I Still call the Close() method for each SqlConnection inside the block Using?
Updated:
I've changed to parameters for best reading code and recommendation syntax.
NOTE
The values and parameters aren't the real ones, my real table descriptions have about 8 fields, but I validate with just two parameters that aren't primary key, but considering that I can't edit the table properties (Have only reading permissions for that database).
Update 2:
Thanks to the recommendation of Sean Lange, it was better and so simple to use a Store Procedure (SP) to validate and insert at the same time, so I do it as follow in code of the process:
public void InsertNewData(string operation)
{
try
{
if(operation == string.Empty)
return;
using(SqlConnection con = new SqlConnection(myDBtwo))
{
con.Open();
var cmd = new SqlCommand("SP_InsertData", con);
cmd.Parameters.Add(new SqlParameter("#id", operation));
cmd.ExecuteNonQuery();
}
}
catch(Exception ex)
{ throw ex; }
}
And then in my SP I insert a select statement of the parameter, to avoid duplicates and also do it in One go:
CREATE PROCEDURE SP_InsertData #id VARCHAR(10)
AS
BEGIN
INSERT INTO History
SELECT O.idoper, O.description
FROM myDBone.dbo.operations O
LEFT JOIN History H
ON H.iddata = O.idoper AND H.description = O.description
WHERE O.idoper=#id AND H.iddata IS NULL
END
Thanks for your support, and hope it helps someone.
First your code is badly written,as they have suggested you don't need to validate,try catch will do it for you.second opening a connection inside a loop ( foreach in your case) will will result to trying to open already open connection. Example here you could do something like
query= "INSERT INTO history(iddata,description, datehist" VALUES(#id,#descrip,GETDATE())";
using (SqlConnection con = new SqlConnection(myDBtwo))
{
con.Open();
SqlCommand com = new SqlCommand(query,con);
foreach (DataRow x in dt.Rows)
{
com.Parameters.Add(new SqlParameter("#id", x["idoper"].ToString()));
com.Parameters.Add(new SqlParameter("#descrip", x["description"] ));
com.ExecuteNonQuery();
}
}
}
I'm trying to write a method which should communicate with database, but I'm not sure if my approach is right.
public void dbWorkerLogin(int workerNumber) {
// Connection string stored in "conn"
if (!new SqlCommand("Some Command WHERE id=" +workernumber,conn).executeReader().HasRows)
{
new SqlCommand("exec STORED_PROCEDURE1 " + workerNumber, conn).ExecuteNonQuery();
new SqlCommand("exec STORED_PROCEDURE2 " + workerNumber, conn).ExecuteNonQuery();
}
else
{
new SqlCommand("exec STORED_PROCEDURE3 " + workerNumber,conn).ExecuteNonQuerry();
}
1) Is it ok to write it like this and start each SqlCommand with keyword new? Or should I do something like:
SqlCommand command = new SqlCommand(null, conn);
command = ...;
and then recycle the variable 'command' or this way?
using(SqlCommand cmd = new SqlCommand("COMMAND", conn);
2) Will my procedures work or should I use SqlCommand.Prepare() function that will covert my data into correct datatypes? eg. workerNumber is int, but in database it is stored as decimal.
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parametres.Add("#id", SqlDbType.Decimal).Value = workNumber;
cmd.Prepare();
cmd.ExecuteNonQuery();
}
Can you please somehow sum up what to use, what better not to? Unfortunately I can't test that first code because of limited access to DB so I'm not sure if it can be executed without errors or not.
Thank you for any help on this subject!
EDIT:
After a few hours I reach to this stage:
public int getWorkerNumber(string uniqueID)
{
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT number FROM worker WHERE workerID = #id",conn))
{
cmd.Parameters.Add("#id", SqlDbType.Decimal).Value = uniqueID;
using (SqlDataReader reader = cmd.ExecuteReader())
{
int answer;
while (reader.Read())
{
answer = (int)reader.GetDecimal(0);
}
return answer;
}
}
}
}
And this one:
public string dbLoginWorker(int workerNumber)
{
SqlCommand cmd;
SqlDataReader reader;
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
cmd = new SqlCommand("SELECT column FROM table WHERE id= #workernumber", conn);
cmd.Parameters.Add("#workernumber", SqlDbType.Decimal).Value = workerNumber;
reader = cmd.ExecuteReader();
if (!reader.HasRows)
{
cmd = new SqlCommand("STORED_PROCEDURE1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 1 executed";
else
{
cmd = new SqlCommand("STORED_PROCEDURE2", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("#INT", SqlDbType.SmallInt).Value = 1;
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 2 executed";
}
}
}
Both methods are functional (if I did no mistake in rewriting :) ). I'm not sure which of these methods (1st or 2nd) are better in terms of stability and if this approach is better and more ressistant to SQL Injection. Can someone comment on this subject? Thank you again for any help!
1) It is best to always use USING blocks when possible. This includes SqlConnection, SqlCommand, SqlReader and other objects that implement IDisposable. USING blocks automatically close and dispose of the objects, so you do not have to do so.
2) I believe that you are using the Prepare() method in the wrong place. Look at the following StackOverflow article for proper usage:
PrepareMethodInstructions.
3) in the dbLoginWorker() method, the first query is just used to determine if rows are found. Therefore, I suggest changing the SELECT command to SELECT TOP 1 column FROM table WHERE id= #workernumber so that the query is faster and more efficient.
4) I do not believe your commands are subject to SQL Injection attacks because they are fully parameterized. Good job on that one.
5) As a general thought, I suggest reading up on refactoring techniques. Your dbLoginWorker() method could be made more readable and maintainable, as well as self-documenting, if you created three additional methods, one for each SQL command, and named them something appropriate. You could also setup a method for creating a connection based on a connection name, and you would not have as much duplicate code. For example:
public static SqlConnection GetConnection(string connectionName)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString);
conn.Open();
return conn;
}
public string dbLoginWorker(int workerNumber)
{
using (conn = GetConnection("dbConnect"))
{
if (CanFindWorkerNumber(conn, workerNumber))
ExecuteProcedure1(conn);
else
ExecuteProcedure2(conn);
}
}
public bool CanFindWorkerNumber (SqlConnection conn, int workerNumber)
{
bool success = false;
using (SqlCommand cmd = new SqlCommand("SELECT TOP 1 column FROM table WHERE id= #workernumber", conn))
{
cmd.Parameters.Add("#workernumber", SqlDbType.Decimal);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
success = cmd.ExecuteScalar() != null;
}
return success;
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal);
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
cmd.Parameters[1].Value = "text";
cmd.ExecuteNonQuery();
}
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.Decimal);
cmd.Parameters.Add("#INT", SqlDbType.SmallInt).Value);
cmd.Parameters.Add("#VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0] = workerNumber;
cmd.Parameters[1] = 1;
cmd.Parameters[2] = "text";
cmd.ExecuteNonQuery();
}
}
You could actually do this in one SQL commend. Right now you are pulling back a result set only to see if it has rows or not, then executing different commands based on that. You should be able to do that in one command, disposing of it and the connection appropriately:
var sql =
#"
IF EXISTS(Some Command WHERE id=#workernumber)
BEGIN
exec STORED_PROCEDURE1 #workernumber;
exec STORED_PROCEDURE2 #workernumber;
END
ELSE
exec STORED_PROCEDURE3 #workernumber;
";
Note that you're not vulnerable to SQL injection because you're not dealing with strings, only integers.
My code shown below is create as an inline SQL statement. How can this code be written as a stored procedure??
The code is:
public Stream SelectEmployeeImageByID(int theID)
{
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString.ToString());
string sql = "SELECT Image FROM Employees WHERE EmployeeId = #EmployeeId";
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#EmployeeId", theID);
connection.Open();
object theImg = cmd.ExecuteScalar();
try
{
return new MemoryStream((byte[])theImg);
}
catch
{
return null;
}
finally
{
connection.Close();
}
}
you can do this
create procedure SelectEmployeeImage(#employee int)
as
begin
SELECT Image FROM Employees WHERE EmployeeId = #EmployeeId
end
then your code will be this form
public Stream SelectEmployeeImageByID(int theID)
{
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString.ToString());
string sql = "SelectEmployeeImage";
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmployeeId", theID);
connection.Open();
object theImg = cmd.ExecuteScalar();
try
{
return new MemoryStream((byte[])theImg);
}
catch
{
return null;
}
finally
{
connection.Close();
}
}
hope this will help you
Creating Stored Procedure
Create procedure SP_InsertEmployee
as
#EmployeeId int
BEGIN
SELECT Image FROM Employees WHERE EmployeeId=#EmployeeId
END
You Should Set CommandType=StoredProcedure and Rest of will be same
cmd.CommandType = CommandType.StoredProcedure;
Recommendations
Always use using which automatically disposes connections
using (SqlConnection con = new SqlConnection())
{
con.open();
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
//object theImg = cmd.ExecuteScalar();
}
con.Dispose();
}
I've got the following code (here with pseudovalues for readability), where the first connection returns a lot of data (thousands of rows). SqlDataReader reads them one by one by the reader.Read() and then opens a new connection to update each row with new values:
using (SqlConnection conn = new SqlConnection(connString))
using (SqlCommand cmd = new SqlCommand("sp1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#param1", param1);
cmd.Connection.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
try
{
string hash= utils.SHA256.Hashing((string)reader["firstRow"], saltValue);
using (SqlConnection conn2 = new SqlConnection(connString))
using (SqlCommand cmd2 = new SqlCommand("sp2", conn2))
{
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("#param1", param1);
cmd2.Parameters.AddWithValue("#param2", param2);
cmd2.Connection.Open();
cmd2.ExecuteNonQuery();
}
}
catch (SqlException ex)
{
//something
}
}
}
}
but it throws an error:
[InvalidOperationException: Invalid attempt to call Read when reader is closed.]
System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout) +640
System.Data.SqlClient.SqlDataReader.Read() +9
In development environment it works fine, but here there's only a few hundred rows. It throws the error immediately, so it doesn't directly look like some kind of timeout, but hey - I don't know...
Don't know why it happens, but it's really a bad idea to execute queries while iterating a live connection to the same database. Keep in mind that as long as you iterate records with a DataReader, the connection is alive.
Much worse is opening then closing a connection thousands of times in a quick succession. This alone can bring any database down to its knees.
Change your logic, store the values you need in a local variable (structure doesn't matter) then use one connection only to execute all the stored procedures you need.
For example:
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
List<string[]> values = new List<string[]>();
using (SqlCommand cmd = new SqlCommand("sp1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#param1", param1);
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
try
{
string hash= utils.SHA256.Hashing((string)reader["firstRow"], saltValue);
string anotherValue = (string)reader["secondRow"];
values.Add(new string[] { hash, anotherValue });
}
catch (SqlException ex)
{
//something
}
}
reader.Close();
}
}
if (values.Count > 0)
{
using (SqlCommand cmd2 = new SqlCommand("sp2", conn))
{
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Parameters.AddWithValue("#param1", null);
cmd2.Parameters.AddWithValue("#param2", null);
values.ForEach(items =>
{
cmd2.Parameters["#param1"].Value = items[0];
cmd2.Parameters["#param2"].Value = items[1];
cmd2.ExecuteNonQuery();
});
}
}
conn.Close();
}
One connection, one command to execute all stored procedures. Really don't need more than that.
This is a really, really stupid question but I am so accustomed to using linq / other methods for connecting and querying a database that I never stopped to learn how to do it from the ground up.
Question: How do I establish a manual connection to a database and pass it a string param in C#? (yes, I know.. pure ignorance).
Thanks
using (SqlConnection conn = new SqlConnection(databaseConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "StoredProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ID", fileID);
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}
}
One uses the SqlCommand class to execute commands (either stored procedures or sql) on SQL Server using ado.net. Tutorials abound.
Here's an example from http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson07.aspx
public void RunStoredProcParams()
{
SqlConnection conn = null;
SqlDataReader rdr = null;
// typically obtained from user
// input, but we take a short cut
string custId = "FURIB";
Console.WriteLine("\nCustomer Order History:\n");
try
{
// create and open a connection object
conn = new
SqlConnection("Server=(local);DataBase=Northwind;Integrated Security=SSPI");
conn.Open();
// 1. create a command object identifying
// the stored procedure
SqlCommand cmd = new SqlCommand(
"CustOrderHist", conn);
// 2. set the command object so it knows
// to execute a stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// 3. add parameter to command, which
// will be passed to the stored procedure
cmd.Parameters.Add(
new SqlParameter("#CustomerID", custId));
// execute the command
rdr = cmd.ExecuteReader();
// iterate through results, printing each to console
while (rdr.Read())
{
Console.WriteLine(
"Product: {0,-35} Total: {1,2}",
rdr["ProductName"],
rdr["Total"]);
}
}
finally
{
if (conn != null)
{
conn.Close();
}
if (rdr != null)
{
rdr.Close();
}
}
}
3 things no one else has shown you yet:
"Stacking" using statements
Setting an explicit parameter type rather than letting .Net try to pick one for you
"var" keyword
.
string sql = "MyProcedureName";
using (var cn = new SqlConnection(databaseConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ParameterName", SqlDbType.VarChar, 50)
.Value = "MyParameterValue";
conn.Open();
using (SqlDataReader rdr =
cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
if (rdr.Read())
{
// process row from resultset;
}
}
}