Don't close SQL connection until is has finished reading - c#

I have the below code, which works, but is only reading the top row of the DB and then terminating. The array should hold 3 pieces of data, but it only holds one.
I think this is because it is not looping.
How do you say for the code to carry on running until it has no more data to read?
SqlConnection conn1 = new SqlConnection(ssConnectionString);
conn1.Open();
SqlCommand command1 = conn1.CreateCommand();
command1.CommandText = "SELECT FeedURL FROM [dbo].[Feeds]";
rssFeeds.Add(command1.ExecuteScalar());
conn1.Close();

By default ExecuteScalar() will only ever return one value. You would need to create a DataReader, and then loop through the results using command1.ExecuteReader()

conn1.Open();
string query = "select feedurl from [dbo].[feeds]";
DataSet DS = new DataSet();
SqlDataAdapter adapt = new SqlDataAdapter(query,conn1);
adapt.Fill(DS);
if (DS != null)
{
if (DS.Tables[0].rows.Count > 0 )
{
foreach(DataRow DR in DS.Tables[0].Rows)
{
string temp = DR['columnname'];
}
}
{

You can just use ExecuteReader for your problem. In this example that I took from MSDN is consuming the connection with using statement because SqlConnection class has some unmanaged resources. If you have more questions about using and Finalizers also check here.
How to use ExecuteReader, you can check here:
static void HasRows(SqlConnection connection)
{
using (connection)
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM Categories;",
connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
reader.GetString(1));
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}

Try this:
//method to make this code reusable
//the returned data set is accessible even if the underlying connection is closed
//NB: that means data's held in memory; so beware of your resource usage
public DataSet ExecuteSQLToDataSet(string ssConnectionString, string query, string name)
{
DataSet ds = new DataSet("Tables");
using (SqlConnection conn1 = new SqlConnection(ssConnectionString))
{
conn1.Open();
SqlDataAdapter sda = new SqlDataAdapter(query, objConn);
sda.FillSchema(ds, SchemaType.Source, name);
sda.Fill(ds, name);
} //using statement will close and dispose the connection for you
return ds;
}
//example usage
DataSet ds = ExecuteSQLToDataSet(ssConnectionString, "SELECT FeedURL FROM [dbo].[Feeds]", "Feeds"); //nb: name doesn't have to match table name; you can call it what you like; naming is useful if you wanted to add other result sets to the same data set
//DataTable tblFeeds = ds.Tables["Feeds"]; //if you want to access the above query by name
foreach (DataTable tbl in ds.Tables)
{
foreach (DataRow dr in tbl.Rows) //tblFeeds.Rows if you did that instead of looping through all tables
{
//Console.WriteLine(dr["FeedURL"].ToString()); //you can specify a named column
Console.WriteLine(dr[0].ToString()); //or just use the index
}
}
Console.WriteLine("Done");
Console.ReadLine();
More Detail: http://support.microsoft.com/kb/314145

Related

How do I clone data from a SqlDataReader keeping all Columns and Rows in a way that can be exported through a method?

I'm currently trying to create a method that allows me to run any query on a connectionstring, and get the results returned in a readable way for my ASP.Net website. Because I need access to all rows and columns a query might need, I cannot simply return a string or an array of strings. If I return a SqlDataReader, I'm not able to read the data outside of the method, because the connection is closed.
This is what my method currently looks like.
private SqlDataReader QueryConnectionString (string query)
{
// New SQL Connection
SqlConnection cnn;
// New Connection String
string connetionString = ConfigurationManager.ConnectionStrings["ConnectionStringName"].ConnectionString;
// Instantiate the SQL Connection with Connection String
cnn = new SqlConnection(connetionString);
// Create a SqlCommand with the input Query
SqlCommand command = new SqlCommand(query, cnn);
cnn.Open();
// Create a SqlDataReader and tie it to the query
SqlDataReader reader = command.ExecuteReader();
cnn.Close();
return reader;
}
In my other methods I would have something like this
SqlDataReader reader = QueryConnectionString("SELECT * FROM tTable");
lblOutput.Text = reader.GetString(0);
But doing so gives me the error
System.InvalidOperationException: 'Invalid attempt to call
CheckDataIsReady when reader is closed.'
I realize that returning a SqlDataReader is not an option. What can I return the data as so that other methods can read the data?
You can return a DataTable which will preserve the columns and rows of the query. Instead of a SqlDataReader consider using SqlDataAdapter. One advantage is that the Fill method of SqlDataAdapter will open and close the connection for you.
var dt = new DataTable();
using (var da = new SqlDataAdapter(query, cnn))
{
da.Fill(dt);
}
Your full method might look something like this:
private DataTable GetData(string query)
{
// New Connection String
string connetionString = ConfigurationManager.ConnectionStrings["ConnectionStringName"].ConnectionString;
// Instantiate the SQL Connection with Connection String
SqlConnection cnn = new SqlConnection(connetionString);
// declare datatable
var dt = new DataTable();
// create SqlDataAdapter
using (var da = new SqlDataAdapter(query, cnn))
{
// fill datatable
da.Fill(dt);
}
return dt;
}
You will then need to read from the DataTable. See my answer here for a method to read data. The method in the linked answer uses the Field<T> extension from DataSetExtensions to extract data. Please see example below:
// get data from your method
DataTable table = GetData("select * from MyTable");
// iterate over the rows of the datatable
foreach (var row in table.AsEnumerable()) // AsEnumerable() returns IEnumerable<DataRow>
{
var id = row.Field<int>("ID"); // int
var name = row.Field<string>("Name"); // string
var orderValue = row.Field<decimal>("OrderValue"); // decimal
var interestRate = row.Field<double>("InterestRate"); // double
var isActive = row.Field<bool>("Active"); // bool
var orderDate = row.Field<DateTime>("OrderDate"); // DateTime
}
To check if a DataTable is null/ empty see this answer. It can be as simple as:
if(table?.Rows?.Count() > 0) { ... }
You can try this
IEnumerable<IDataRecord> GetRecords()
{
using(var connection = new SqlConnection(#"..."))
{
connection.Open();
using(var command = new SqlCommand(#"...", connection);
{
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
// your code here.
yield return reader;
}
}
}
}
}

Retrieve table names

I'm trying to retrieve the names of the table from the local database I'm using.
This the code I've tried but it never goes through the foreach loop:
public void GetColumnNames()
{
SqlConnection con;
SqlDataAdapter adapter = new SqlDataAdapter();
DataSet ds = new DataSet();
con = new SqlConnection(Properties.Settings.Default.AlhusainSoundDBConnectionString);
List<string> colns = new List<string>();
try
{
con.Open();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
foreach (DataTable dt in ds.Tables)
{
colns.Add(dt.TableName);
Console.WriteLine(dt.TableName);
}
}
So could anyone please suggest me how to do that correctly
Regards
To get table names you need to use INFORMATION_SCHEMA
USE <your_database_name>
GO
SELECT * FROM INFORMATION_SCHEMA.TABLES
You haven't done anything except open a connection to the database. Your dataset has not been populated with any data. My approach would be to use a SqlCommand object to execute the following SQL Statement and populate a SqlDataReader
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
So, the C# code might look something like this:
string sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";
using (SqlConnection con = new SqlConnection(Properties.Settings.Default.AlhusainSoundDBConnectionString))
using (SqlCommand cmd = new SqlCommand(sql, con))
{
con.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
// do something with each table
string tableName= dr["TABLE_NAME"].ToString();
// OR
// string tableName = dr[0].ToString();
// OR
// string tableName = dr.GetString(0);
}
}
}

How to display a specific data from a database into textbox?

public DataTable DisplayHolidays(int empid)
{
DataTable dt = new DataTable();
SqlCommand cmd = new SqlCommand("ComputeHoliday", myCon);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#EmployeeID", SqlDbType.Int).Value = empid;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dt);
SqlDataReader rd = cmd.ExecuteReader();
while (rd.Read())
{
temp2 = rd[0].ToString();
}
return dt;
}
This is my code I had a problem/error with this part. This code is in the class not in the form load. It cannot display the data in the textbox. I'm using temp2 to store the data in the selected row but it's not yet working.
I assume that you're getting an exception at cmd.ExecuteReader().
Note that you must open the connection before you can use the command in cmd.ExecuteReader(). DataAdapter.Fill does not need an open connection, the dataadapter will open/close it implicitly.
MSDN:
The connection object associated with the SELECT statement must be
valid, but it does not need to be open. If the connection is closed
before Fill is called, it is opened to retrieve data, then closed. If
the connection is open before Fill is called, it remains open.
Why do you use DataAdapter.Fill(DataTable) and also Command.ExecuteReader? You need just one way to get the data. If you already have filled a table:
If(dt.Rows.Count > 0)
{
temp2 = dt.Rows[0].Field<string>(0);
}
If you don't use the DataAdapter and the DataTable but only the reader:
using(SqlDataReader rd = cmd.ExecuteReader())
{
if(rd.HasRows)
{
rd.Read();
temp2 = rd.GetString(0);
}
}

SQL timeout exception

Why do I get this exception in my code? I restarted the server, changed ports, etc, but nothing is working.
What's wrong?
DataTable dt = new DataTable();
SqlConnection con = new SqlConnection("server=localhost;user=armin;password=root;");
con.Open();
SqlCommand result = new SqlCommand(
"SELECT userid FROM KDDData.dbo.userprofile order by userid", con);
SqlDataReader reader = result.ExecuteReader();
dt.Load(reader);
List<string> userids = new List<string>(dt.Rows.Count);
foreach (DataRow item in dt.Rows)
{
userids.Add(item.ItemArray[0].ToString().Trim());
}
con.Close();
con = new SqlConnection("server=localhost;user=armin;password=root;");
con.Open();
foreach (string user in userids)
{
DataTable temp = new DataTable();
SqlCommand result1 = new SqlCommand(
"select itemid from KDDTrain.dbo.train where userid=" + user, con);
SqlDataReader reader1 = result1.ExecuteReader();
if (!reader1.HasRows)
{
continue;
}
temp.Load(reader1);
}
The first query works fine, but the second doesn't. As you can see I even use some other SqlConnection but it still doesn't work.
Note:The database i'm working with has atleast 100 milion records,thought may be this would be a problem.
Something doesn't look right in your connection string
I always seen "server=localhost; user=armin;password=root" in connections strings for MySql not for SqlServer where instead I will use "Data Source=(LOCAL);Integrated Security=SSPI" or the INSTANCE name of SqlServer. Are you sure that the first query works?.
However I think you should use the appropriate using statement
DataTable dt = new DataTable();
using(SqlConnection con = new SqlConnection("server=localhost;user=armin;password=root;"))
{
using(SqlCommand result = new SqlCommand(
"SELECT userid FROM KDDData.dbo.userprofile order by userid", con))
{
con.Open();
using(SqlDataReader reader = result.ExecuteReader())
{
dt.Load(reader);
List<string> userids = new List<string>(dt.Rows.Count);
foreach (DataRow item in dt.Rows)
{
userids.Add(item.ItemArray[0].ToString().Trim());
}
}
DataTable temp = new DataTable();
foreach (string user in userids)
{
using(SqlCommand result1 = new SqlCommand(
"select itemid from KDDTrain.dbo.train where userid=" + user, con))
{
using(SqlDataReader reader1 = result1.ExecuteReader())
{
if (!reader1.HasRows) continue;
temp.Load(reader1);
}
}
}
}
Please insert this line
result1.CommandTimeout = 0;
befor this line in the second query
SqlDataReader reader1 = result1.ExecuteReader();
Dispose your reader after:
foreach (DataRow item in dt.Rows)
{
userids.Add(item.ItemArray[0].ToString().Trim());
}
...and also close the connection after temp.Load(reader1). Also close the reader1.
Instead of all this... the clean way is to use USING for initializng the readers and connection. :)

Read SQL Table into C# DataTable

I've read a lot of posts about inserting a DataTable into a SQL table, but is there an easy way to pull a SQL table into a .NET DataTable?
Here, give this a shot (this is just a pseudocode)
using System;
using System.Data;
using System.Data.SqlClient;
public class PullDataTest
{
// your data table
private DataTable dataTable = new DataTable();
public PullDataTest()
{
}
// your method to pull data from database to datatable
public void PullData()
{
string connString = #"your connection string here";
string query = "select * from table";
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
conn.Close();
da.Dispose();
}
}
var table = new DataTable();
using (var da = new SqlDataAdapter("SELECT * FROM mytable", "connection string"))
{
da.Fill(table);
}
Lots of ways.
Use ADO.Net and use fill on the data adapter to get a DataTable:
using (SqlDataAdapter dataAdapter
= new SqlDataAdapter ("SELECT blah FROM blahblah ", sqlConn))
{
// create the DataSet
DataSet dataSet = new DataSet();
// fill the DataSet using our DataAdapter
dataAdapter.Fill (dataSet);
}
You can then get the data table out of the dataset.
Note in the upvoted answer dataset isn't used, (It appeared after my answer)
It does
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
Which is preferable to mine.
I would strongly recommend looking at entity framework though ... using datatables and datasets isn't a great idea. There is no type safety on them which means debugging can only be done at run time. With strongly typed collections (that you can get from using LINQ2SQL or entity framework) your life will be a lot easier.
Edit: Perhaps I wasn't clear: Datatables = good, datasets = evil. If you are using ADO.Net then you can use both of these technologies (EF, linq2sql, dapper, nhibernate, orm of the month) as they generally sit on top of ado.net. The advantage you get is that you can update your model far easier as your schema changes provided you have the right level of abstraction by levering code generation.
The ado.net adapter uses providers that expose the type info of the database, for instance by default it uses a sql server provider, you can also plug in - for instance - devart postgress provider and still get access to the type info which will then allow you to as above use your orm of choice (almost painlessly - there are a few quirks) - i believe Microsoft also provide an oracle provider. The ENTIRE purpose of this is to abstract away from the database implementation where possible.
Vendor independent version, solely relies on ADO.NET interfaces; 2 ways:
public DataTable Read1<T>(string query) where T : IDbConnection, new()
{
using (var conn = new T())
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = query;
cmd.Connection.ConnectionString = _connectionString;
cmd.Connection.Open();
var table = new DataTable();
table.Load(cmd.ExecuteReader());
return table;
}
}
}
public DataTable Read2<S, T>(string query) where S : IDbConnection, new()
where T : IDbDataAdapter, IDisposable, new()
{
using (var conn = new S())
{
using (var da = new T())
{
using (da.SelectCommand = conn.CreateCommand())
{
da.SelectCommand.CommandText = query;
da.SelectCommand.Connection.ConnectionString = _connectionString;
DataSet ds = new DataSet(); //conn is opened by dataadapter
da.Fill(ds);
return ds.Tables[0];
}
}
}
}
I did some performance testing, and the second approach always outperformed the first.
Stopwatch sw = Stopwatch.StartNew();
DataTable dt = null;
for (int i = 0; i < 100; i++)
{
dt = Read1<MySqlConnection>(query); // ~9800ms
dt = Read2<MySqlConnection, MySqlDataAdapter>(query); // ~2300ms
dt = Read1<SQLiteConnection>(query); // ~4000ms
dt = Read2<SQLiteConnection, SQLiteDataAdapter>(query); // ~2000ms
dt = Read1<SqlCeConnection>(query); // ~5700ms
dt = Read2<SqlCeConnection, SqlCeDataAdapter>(query); // ~5700ms
dt = Read1<SqlConnection>(query); // ~850ms
dt = Read2<SqlConnection, SqlDataAdapter>(query); // ~600ms
dt = Read1<VistaDBConnection>(query); // ~3900ms
dt = Read2<VistaDBConnection, VistaDBDataAdapter>(query); // ~3700ms
}
sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());
Read1 looks better on eyes, but data adapter performs better (not to confuse that one db outperformed the other, the queries were all different). The difference between the two depended on query though. The reason could be that Load requires various constraints to be checked row by row from the documentation when adding rows (its a method on DataTable) while Fill is on DataAdapters which were designed just for that - fast creation of DataTables.
Centerlized Model: You can use it from any where!
You just need to call Below Format From your function to this class
DataSet ds = new DataSet();
SqlParameter[] p = new SqlParameter[1];
string Query = "Describe Query Information/either sp, text or TableDirect";
DbConnectionHelper dbh = new DbConnectionHelper ();
ds = dbh. DBConnection("Here you use your Table Name", p , string Query, CommandType.StoredProcedure);
That's it. it's perfect method.
public class DbConnectionHelper {
public DataSet DBConnection(string TableName, SqlParameter[] p, string Query, CommandType cmdText) {
string connString = # "your connection string here";
//Object Declaration
DataSet ds = new DataSet();
SqlConnection con = new SqlConnection();
SqlCommand cmd = new SqlCommand();
SqlDataAdapter sda = new SqlDataAdapter();
try {
//Get Connection string and Make Connection
con.ConnectionString = connString; //Get the Connection String
if (con.State == ConnectionState.Closed) {
con.Open(); //Connection Open
}
if (cmdText == CommandType.StoredProcedure) //Type : Stored Procedure
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = Query;
if (p.Length > 0) // If Any parameter is there means, we need to add.
{
for (int i = 0; i < p.Length; i++) {
cmd.Parameters.Add(p[i]);
}
}
}
if (cmdText == CommandType.Text) // Type : Text
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = Query;
}
if (cmdText == CommandType.TableDirect) //Type: Table Direct
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = Query;
}
cmd.Connection = con; //Get Connection in Command
sda.SelectCommand = cmd; // Select Command From Command to SqlDataAdaptor
sda.Fill(ds, TableName); // Execute Query and Get Result into DataSet
con.Close(); //Connection Close
} catch (Exception ex) {
throw ex; //Here you need to handle Exception
}
return ds;
}
}

Categories

Resources