I get query result from SQL Server with dynamic number of columns with variable column names.
How can I transform the result from datareader into generic list List ?
public ? getItems(string orderId)
{
SqlConnection sqlConn = new SqlConnection(conn);
SqlCommand command = new SqlCommand();
SqlDataReader reader;
try
{
sqlConn.Open();
command.Connection = sqlConn;
command.CommandText = "usp_get_orders";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#Id", orderId)));
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
?
}
reader.Close();
}
catch (Exception exp)
{
}
finally
{
command.Dispose();
command1.Dispose();
sqlConn.Close();
sqlConn.Dispose();
}
return ?;
}
If you resulting object is completely dynamic you could use a dictionary instead of a strongly typed object. Or, if you want at least an object, go with dynamic objects. Either use the dynamic keyword and a List<dynamic> or use DynamicObject.
The difference to a dictionary is not that big though...
Something like this could do it:
SqlDataReader reader = command.ExecuteReader();
var listOfValues = new Dictionary<string, object>();
while (reader.Read())
{
for(int i = 0; i <reader.FieldCount;i++)
{
listOfValues.Add(reader.GetName(i), reader.GetValue(i));
}
}
Related
public ArrayList P2a(string sql)
{
ArrayList result = new ArrayList();
MySqlCommand cmd = new MySqlCommand();
MySqlConnection mysqlconnection = new MySqlConnection(xxx);
cmd.Connection = mysqlconnection;
cmd.CommandText = sql;
try
{
cmd.Connection.Open();
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Dictionary<string, object> dict = new Dictionary<string, object>();
for (int count = 0; (count <= (reader.FieldCount - 1)); count++)
{
dict.Add(reader.GetName(count), reader[count]);
}
result.Add(dict);
}
return result;
} catch {
return result;
} finally {
cmd.Connection.Close();
}
}
C# Visual Studio 2017 MySQL void return echo problem.
I want to make sure that I print out the result properly.
Example:
ArrayList query = P2a("select id,site,comment from sites");
MessageBox.Show(query[0]["site"].toString());
To use it this way.
Can you make the necessary corrections in the function?
I recommend as a start to change your catch block.
You should be throwing the exception, ideally with more information.
I also recommend that you have an optional list of MySqlParameters (defaults to null), while its not obvious here, since there are no parameters, more than likely you have SQL that is suspectible to SQL injection. In most cases, if you have a where block and its not null, you have done something incorrectly.
Your code does not cleanup the disposable objects, so using will take care of that.
Use generics instead as the list item will now be a dictionary.
public List<Dictionary<String, object>> P2a(string sql, MySqlParameter[] parameters)
{
List<Dictionary<String, object>> result = new List<Dictionary<String, object>>();
using (MySqlCommand cmd = new MySqlCommand())
{
using (MySqlConnection mysqlconnection = new MySqlConnection(xxx))
{
cmd.Connection = mysqlconnection;
cmd.CommandText = sql;
if (parameters != null)
{
foreach (MySqlParameter p in parameters)
{
cmd.Parameters.Add(p);
}
}
try
{
cmd.Connection.Open();
using (MySqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Dictionary<string, object> dict = new Dictionary<string, object>();
for (int count = 0; (count <= (reader.FieldCount - 1)); count++)
{
dict.Add(reader.GetName(count), reader[count]);
}
result.Add(dict);
}
}
return result;
}
catch (ApplicationException ex)
{
throw new ApplicationException("Error Executing " + sql, ex);
}
finally
{
cmd.Connection.Close();
}
}
}
}
I solved the problem myself
I wrote a new function
public DataTable query(string sql) {
DataTable table = new DataTable();
MySqlConnection connection = null;
MySqlDataReader reader = null;
try {
connection = new MySqlConnection(xxx);
connection.Open();
MySqlDataAdapter dataAdapter = new MySqlDataAdapter();
dataAdapter.SelectCommand = new MySqlCommand(sql, connection);
dataAdapter.Fill(table);
return table;
} catch {
return table;
} finally {
if (reader != null)
reader.Close();
if (connection != null)
connection.Close();
}}
use off
DataTable list = query("select * from tablename");
MessageBox.Show(list.Rows["rowsname"]["cellname"].ToString());
I have the following method
public static SqlDataReader MenuDataReader(string url)
{
using (SqlConnection con = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("spR_GetChildMenus", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#PageUrl", url);
cmd.Parameters.AddWithValue("#MenuId", ParameterDirection.Output);
cmd.Parameters.AddWithValue("#ParentId", ParameterDirection.Output);
cmd.Parameters.AddWithValue("#TitleText", ParameterDirection.Output);
cmd.Parameters.AddWithValue("#ExternalUrl", ParameterDirection.Output);
cmd.Parameters.AddWithValue("#FullUrl", ParameterDirection.Output);
cmd.Parameters.AddWithValue("#ChildCount", ParameterDirection.Output);
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
//return reader;
while (reader.Read())
{
return reader;
}
}
}
}
return null;
}
which im calling like this
SqlDataReader reader = MenuDataReader(url);
if (reader.HasRows)
{
while (reader.Read())
{ }}
however im getting the error message
Invalid attempt to call HasRows when reader is closed.
can anyone help me out
thanks
As seen in https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand(v=vs.110).aspx :
public static SqlDataReader ExecuteReader(String connectionString, String commandText,
CommandType commandType, params SqlParameter[] parameters) {
SqlConnection conn = new SqlConnection(connectionString);
using (SqlCommand cmd = new SqlCommand(commandText, conn)) {
cmd.CommandType = commandType;
cmd.Parameters.AddRange(parameters);
conn.Open();
// When using CommandBehavior.CloseConnection, the connection will be closed when the
// IDataReader is closed.
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
}
Do you really need the reader, or do you just need some way to iterate over the rows inside it? I suggest an iterator block. You can iterate over your rows inside the source method, and yield each row in turn to the caller.
There is a twist with this technique: because you're yielding the same object with each iteration, there are cases where this can cause a problem, and so you're best off also asking for a delegate to copy the contents of the row somewhere. I also like to abstract this to a generic method that can be used for any query, and use the same delegate technique to handle parameter data, like so:
private IEnumerable<T> GetRows<T>(string sql, Action<SqlParameterCollection> addParameters, Func<IDataRecord, T> copyRow)
{
using (var cn = new SqlConnection("Connection string here"))
using (var cmd = new SqlCommand(sql, cn)
{
cmd.CommandType = CommandType.StoredProcedure;
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return copyRow(rdr);
}
rdr.Close();
}
}
}
public IEnumerable<MenuItem> GetChildMenus(string url)
{
return GetRows<MenuItem>("spR_GetChildMenus", p =>
{
//these lines are copied from your question, but they're almost certainly wrong
p.AddWithValue("#PageUrl", url);
p.AddWithValue("#MenuId", ParameterDirection.Output);
p.AddWithValue("#ParentId", ParameterDirection.Output);
p.AddWithValue("#TitleText", ParameterDirection.Output);
p.AddWithValue("#ExternalUrl", ParameterDirection.Output);
p.AddWithValue("#FullUrl", ParameterDirection.Output);
p.AddWithValue("#ChildCount", ParameterDirection.Output);
}, r =>
{
return new MenuItem( ... );
}
}
I would not return the reader - the Dispose of your connection and command are closing the connection. I would instead return a representative model of your data.
When you return inside the using statement the code calls Dispose on the SqlConnection. This closes the DataReader, causing the error.
Triggered by a question under Danan's answer, here is a solution based on abstractions. Additionally, it uses good practices like using declarations, async programming (with cancellation tokens omitted for brevity), and proper object disposal (especially with regards to the connection).
// Example invocation
public async Task DemonstrateUsage()
{
var query = #"SELECT * FROM Order WHERE Id = #Id;";
var parameters = new Dictionary<string, object>()
{
["#Id"] = 1,
};
// Caller disposes the reader, which disposes the connection too
await using var reader = await this.ExecuteReader(this.CreateConnection, query, parameters);
while (await reader.ReadAsync())
Console.Log("And another row!");
}
// Concrete implementation of how we produce connections
private DbConnection CreateConnection()
{
return new SqlConnection("ConnectionString");
}
// Fully abstract solution
private async Task<DbDataReader> ExecuteReader(Func<DbConnection> connectionFactory,
string query, IReadOnlyDictionary<string, object> parameters,
CommandType commandType = CommandType.Text)
{
var connection = connectionFactory();
try
{
await using var command = connection.CreateCommand();
command.CommandType = commandType;
command.CommandText = query;
foreach (var pair in parameters)
{
var parameter = command.CreateParameter();
parameter.ParameterName = pair.Key;
parameter.Value = pair.Value;
command.Parameters.Add(parameter);
}
await connection.OpenAsync();
return await command.ExecuteReaderAsync(CommandBehavior.CloseConnection);
}
catch
{
// We have failed to return a disposable reader that can close the connection
// We must clean up by ourselves
await connection.DisposeAsync();
throw;
}
}
If I have a DbCommand defined to execute something like:
SELECT Column1 FROM Table1
What is the best way to generate a List<String> of the returned records?
No Linq etc. as I am using VS2005.
I think this is what you're looking for.
List<String> columnData = new List<String>();
using(SqlConnection connection = new SqlConnection("conn_string"))
{
connection.Open();
string query = "SELECT Column1 FROM Table1";
using(SqlCommand command = new SqlCommand(query, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
columnData.Add(reader.GetString(0));
}
}
}
}
Not tested, but this should work fine.
Loop through the Items and Add to the Collection. You can use the Add method
List<string>items=new List<string>();
using (var con= new SqlConnection("yourConnectionStringHere")
{
string qry="SELECT Column1 FROM Table1";
var cmd= new SqlCommand(qry, con);
cmd.CommandType = CommandType.Text;
con.Open();
using (SqlDataReader objReader = cmd.ExecuteReader())
{
if (objReader.HasRows)
{
while (objReader.Read())
{
//I would also check for DB.Null here before reading the value.
string item= objReader.GetString(objReader.GetOrdinal("Column1"));
items.Add(item);
}
}
}
}
Or a nested List (okay, the OP was for a single column and this is for multiple columns..):
//Base list is a list of fields, ie a data record
//Enclosing list is then a list of those records, ie the Result set
List<List<String>> ResultSet = new List<List<String>>();
using (SqlConnection connection =
new SqlConnection(connectionString))
{
// Create the Command and Parameter objects.
SqlCommand command = new SqlCommand(qString, connection);
// Create and execute the DataReader..
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
var rec = new List<string>();
for (int i = 0; i <= reader.FieldCount-1; i++) //The mathematical formula for reading the next fields must be <=
{
rec.Add(reader.GetString(i));
}
ResultSet.Add(rec);
}
}
If you would like to query all columns
List<Users> list_users = new List<Users>();
MySqlConnection cn = new MySqlConnection("connection");
MySqlCommand cm = new MySqlCommand("select * from users",cn);
try
{
cn.Open();
MySqlDataReader dr = cm.ExecuteReader();
while (dr.Read())
{
list_users.Add(new Users(dr));
}
}
catch { /* error */ }
finally { cn.Close(); }
The User's constructor would do all the "dr.GetString(i)"
Where the data returned is a string; you could cast to a different data type:
(from DataRow row in dataTable.Rows select row["columnName"].ToString()).ToList();
This version has the same purpose of #Dave Martin but it's cleaner, getting all column, and easy to manipulate the data if you wan't to put it on Email, View, etc.
List<string> ResultSet = new List<string>();
using (SqlConnection connection = DBUtils.GetDBConnection())
{
connection.Open();
string query = "SELECT * FROM DATABASE";
using (SqlCommand command = new SqlCommand(query, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var rec = new List<string>();
for (int i = 0; i <= reader.FieldCount - 1; i++)
{
rec.Add(reader.GetString(i));
}
string combined = string.Join("|", rec);
ResultSet.Add(combined);
}
}
}
}
I want to call the stored procedure using C#.
I would like to create a shorthand for the procedure call.
I still do not want to re-define the connection and open it.
How do I create a method - I still did not open connection to the database?
I use the following code:
SqlConnection conn = null;
SqlDataReader rdr = null;
conn = new SqlConnection("");
conn.Open();
SqlCommand cmd = new SqlCommand("Procedure", conn);
cmd.CommandType = CommandType.StoredProcedure;
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
}
I have no idea if I understand what you are asking or not, but do you mean something like:
public static SqlReader executeProcedure(SqlConnection oConn, string commandName, Dictionary<string, object> params)
{
SqlCommand comm = oConn.CreateCommand();
comm.CommandType = CommandType.StoredProcedure;
comm.CommandText = commandName;
if (params != null)
foreach(KeyValuePair<string, object> kvp in params)
comm.Parameters.Add(new SqlParameter(kvp.Key, kvp.Value));
return comm.ExecuteReader();
}
An example of use might be
Dictionary<string, object> paras = new Dictionary<string, object>();
paras.Add("user_name", "Timmy");
paras.Add("date", DateTime.Now);
SqlReader results = executeProcedure(oConn, "sp_add_user", paras);
while (results.Read())
{
//do something with the rows returned
}
results.Close();
FlyingStreudel's answer is good, but I've adapted that code to make this version that demonstrates best practices (links at the bottom.) You can also use Microsoft's Enterprise Library which will give you robust Data Access classes.
private string _connectionString = "yourconnectionstring"; // from web.config, or wherever you store it
public static SqlDataReader executeProcedure(string commandName,
Dictionary<string, object> params)
{
SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();
SqlCommand comm = conn.CreateCommand();
comm.CommandType = CommandType.StoredProcedure;
comm.CommandText = commandName;
if (params != null)
{
foreach(KeyValuePair<string, object> kvp in params)
comm.Parameters.Add(new SqlParameter(kvp.Key, kvp.Value));
}
return comm.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
}
used, like so:
Dictionary<string, object> paras = new Dictionary<string, object>();
paras.Add("user_name", "Timmy");
paras.Add("date", DateTime.Now);
using(SqlDataReader results = executeProcedure("sp_add_user", paras))
{
while (results.Read())
{
//do something with the rows returned
}
}
References:
How Microsoft use Connections in Enterprise Library
Keeping an SqlConnection open is 'foo bar'
Returning a data reader from a class
using (SqlConnection sqlConnection1 = new SqlConnection("Your Connection String")) {
using (SqlCommand cmd = new SqlCommand()) {
Int32 rowsAffected;
cmd.CommandText = "StoredProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConnection1;
sqlConnection1.Open();
rowsAffected = cmd.ExecuteNonQuery();
}}
If you're looking to reuse this kind of code, one possible solution is to wrap this kind of a method (one that executes a stored procedure and returns results) into a generic data access layer of your application. The variations you'd have to consider are for procedures not returning results, or expecting parameters.
You could, for example, wrap this shell code as an ExecuteProcedure() that expects a connection string back to the database.
There are myriad other ways to accomplish this kind of task, so you need to determine what would be the best option suited to your particular requirements.
You can wrap this code and take the procedure as a parameter. Something like this:
public SqlCommand GetData(string procedure)
{
var conn = new SqlConnection(connectionString);
var cmd = new SqlCommand(procedure, conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
return cmd;
}
The only problem with this method is that you are not properly disposing resources and are relying on the caller to do so.
I created the below method which i tested and does return the correct data. Where I am confused is what is the proper way to populate individual textboxes on a form with the results from this method?
Rather than using an objectdatasource and then binding a gridview to the objectdatasource that works but I need more freedom to customize the form.
public MemberDetails GetMemberDetail(int membershipgen)
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("usp_getmemberdetail", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int, 5));
cmd.Parameters["#MEMBERSHIPGEN"].Value = membershipgen;
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
reader.Read();
MemberDetails mem = new MemberDetails((int)reader["MEMBERSHIPGEN"], (string)reader["MEMBERSHIPID"], (string)reader["LASTNAME"],
(string)reader["FIRSTNAME"], (string)reader["SUFFIX"], (string)reader["MEMBERTYPESCODE"]);
reader.Close();
return mem;
}
catch (SqlException err)
{
throw new ApplicationException("Data error.");
}
finally
{
con.Close();
}
Something along the lines of:
var memberDetails = GetMemberDetail(12345);
textBox1.Text = memberDetails.Prop1;
textBox2.Text = memberDetails.Prop2;
...
Also I would refactor this method and make sure that I properly dispose disposable resources by wrapping them in using statements to avoid leaking unmanaged handles:
public MemberDetails GetMemberDetail(int membershipgen)
{
using (SqlConnection con = new SqlConnection(connectionString))
using (SqlCommand cmd = con.CreateCommand())
{
con.Open();
cmd.CommandText = "usp_getmemberdetail";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#MEMBERSHIPGEN", membershipgen);
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (reader.Read())
{
return new MemberDetails(
reader.GetInt32(reader.GetOrdinal("MEMBERSHIPGEN")),
reader.GetString(reader.GetOrdinal("MEMBERSHIPID")),
reader.GetString(reader.GetOrdinal("LASTNAME")),
reader.GetString(reader.GetOrdinal("FIRSTNAME")),
reader.GetString(reader.GetOrdinal("SUFFIX")),
reader.GetString(reader.GetOrdinal("MEMBERTYPESCODE"))
);
}
return null;
}
}
}
Get the MemberDetails;
var memberDetails = GetMemberDetail(1);
Populate the textbox;
TextBox.Text = memberDetails.Property;
Jawaid outside of the correct answers that were provided below I would also set SqlConnection con = null; and
SqlCommand cmd = null; outside the try and inside the try put the following
con = new SqlConnection(connectionString);
this way if there is an Error when doing cmd.Parameters.Add -- you can trap that exception
also dispose of the reader object
if (reader != null)
{
((IDisposable)reader).Dispose();
// somthing like that .. do the same for con and cmd objects or wrap them in a using() {}
}
cmd = new SqlCommand("usp_getmemberdetail", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int, 5));
cmd.Parameters["#MEMBERSHIPGEN"].Value = membershipgen;