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.
Related
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();
}
Here is my code:
private List<string> readFromDatabase()
{
SQLiteConnection m_dbConnection = new SQLiteConnection("Data Source=" + fileName + ".sqlite;Version=3;");
string sql = "SELECT * FROM formsData";
m_dbConnection.Open();
SQLiteDataReader dr = DatabaseHandler.ExecuteCommandReader(sql, m_dbConnection);
List<string> tempList = new List<string>();
if(dr.HasRows)
{
while (dr.Read())
{
tempList.Add(Convert.ToString(dr["fieldName"]));
tempList.Add(Convert.ToString(dr["dataType"]));
tempList.Add(Convert.ToString(dr["numberOfCharacters"]));
}
}
return tempList;
}
I am trying to make it add each value from the database into the list, however it is only adding the last value found, from the final column. Does anyone know how to solve this? Thank you...
you can use a DataTable instead of DataReader (even though its possible to achieve the same result with DataReader).
please check that discussion: Why is DataTable faster than DataReader.
also its a better practice to use Try-Catchsteatments when connecting to databases.
here is an example (based on your code) how to achieve that task usingDataTable:
private List<string> readFromDatabase()
{
DataTable dt = PullData();
List<string> tempList = new List<string>();
if (dt != null & dt.Rows.Count >0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
tempList.Add(Convert.ToString(dt.Rows[i]["fieldName"]));
tempList.Add(Convert.ToString(dt.Rows[i]["dataType"]));
tempList.Add(Convert.ToString(dt.Rows[i]["numberOfCharacters"]));
}
}
return tempList;
}
public DataTable PullData()
{
try
{
string connString = #"your connection string here";
string query = "select * from table";
DataTable dataTable = new DataTable();
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();
return dataTable;
}
catch (Exception ex)
{
return null;
}
}
Make sure your record set is positioned at the first row before you start iterating through it.
Also, do you really want to take all the rows of the table and have them as sequential values in a list? Perhaps adding them to class might make more sense.
public class Record{
public string Name { get; set; }
public string Type { get; set; }
public int Size { get; set; }
}
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;
}
}
I have a method which populates my ComboBox from a DataTable:
public string populateCompanyTransSellingEntityLookUp(ref System.Windows.Forms.ComboBox Combo, string Id, Contract Contract)
{
SqlCommand _comm = new SqlCommand();
_comm.Parameters.AddWithValue("#id", Id);
_comm.CommandText = "SELECT [name] FROM dbo.fnGetList(#id) ORDER BY [name]; ";
_comm.Connection = _conn;
_comm.CommandTimeout = _command_timeout;
DataTable dt = new DataTable();
try
{
SqlDataReader myReader = _comm.ExecuteReader();
dt.Load(myReader);
Combo.DataSource = dt;
Combo.DisplayMember = "name";
foreach (DataRow dr in dt.Rows)
{
if (dr["name"].ToString() == Contract.Company_Name.ToString())
{
Combo.Text = dr["company_int_name"].ToString();
}
}
}
catch
{
MessageBox.Show("Unable to populate Company Name LookUp");
}
return "";
}
I'm passing my saved value Contract.Company_Name into the forEach loop to find my required SelectedItem from the DataTable. The ComboBox is populated with my DataTable values from Combo.Datasource =dt; but my selected item isn't being set. The code compiles without exception. If I remove Datasource = dt;, theSelectedItemis set no problem. Why is theDatasourceoverriding mySelectedItem` and is there something I've missed with my binding?
Thanks all
At first you have to set the valueMember for sure. Then you can set the selectedValue Property instead of SelectedItem. The Item is one datasource record. So in your case it would be SelectedItem = dr! But iam not sure this is working.
Try this:
Combo.SelectedItem = dr;
I would suggest to use SelectedValue, then you don't need to loop through values "manually".
Also you don't need to use "heavy-weight" DataTable where you need just a collection of string values.
private IEnumerable<string> LoadNames(string id)
{
var query = "SELECT [name] FROM dbo.fnGetList(#id) ORDER BY [name]";
using (var connection = new SqlConnection("connectionString")
using (var command = new SqlCommand(query, connection)
{
// 36 is the size of the VarChar column in database(use your value)
command.Parameters.Add("#id", SqlDbType.VarChar, 36).Value = id;
connection.Open();
using (var reader = command.ExecuteReader())
{
var names = new List<string>();
while(reader.Read())
{
names.Add(reader.GetString(0));
}
return names;
}
}
}
public void Populate(ComboBox combobox, string id, Contract contract)
{
combobox.DataSource = LoadNames(id);
combobox.SelectedValue = contract.Copmpany_Name.ToString();
}
Few things to notice:
Dispose all objects which dealing with external resources (SqlConnection, SqlCommand and SqlDataReader)
Create SqlParameter with precise information about the type, for strings is important to provide size of the column in database. This information will improve SQL query performance on server side.
Don't pass combobox as a reference, populate method does not create new instance but only consume the given ComboBox instance.
Thank you for the help, I edited the code given that my problem was much more trivial.
public string populate_comboBox(ref System.Windows.Forms.ComboBox Combo)
{
SqlCommand _comm = new SqlCommand();
//edited for a simple one column sql query
_comm.CommandText ="SELECT [Column] FROM dbo.SQL_Table ORDER BY [Column];";
//MUST open sql connection to DB
SqlConnection conn = new SqlConnection(global_DB_String_Connection);
conn.Open();
_comm.Connection = conn;
DataTable dt = new DataTable();
try
{
SqlDataReader myReader = _comm.ExecuteReader();
dt.Load(myReader);
Combo.DataSource = dt;
Combo.DisplayMember = "ColumnName";
foreach (DataRow dr in dt.Rows)
{
//populates the combo box with query results
Combo.Text = dr["ColumnName"].ToString();
}
}
catch
{
Console.WriteLine("ComboBox Populate method has failed! ");
}
conn.Close();
return "";
}
I am using generic list to store the data that comes by querying the databse.I uses List of classes actually for multiple rows.
But my problem is my classes have almost more than 20 properties and most of the time i uses only its 2 or 3 properties.
So I want to know that what is the best way to keep the data coming from database.
Below is my code
List<ImageGalleryCollection> tempList = new List<ImageGalleryCollection1>();
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList, connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
tempList.Add(igc);
}
data.Close();
connection.Close();
return tempList;
In ImageGalleryCollection I have more that 20 properties and above i only uses two properties.I think it is very inefficient
Can you how your base class implementation? You can create another class with the most using attributes and use an object of that class inside your class.
IEnumerable<ImageGalleryCollection> GetImageGalleryCollection()
{
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList,connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
yield return igc;
}
data.Close();
connection.Close();
}
I would like to suggest you to write a extension method for SqlDataReader and make use of the method in linq to fetch required columns from the returned rows of reader.
Extension method:
public static class DataReaderExtension
{
public static IEnumerable<Object[]> DataRecord(this System.Data.IDataReader source)
{
if (source == null)
throw new ArgumentNullException("source");
while (source.Read())
{
Object[] row = new Object[source.FieldCount];
source.GetValues(row);
yield return row;
}
}
}
using it in linq:
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("Select * from tblUser"))
{
cmd.CommandType = CommandType.Text;
cmd.Connection = cn;
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
var result = (from row in dr.DataRecord()
select new
{
UserId = row[0],
UserName = row[1]
}).ToList();
}
}
}
This result list has only the required properties you select and helps to reduce the consumption of memory for unwanted properties.