Is this the following code healthy? Or I don't need to use the using keyword as the SqlDataAdapter will handle closing the connection?
public static DataSet Fetch(string sp, SqlParameter [] prm)
{
using (SqlConnection con = new SqlConnection(ConStr))
{
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = sp;
cmd.Parameters.AddRange(prm);
using (SqlDataAdapter dta = new SqlDataAdapter(cmd))
{
DataSet dst = new DataSet();
dta.Fill(dst);
return dst;
}
}
}
}
#MarkGravell I need a suggestions here, I am really looking to use DataReader, but I was looking all the time to use the using keyword to ensure closing the connections. Where with DataReader we can not use it because it will close the connection if we want to return the DataReader back to some method.
So do you think the following technique is fine with DataReader and the using keyword:
public static SqlDataReader Fetch(string sp, SqlParameter [] prm)
{
SqlCommand cmd = new SqlConnection(ConStr).CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = sp;
cmd.Parameters.AddRange(prm);
cmd.Connection.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
using (SqlDataReader dtrPrize = Sql.Fetch("SelectPrize", new SqlParameter[] { new SqlParameter("id", id) }))
{
dtrPrize.Read();
Prize prize = new Prize();
prize.id = (int)dtrPrize[dtrPrize.GetOrdinal("id")];
prize.artitle = (string)dtrPrize[dtrPrize.GetOrdinal("artitle")];
prize.entitle = (string)dtrPrize[dtrPrize.GetOrdinal("entitle")];
prize.ardetail = (string)dtrPrize[dtrPrize.GetOrdinal("ardetail")];
prize.endetail = (string)dtrPrize[dtrPrize.GetOrdinal("endetail")];
prize.image = (string)dtrPrize[dtrPrize.GetOrdinal("image")];
prize.theme = (string)dtrPrize[dtrPrize.GetOrdinal("theme")];
prize.price = (int)dtrPrize[dtrPrize.GetOrdinal("price")];
prize.audience = (int)dtrPrize[dtrPrize.GetOrdinal("audience")];
prize.type = (byte)dtrPrize[dtrPrize.GetOrdinal("type")];
prize.status = (byte)dtrPrize[dtrPrize.GetOrdinal("status")];
prize.voucher = (string)dtrPrize[dtrPrize.GetOrdinal("voucher")];
prize.supplierid = (int)dtrPrize[dtrPrize.GetOrdinal("supplierid")];
prize.created = (DateTime)dtrPrize[dtrPrize.GetOrdinal("created")];
prize.updated = (DateTime)dtrPrize[dtrPrize.GetOrdinal("updated")];
return prize;
}
Healthy-ish; personally I'd say the unhealthy bit is the bit where it makes use of DataSet and DataAdapter, but that is perhaps just my personal bias.
Yes, you should dispose the adapter etc here (which is what the using does for you, obviously).
As a trivial pointless tidy, you can stack the usings - just makes it a little less verbose:
using (SqlConnection con = new SqlConnection(ConStr))
using (SqlCommand cmd = con.CreateCommand())
{
It will be enough to leave just the first using (the one on the Connection) because disposing the connection will dispose everything you need disposed.
However, there is no harm disposing everything, just a bit more code.
Related
I have a connection to a database set up like this to call a stored procedure. I am just wondering if this is the best way to do this.
I have two using statements one for the sqlConnection and one for the sqlCommand (which I am not really sure if its needed)
using (SqlConnection con1 = new SqlConnection(conString1))
{
using (SqlCommand cmd1 = new SqlCommand())
{
cmd1.Connection = con1;
cmd1.CommandType = CommandType.StoredProcedure;
cmd1.CommandText = "updateVendorEstNo";
cmd1.Parameters.AddWithValue("#plantNameNew", vPlantName.Value.ToString().Trim());
var result = cmd1.Parameters.Add("#result", SqlDbType.Int);
result.Direction = ParameterDirection.Output;
var resultDesc = cmd1.Parameters.Add("#resultDesc", SqlDbType.VarChar, 100);
resultDesc.Direction = ParameterDirection.Output;
con1.Open(); // open connection
cmd1.ExecuteNonQuery();
res = result.Value.ToString().Trim();
resDesc = resultDesc.Value.ToString().Trim();
}
}
My biggest question is when I am doing :
using (SqlCommand cmd1 = new SqlCommand())
Is it fine the way it is done right now.. or should it be more like,
using (SqlCommand cmd1 = new SqlCommand("updateVendorEstNo",con1))
I think that the way you have it is fine, because the using statement will ensure that the object is disposed of either way.
I'm using a C# function to query the DB based on SqlConnection, SqlCommand and SqlDataAdapter but performances are quite bad when I try to retrieve a huge number of rows (few millions).
using (SqlConnection mySqlConnection = new SqlConnection(connectionString))
using (SqlCommand mySqlCommand = new SqlCommand(query, mySqlConnection)){
mySqlConnection.Open();
DataSet myDataSet = new DataSet();
using (SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(mySqlCommand)) {
affectedRow = mySqlDataAdapter.Fill(myDataSet);
}
}
Is there a way to optimize this query for big data set?
Just as a comparison this piece of VBA code requires only 4-5 versus 20-22 seconds of above C#
Set cnt = New ADODB.Connection
Set rst = New ADODB.Recordset
Set cmd = New ADODB.Command
cnt.Open
Set rst = cnt.Execute(queryString)
Dim nDimArray As Variant
nDimArray = rst.GetRows
cnt.Close
Don't use DataSet. Use a SqlReader which is similar to what you were doing in VBA.
using (var mySqlConnection = new SqlConnection(connectionString))
using (var mySqlCommand = new SqlCommand(query, mySqlConnection))
{
mySqlConnection.Open();
var reader = mySqlCommand.ExecuteReader();
while (reader.Read())
{
}
reader.Close();
}
More information here: https://msdn.microsoft.com/en-us/library/9kcbe65k%28v=vs.110%29.aspx
You should be able to do this as well, which will clean up after yourself just in case you have exceptions thrown during the read:
using (var mySqlConnection = new SqlConnection(connectionString))
{
using (var mySqlCommand = new SqlCommand(query, mySqlConnection))
{
mySqlConnection.Open();
using (var reader = mySqlCommand.ExecuteReader())
{
while (reader.Read())
{
}
}
}
}
private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
// Call Close when done reading.
reader.Close();
}
}
How can I enhance the method above to accept any queryString? The problem is in the while. There's a fixed # of columns I can read. I want to be able to read any number of columns so that I can populate and return a DataSet. How can I do it?
You can do something along these lines:
private static void ReadOrderData(string connectionString,
string query, Action<SqlDataReader> action)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(query, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
action(reader);
}
// Call Close when done reading.
reader.Close();
}
}
You're really barking up the wrong tree. You shouldn't be using "methods that accept query strings". You should raise the level of abstraction by using Entity Framework or the like.
You will then not need to use the above code because it will not exist. Those who would have called that code will do something like this:
var orders = from o in ordersDAL.Orders
select new {o.OrderID, o.CustomerID};
foreach (var order in orders)
{
Console.WriteLine("{0}, {1}", order.OrderID, order.CustomerID);
}
Your code is badly designed in any case. Why in the world would you combine the fetching of the data with the use of it? That while loop should not be in that same method.
I would use something like the answer from obrok, but I would add the ability to use parameters.
Also, the SqlCommand and SqlDataReader both need to be within a using block:
private static void ReadOrderData(string connectionString,
string query, Action<SqlDataReader> action)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command =
new SqlCommand(query, connection)) {
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
// Call Read before accessing data.
while (reader.Read())
{
action(reader);
}
// No need to call Close when done reading.
// reader.Close();
} // End SqlDataReader
} // End SqlCommand
}
}
Use SqlDataAdapter:
private static DataSet ReadData(string connectionString, string queryString)
{
DataSet dataSet;
using (SqlConnection connection =
new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command =
new SqlCommand(queryString, connection);
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(dataSet);
}
return dataSet;
}
Or something like this.
How I'm doing this:
(basically idea is to use DataSet along with IDataAdapter)
DataSet ds = null;
List<SqlParameter> spParams = ...
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand(spName, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Clear();
command.Parameters.AddRange(spParams);
connection.Open();
IDbDataAdapter da = new SqlDataAdapter();
da.SelectCommand = command;
ds = new DataSet("rawData");
da.Fill(ds);
ds.Tables[0].TableName = "row";
foreach (DataColumn c in ds.Tables[0].Columns)
{
c.ColumnMapping = MappingType.Attribute;
}
}
}
// here is you have DataSet flled in by data so could delegate processing up to the particular DAL client
You said you tried EF, but did you try Dapper ? looks like a simple enough ORM that should work with your database (it's raw SQL), and will avoid you most of this mapping code. Dapper is used in StackOverflow so it cannot be too bad :)
Same suggestion as 'WorkerThread' but change the Method Signature to:
private static DataSet ReadOrderData(string connectionString, string queryString)
{
// do work
}
Drop the following line from 'WorkerThread' example:
string queryString = "SELECT OrderID, CustomerID FROM dbo.Orders;";
Once you have made these two changes to 'WorkerThread's' method it should be perfect for what you need.
Look at the DataTable.Load method. Or, for a finer level of control, check out the properties and methods on IDataReader, such as FieldCount, GetFieldType, and GetName.
Or what's the recommended way to read and write at the same time? Having two connections open is the only way? This is on a plain Windows Forms C# (.NET 3.5) app
using (SqlConnection conn = new SqlConnection(connStr)) {
SqlCommand cmdRead = new SqlCommand("select stools from foo", conn);
SqlDataReader rdr = cmdRead.ExecuteReader();
SqlCommand cmdWrite = new SqlCommand("insert into bar values (#beer)", conn);
SqlParameter beer = new SqlParameter("#beer", SqlDbType.Int);
cmdWrite.Parameters.Add(beer);
while(rdr.Read()) {
int stools = rdr.GetInt32(0);
cmdWrite.Parameters["#beer"].value = stools;
//Next line fails for an open data reader associated to the "command"
cmdWrite.ExecuteNonQuery();
}
rdr.Close()
}
This, OTOH, works, but looks ugly to me (besides of opening an extra connection)
using (SqlConnection connR = new SqlConnection(connStr)) {
using (SqlConnection connW = new SqlConnection(connStr)) {
SqlCommand cmdRead = new SqlCommand("select stools from foo", connR);
SqlDataReader rdr = cmdRead.ExecuteReader();
SqlCommand cmdWrite = new
SqlCommand("insert into bar values (#beer)", connW);
SqlParameter beer = new SqlParameter("#beer", SqlDbType.Int);
cmdWrite.Parameters.Add(beer);
while(rdr.Read()) {
int stools = rdr.GetInt32(0);
cmdWrite.Parameters["#beer"].value = stools;
cmdWrite.ExecuteNonQuery();
}
rdr.Close()
}
}
In this simple case, read all the stools, store them in a list, close the reader and then write them will work (as long as there aren't many stools in the database), but in more complex cases it starts being unwieldy and memory hungry, so that's also not desirable.
With SQL 2005 and later, you can use Multiple Active Result Sets (MARS):
http://msdn.microsoft.com/en-us/library/ms345109%28SQL.90%29.aspx
http://msdn.microsoft.com/en-us/library/ms131686.aspx
For this to work, you need to enable it in your connection string.
Why not having two methods: one for reading and one for writing, each using its own connection drawn from the connection pool.
Reading
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmdRead = new SqlCommand("select stools from foo", conn))
{
conn.Open();
using (SqlDataReader rdr = cmdRead.ExecuteReader())
{
while(rdr.Read())
{
int stools = rdr.GetInt32(0);
}
}
}
Writing
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmdWrite = new SqlCommand("insert into bar values (#beer)", conn))
{
conn.Open();
SqlParameter beer = new SqlParameter("#beer", SqlDbType.Int);
cmdWrite.Parameters.Add(beer);
cmdWrite.Parameters["#beer"].value = stools;
cmdWrite.ExecuteNonQuery();
}
And then just call them on different threads. This makes methods clearer and more specific.
You can't write while the DataReader is open, creating a second connection is needed in this situation, and IMHO it's not a design issue.
Well the obvious question is why are you doing this in code at all?
You're selecting from one table to write into another - that screams insert query to me.
Doubtless the real world example is more complex in which case you need two connections because datareaders work by holding the connection open 'til you're done and that's more or less 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;
}
}
}