how to fill DataTable from stored procedure - c#

I am trying to execute a stored procedure with parameters, and get the results into a DataTable; but after execution, I get this:
this is the screen
I can get IDataRecord but I can't IDataReader - why? What do I miss? Thanks
public DataTable ExecuteProcedureTest(string storedProcedure, Dictionary<Filter, object> filterValue)
{
var parameters = GetParams(filterValue);
var sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["testconn"].ConnectionString);
var cmd = sqlConnection.CreateCommand();
SqlDataReader reader;
IDataReader dataReader;
IDataRecord[] dataRecords;
var table = new DataTable();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedProcedure;
foreach (var item in parameters)
{
cmd.Parameters.Add(item);
}
try
{
sqlConnection.Open();
reader = cmd.ExecuteReader();
dataRecords = reader.OfType<IDataRecord>().ToArray();
table.Load(reader);
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConnection.Dispose();
}
//var count = dataRecords.Count();
return table;
}

If your goal is to fill a DataTable from a SqlCommand, use a SqlDataAdapter instead of a SqlDataReader. Here is a handy extension method:
public static class SqlExtensions
{
public static DataTable ExecuteDataTable(this SqlCommand command)
{
DataTable table = new DataTable();
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(command))
{
dataAdapter.Fill(table);
}
return table;
}
}
Then from your code you can use it like this:
try
{
sqlConnection.Open();
table = cmd.ExecuteDataTable();
}
finally
{
sqlConnection.Dispose();
}

Related

SqlHelper DataAdapter

I am using a SqlHelper class which has common methods for CRUD operations.
public static void Fill(DataSet dataSet, String procedureName)
{
SqlConnection oConnection = new SqlConnection(DBInterface.ConnectionString);
SqlCommand oCommand = new SqlCommand(procedureName, oConnection);
oCommand.CommandType = CommandType.StoredProcedure;
SqlDataAdapter oAdapter = new SqlDataAdapter();
oAdapter.SelectCommand = oCommand;
oConnection.Open();
using (SqlTransaction oTransaction = oConnection.BeginTransaction())
{
try
{
oAdapter.SelectCommand.Transaction = oTransaction;
oAdapter.Fill(dataSet);
oTransaction.Commit();
}
catch
{
oTransaction.Rollback();
throw;
}
finally
{
if (oConnection.State == ConnectionState.Open)
oConnection.Close();
oConnection.Dispose();
oAdapter.Dispose();
}
}
}
Now in my code, I am calling this method as,
private void BindCustomers()
{
DataSet dsCust = new DataSet();
SqlHelper.Fill(dsCust, "getCustomers");
--then I bind this dataset to datagridview
}
This all works fine. Now I want to update the data in the database. But I am confused how do I call DataAdatpaer.Update(dataset) here to update the changes made in datagridview into database. Is this possible here? Or I need to do it conventionally to find the updated row and call the ExecuteNonQuery function in the SqlHelper? Is there anything which can be done to use dataadapter.update(ds)
Thanks
You don't need to hide data adapter, or if for any reason you did so, you need to expose a method in your class to push updates to server.
Example
Public class SqlHelper
{
string commandText;
string connectionString;
public SqlHelper(string command, string connection)
{
commandText = command;
connectionString = connection;
}
public DataTable Select()
{
var table = new DataTable();
using (var adapter = new SqlDataAdapter(this.commandText, this.connectionString))
adapter.Fill(table)
return table;
}
public void Update(DataTable table)
{
using (var adapter = new SqlDataAdapter(this.commandText, this.connectionString))
{
var builder = new SqlCommandBuilder(adapter);
adapter.Update(table);
}
}
}
By calling this method in your code you can perform all crud operation select, update, delete and insert. you just need to pass connection string, procedure name and parameters list. Using this method you will retrieve data in the DataTable.if anyone want Dataset(Multiple Result) then just need to replace DataSet on the place of DataTable
SqlConnection conn = new SqlConnection("Your ConnectionString");
public DataTable ExecuteDataTable(string ProcedureName, SqlParameter[] _Param)
{
try
{
DataTable dataTable = new DataTable();
SqlCommand cmd = new SqlCommand(ProcedureName, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
if (_Param is not null)
{
for (int i = 0; i < _Param.Length; i++)
{
if (_Param[i].ParameterName is not null)
{
if (_Param[i].Value is not null)
{
cmd.Parameters.AddWithValue(_Param[i].ParameterName, _Param[i].Value);
}
else
{
cmd.Parameters.AddWithValue(_Param[i].ParameterName, DBNull.Value);
}
}
}
}
conn.Open();
SqlDataAdapter DA = new SqlDataAdapter(cmd);
DA.Fill(dataTable);
conn.Close();
return dataTable;
}
catch (Exception ex)
{
conn.Close();
throw;
}
}

Cannot implicitly convert type System.Data.SqlClient.SqlDataReader to Microsoft.ReportingServices.DataProcessing.IDataReader

I'm receiving the following error:
Cannot implicitly convert type 'System.Data.SqlClient.SqlDataReader'
to 'Microsoft.ReportingServices.DataProcessing.IDataReader'. An
explicit conversion exists (are you missing a cast?)
public IDataReader ExecuteReader(CommandBehavior behavior)
{
string query = "select * from Sales.Store";
SqlConnection readerconn = new SqlConnection("Data Source=localhost;Initial Catalog=AdventureWorks2000;Integrated Security=SSPI");
SqlCommand readercmd = new SqlCommand(query);
try
{
readerconn.Open();
readercmd = readerconn.CreateCommand();
readercmd.CommandText = query;
readercmd.CommandType = System.Data.CommandType.Text;
SqlDataReader TestReader = readercmd.ExecuteReader();
return TestReader; //Getting error at this line
readerconn.Close();
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
After modifying the code as per the Radin Gospodinov suggestion, i am not getting the error but i am unable to get the data into the dataset.Here is the modified code
public class MySqlDataReader:Microsoft.ReportingServices.DataProcessing.IDataReader
{
private System.Data.IDataReader sourceDataReader;
private System.Data.DataTable dt;
private System.Data.DataSet ds;
public MySqlDataReader(System.Data.IDataReader datareader)
{
this.sourceDataReader = datareader;
}
public MySqlDataReader(System.Data.DataTable dt)
{
// TODO: Complete member initialization
this.dt = dt;
}
public MySqlDataReader(System.Data.DataSet ds)
{
// TODO: Complete member initialization
this.ds = ds;
}
public IDataReader ExecuteReader(CommandBehavior behavior)
{
string query = "select * from Sales.Store";
SqlConnection readerconn = new SqlConnection("Data Source=IE11DT4RJC6Q1;Initial Catalog=AdventureWorks2000;Integrated Security=SSPI");
SqlCommand readercmd = new SqlCommand(query);
try
{
readerconn.Open();
readercmd = readerconn.CreateCommand();
readercmd.CommandText = query;
readercmd.CommandType = System.Data.CommandType.Text;
SqlDataReader TestReader = readercmd.ExecuteReader();
//TestReader.Close();
readerconn.Close();
//readerconn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(query,readerconn);
adapter.SelectCommand = readercmd;
System.Data.DataSet ds = new System.Data.DataSet();
adapter.Fill(ds, "result");
System.Data.DataTable dt = new System.Data.DataTable();
adapter.Fill(dt);
readerconn.Close();
return new MySqlDataReader(ds);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
SqlDataReader implements System.Data.IDataReader, so if you want to cast to IDataRader you should use public System.Data.IDataReader ExecuteReader(CommandBehavior behavior)
Also close the connection in finally block:
try
{
readerconn.Open()
}
catch{...}
finally
{
readerconn.Close();
}
If you want to use Microsoft.ReportingServices.DataProcessing.IDataReader then you should create a class that implements this and pass SqlDataReader to it:
public class MySqlDataReaderWrapper:
Microsoft.ReportingServices.DataProcessing.IDataReader
{
private System.Data.IDataReader sourceDataReader;
public MySqlDataReaderWrapper(System.Data.IDataReader dataReader)
{
this.sourceDataReader = dataReader.
}
// Implementation of methods of Microsoft.ReportingServices.DataProcessing.IDataReader
}
then in your method use return new MySqlDataReaderWrapper(testDataReader);
You have mixed namespaces. You're looking for System.Data.IDataReader:
using System.Data;
And add a reference to that assembly, and if not needed remove Microsoft.ReportingServices.DataProcessing.
Do you want a Microsoft.ReportingServices.DataProcessing.IDataReader, I'm assuming not, which suggests in your using statements you've got a reference to some of ReportingServices with its implementation of IDataReader.
So you could remove that reference, or fully qualify the IDataReader in the method i.e. public System.Data.IDataReader ExecuteReader(CommandBehavior behavior)

Repeated SQL Connections, should I create a class?

I'm still quite new to c# and I have multiple count queries on a single page. I don't need to use these queries anywhere else so have avoided creating a class. Still though, I can help but think there must be a more efficient approach but I was wondering what that would be!
Here's an example
private void cntUp() {
Dictionary<string, string> crd = getCredentials(Session["secure"].ToString());
Label UserUpcoming = frmDash.FindControl("lblUserReviewUp") as Label;
using (SqlConnection con = new SqlConnection(WebConfigurationManager.ConnectionStrings["CS"].ConnectionString))
{
con.Open();
try
{
using (SqlCommand countUpcoming = new SqlCommand("SELECT COUNT(*) FROM vw_dash_user_upcoming WHERE Employee_ID = #employee_id", con))
{
countUpcoming.Parameters.Add(new SqlParameter("employee_id", crd["employee_id"].ToString()));
SqlDataReader readerUpcoming = countUpcoming.ExecuteReader();
while (readerUpcoming.Read())
{
UserUpcoming.Text = readerUpcoming.GetInt32(0).ToString();
}
}
con.Close();
}
catch
{
Response.Redirect(this.ErrorPage);
}
}
}
The following Repository works with SQL Server.
Basically, you can either issue a regular or parameterized query.
You can pass in your parameters as all strings, or just as objects if you are going to use models with strongly typed numbers and dates.
You can take out the Release mode info if you don't use such a construct in developement, this is just to make it easier to switch between development and production databases without coding a change.
An example usage would be :
var updateStatement = "UPDATE OCCHistoryHeaders SET ValidatingUsername=#pUsername,ValidatingWorkstation=#pWorkstation,CurrentState = #pCurrentStatus,RejectionReason = #pRejectionReason, AutomatedValidation = '0' WHERE BatchId = #pBatchId";
var parameters = new Dictionary<string, object>
{
{"pUsername", Environment.UserName},
{"pWorkstation", Environment.MachineName},
{"pCurrentStatus", currentStatus},
{"pRejectionReason", rejectionReason},
{"pBatchId", batchId}
};
var absRowsUpdated = _occDb.ExecuteParameterizedNonQueryObjects(updateStatement, parameters);
Here is the class...
public class SomeRepository
{
private string _connectionString { get; set; }
private SqlConnection _sqlConnection { get; set; }
public SomeRepository()
{
switch (AppSettings.ReleaseMode)
{
case ReleaseMode.DEV:
_connectionString = "server=;database=;User Id=;Password=";
break;
case ReleaseMode.PRODUCTION:
_connectionString = "server=;database=;User Id=;Password=";
break;
}
}
public DataTable ExecuteQuery(string commandText)
{
var dataTable = new DataTable();
var _sqlConnection = new SqlConnection(_connectionString);
var cmd = new SqlCommand(commandText, _sqlConnection);
var da = new SqlDataAdapter(cmd);
try
{
_sqlConnection.Open();
da.Fill(dataTable);
}
catch (Exception ex)
{
var errorText = string.Format("Occ Repository ExecuteQuery Error : QueryString={0} :: Error={1}", commandText, ex.Message);
throw new Exception(errorText, ex);
}
finally
{
da.Dispose();
_sqlConnection.Dispose();
}
return dataTable;
}
public DataTable ExecuteParameterizedQuery(string commandText, Dictionary<string, string> parameters)
{
var dataTable = new DataTable();
var _sqlConnection = new SqlConnection(_connectionString);
var cmd = new SqlCommand(commandText, _sqlConnection);
var da = new SqlDataAdapter(cmd);
foreach (var entry in parameters)
{
cmd.Parameters.Add(entry.Key, entry.Value);
}
try
{
_sqlConnection.Open();
da.Fill(dataTable);
}
catch (Exception ex)
{
var errorText = string.Format("Occ Repository ExecuteQuery Error : QueryString={0} :: Error={1}", commandText, ex.Message);
throw new Exception(errorText, ex);
}
finally
{
da.Dispose();
_sqlConnection.Dispose();
}
return dataTable;
}
public DataTable ExecuteParameterizedQueryObjects(string commandText, Dictionary<string, object> parameters)
{
var dataTable = new DataTable();
var _sqlConnection = new SqlConnection(_connectionString);
var cmd = new SqlCommand(commandText, _sqlConnection);
var da = new SqlDataAdapter(cmd);
foreach (var entry in parameters)
{
cmd.Parameters.Add(entry.Key, entry.Value);
}
try
{
_sqlConnection.Open();
da.Fill(dataTable);
}
catch (Exception ex)
{
var errorText = string.Format("Occ Repository ExecuteQuery Error : QueryString={0} :: Error={1}", commandText, ex.Message);
throw new Exception(errorText, ex);
}
finally
{
da.Dispose();
_sqlConnection.Dispose();
}
return dataTable;
}
public int ExecuteNonQuery(string commandText)
{
var _sqlConnection = new SqlConnection(_connectionString);
var rowsAffected = 0;
try
{
var cmd = new SqlCommand(commandText, _sqlConnection);
_sqlConnection.Open();
rowsAffected = cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
var errorText = string.Format("Occ Repository ExecuteNonQuery Error : Command={0} :: Error={1}", commandText, ex.Message);
throw new Exception(errorText, ex);
}
finally
{
_sqlConnection.Dispose();
}
return rowsAffected;
}
public int ExecuteParameterizedNonQuery(string commandText, Dictionary<string, string> parameters)
{
var _sqlConnection = new SqlConnection(_connectionString);
var rowsAffected = 0;
var cmd = new SqlCommand(commandText, _sqlConnection);
foreach (var entry in parameters)
{
cmd.Parameters.Add(entry.Key, entry.Value);
}
try
{
_sqlConnection.Open();
rowsAffected = cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
var errorText = string.Format("Occ Repository ExecuteNonQuery Error : Command={0} :: Error={1}", commandText, ex.Message);
throw new Exception(errorText, ex);
}
finally
{
_sqlConnection.Dispose();
}
return rowsAffected;
}
public int ExecuteParameterizedNonQueryObjects(string commandText, Dictionary<string, object> parameters)
{
var _sqlConnection = new SqlConnection(_connectionString);
var rowsAffected = 0;
var cmd = new SqlCommand(commandText, _sqlConnection);
foreach (var entry in parameters)
{
cmd.Parameters.Add(entry.Key, entry.Value);
}
try
{
_sqlConnection.Open();
rowsAffected = cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
var errorText = string.Format("Occ Repository ExecuteNonQuery Error : Command={0} :: Error={1}", commandText, ex.Message);
throw new Exception(errorText, ex);
}
finally
{
_sqlConnection.Dispose();
}
return rowsAffected;
}
}
If you only do stuff 1 place, generally you'd make everything less readable. So I would probably avoid it. It's a common pattern, however, to you would do yourself a favor writing a utility class with some functions for this.
//requires query parameters to have names #0, #1 etc in string
public static List<object[]> Query(String query, params String[] parameters) //no injection check on this string, be careful.
{
using(SqlConnection conn = new SqlConnection(_CONN_STRING_))
{
conn.Open()
using(SqlCommand cmd = new SqlCommand(query, conn))
{
AddSqlParams(cmd, parameters);
return Query(cmd);
}
}
}
/// <summary>Generic SQL query. Requires open connection.</summary>
/// <param name="cmd">SqlCommand object with all necessary fields configured.</param>
/// <returns>A list of Object arrays (each array is one row).</returns>
private static List<Object[]> Query(SqlCommand cmd)
{
List<Object[]> results = new List<Object[]>();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Object[] row = new Object[rdr.VisibleFieldCount];
rdr.GetValues(row);
results.Add(row);
}
return results;
}
}
private static void AddSqlParams(SqlCommand cmd, params String[] sqlParams)
{
for (Int32 i = 0; i < sqlParams.Length; i++)
cmd.Parameters.AddWithValue("#" + i, (Object)sqlParams[i] ?? DBNull.Value);
}
Then use like
UserUpcoming.Text = Query("SELECT COUNT(*) FROM vw_dash_user_upcoming WHERE Employee_ID = #0", crd["employee_id"].ToString())[0][0];
I like to have a separate class for each table in the database. In your case I would have a separate class called vw_dash_user_upcoming, this would have a static method something like...
public static int CountEmployees(int employeeId) {
int returnValue;
// do database stuff here
return returnValue;
}
I usually create a folder and namespace in my project called "database" and all the database stuff goes in there. Nothing goes in there that's not database access. For me this really helps keep things organized, especially in a large project.
Personally I would create a separate class, simply because it's not a good idea to keep pieces of code with totally different responsibilities in one class. When divided into small classes code is easier to read and maintain.
Communication with database should be separated anyway to make it easier to switch between datasources (e.g. different database servers).

Loop Through Results from a Method

I have a method that calls a Proc ad returns the data. But I want to call the method and loop through all the results and use the results to plug into an Excel document. I can't seem to figure out how to loop through results coming from the method call. This is my method:
public DataView GetCoupons()
{
string connStr = ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString;
SqlConnection conn = new SqlConnection(connStr);
SqlCommand cmd = new SqlCommand("CPC_GetAllCoupons", conn);
cmd.CommandType = CommandType.StoredProcedure;
//make the dap & ds
SqlDataAdapter dap = new System.Data.SqlClient.SqlDataAdapter(cmd);
DataSet ds = new DataSet();
//open con
if (conn.State == ConnectionState.Closed)
conn.Open();
//get the data
dap.Fill(ds);
//close the conn and return
if (conn.State == ConnectionState.Open)
conn.Close();
return ds.Tables[0].DefaultView;
}
So I want to call that method and get all the results and loop through the results. How would I go about doing that?
Thanks!
When you fill a DataSet, you get a representation of all the result sets returned from the procedure call. To access individual result sets, you simply enumerate over the Tables collection like you are doing:
foreach(DataTable table in ds.Tables)
Then, you can enumerate through all the records in each dataset like so:
foreach(DataRow row in table.Rows)
And then get data from each record:
object something = row["SomeColumnName"];
DataView coupons = GetCoupons();
foreach (var row in coupons) {
// do something here
}
Something like this, should be enough
foreach (DataRowView rowView in dv) //where dv is your DataView
{
DataRow row = rowView.Row;
// Do your stuff here
}
Hope this helps.
I recommend encapsulating the data access in a repository which returns objects of a type which you require. E.g.
public class Coupon
{
public int Foo { get; set; }
public int Bar { get; set; }
}
public class CouponRepository
{
private readonly string connectionString;
public CouponRepository(string connectionString)
{
this.connectionString = connectionString;
}
public IEnumerable<Coupon> GetCoupons()
{
using(var conn = new SqlConnection(this.connectionString))
using(var cmd = new SqlCommand("CPC_GetAllCoupons", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return new Coupon
{
Foo = (int)reader["Foo"],
Bar = (int)reader["Bar"],
};
}
}
}
}
}
Usage:-
var connectionString = ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString;
var repo = new CouponRepository(connectionString);
foreach (var coupon in repo.GetCoupons())
{
// do something
}
You could use:
Dataview dv = GetCoupons();
foreach(Item item in dv.Table.Rows)
{
//...
}
But there is a lot of ways of doing this.

Programming Practice: Using ExecuteNonQuery with SqlDataAdapter

public DataTable UserUpdateTempSettings(int install_id, int install_map_id, string Setting_value,string LogFile)
{
SqlConnection oConnection = new SqlConnection(sConnectionString);
DataSet oDataset = new DataSet();
DataTable oDatatable = new DataTable();
SqlDataAdapter MyDataAdapter = new SqlDataAdapter();
try
{
oConnection.Open();
cmd = new SqlCommand("SP_HOTDOC_PRINTTEMPLATE_PERMISSION", oConnection);
cmd.Parameters.Add(new SqlParameter ("#INSTALL_ID", install_id));
cmd.Parameters.Add(new SqlParameter ("#INSTALL_MAP_ID", install_map_id));
cmd.Parameters.Add(new SqlParameter("#SETTING_VALUE", Setting_value));
if (LogFile != "")
{
cmd.Parameters.Add(new SqlParameter("#LOGFILE",LogFile));
}
cmd.CommandType = CommandType.StoredProcedure;
MyDataAdapter.SelectCommand = cmd;
cmd.ExecuteNonQuery();
MyDataAdapter.Fill(oDataset);
oDatatable = oDataset.Tables[0];
return oDatatable;
}
catch (Exception ex)
{
Utils.ShowError(ex.Message);
return oDatatable;
}
finally
{
if ((oConnection.State != ConnectionState.Closed) || (oConnection.State != ConnectionState.Broken))
{
oConnection.Close();
}
oDataset = null;
oDatatable = null;
oConnection.Dispose();
oConnection = null;
}
}
I have used ExecuteNonQuery above. Normally it's not used with SqlDataAdapter. If I do not use it, I get an error.
Is it bad programming practice to use ExecuteNonQuery with SqlDataAdapter?
Hi deepti you don't have to call executeNonQuery because the fill method of the dataadapter is taking care for you. Here is a small sample:
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace WindowsFormsApplication9
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
FillData();
}
void FillData()
{
// 1
// Open connection
using (SqlConnection c = new SqlConnection(
Properties.Settings.Default.DataConnectionString))
// 2
// Create new DataAdapter
using (SqlDataAdapter a = new SqlDataAdapter("SELECT * FROM EmployeeIDs", c))
{
// 3
// Use DataAdapter to fill DataTable
DataTable t = new DataTable();
a.Fill(t);
// 4
// Render data onto the screen
// dataGridView1.DataSource = t; // <-- From your designer
}
}
}
}
I don't think it's a matter of bad practice, it just doesn't make sense to do it in this situation. The fill method is used to populate the results from a select command. ExecuteNonQuery() is normally used when you're not retrieving data, and will only return the number of results.
You can find more about the fill method here

Categories