adapter.Fill takes long time to run - c#

I have an ADO.NET code to make a server call with input parameters and return Dataset. I am using SqlDataAdapter to get data and assign it Dataset.
Here is my code
public DataSet GetRecordsWithParam(string spName, Dictionary<string, dynamic> ParaArr)
{
try
{
Connection();
DataSet ds = new DataSet();
cmd = new SqlCommand(spName, con);
cmd.CommandTimeout = 300;
cmd.CommandType = CommandType.StoredProcedure;
if ((ParaArr != null))
{
foreach (KeyValuePair<string, dynamic> kvp in ParaArr)
{
cmd.Parameters.Add(kvp.Key, kvp.Value);
}
}
if (con.State == ConnectionState.Open)
{
con.Close();
}
con.Open();
SqlDataAdapter myAdapter = new SqlDataAdapter(cmd);
myAdapter.Fill(ds);
return ds;
}
catch
{
throw;
}
finally
{
con.Close();
}
}
But it takes too much time to execute the line
myAdapter.Fill(ds);
There is no problem with my procedure. While assigning the data from myAdapter to ds using Fill it takes too much time. It is a global method I am using in my project, most of the time it works fine. But only when called from this method
private DataSet PayPeriodFromCountryCompany(string Country, string Company, string EmployeeType, string startDate, string EndDate, string Action)
{
Dictionary<string, object> Dic = new Dictionary<string, object>();
DBConnection DB = new DBConnection();
DataSet _dsPayPeriod = new DataSet();
string _strConnectionString = ConfigurationManager.ConnectionStrings["TimesheetconnString"].ConnectionString;
try
{
Dic.Add("#Country", Country);
Dic.Add("#Company", Company);
Dic.Add("#EmployeeType", EmployeeType);
Dic.Add("#StartDate", startDate);
Dic.Add("#EndDate", EndDate);
Dic.Add("#Action", Action);
_dsPayPeriod = DB.GetRecordsWithParam("TSTime_spTimesheetToCPS", Dic);
return _dsPayPeriod;
}
catch
{
throw;
}
}
loading problem. It takes 1-2 minute to execute the line ' myAdapter.Fill(ds); '.
Anyone point if there is any bug in the code. Thanks in Advance

Related

How to plug info from one stored procedure to another?

The way this works, a drop down list has Price Groups in it and I need to get that Price Group and plug it into the stored procedure (_getVendorDetails) to get the Vendor ID. Then I need to put the Vendor ID into another stored procedure (DIST_get_POShortagesaAndExtra_VendorIDNotes) to get notes about the Vendor ID.
protected void LoadExtraVendorIDNotes()
{
if (ddlVendorPriceGroup.SelectedValue == "NA")
return;
DataSet ds = getVendorIDNotesPONotifications(ddlVendorPriceGroup.SelectedValue);
if (ds.Tables[0].Rows.Count > 0)
{
DataTable dt = ds.Tables[0];
lblShipmentScrutinyLow.Visible = Convert.ToBoolean(dt.Rows[0]["ShipmentScrutiny"].ToString());
lblContainsKitsLow.Visible = Convert.ToBoolean(dt.Rows[0]["ContainsKits"].ToString());
lblShippingIssuesLow.Visible = Convert.ToBoolean(dt.Rows[0]["ShippingIssues"].ToString());
lblMeetingNotesLow.Text = Convert.ToString(dt.Rows[0]["MeetingNotes"].ToString());
lblMeetingNotesLow.Visible = Convert.ToString(dt.Rows[0]["MeetingNotes"].ToString()).Length > 0;
lblShipmentNotesLow.Text = Convert.ToString(dt.Rows[0]["ShippingNotes"].ToString());
lblShipmentNotesLow.Visible = Convert.ToString(dt.Rows[0]["ShippingNotes"].ToString()).Length > 0;
}
}
protected DataSet getVendorIDNotes(string VENDORID)
{
DataSet ds = new DataSet();
using (SqlConnection objConn = new SqlConnection(ConfigurationManager.AppSettings["MeyerConnectionString"]))
{
using (SqlDataAdapter dadapter = new SqlDataAdapter())
{
dadapter.SelectCommand = new SqlCommand("DIST_get_POShortagesaAndExtra_VendorIDNotes", objConn);
dadapter.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter vendorID = new SqlParameter("#VENDORID", VENDORID);
dadapter.SelectCommand.Parameters.Add(vendorID);
dadapter.Fill(ds);
}
}
return ds;
}
protected DataSet getVendorIDNotesPONotifications(string VENDORID)
{
DataSet ds = new DataSet();
using (SqlConnection objConn = new SqlConnection(ConfigurationManager.AppSettings["MeyerConnectionString"]))
{
using (SqlDataAdapter dadapter = new SqlDataAdapter())
{
dadapter.SelectCommand = new SqlCommand("_getVendorDetails", objConn);
dadapter.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter vendorID = new SqlParameter("#pricegroup", VENDORID);
dadapter.SelectCommand.Parameters.Add(vendorID);
dadapter.Fill(ds);
}
}
return ds;
}
Going by your example code it seems you are on the right track just that you need to invoke the getVendorIDNotes method from within the getVendorIDNotesPONotifications method and pass the vendorID to it.
The dataset returned from getVendorIDNotes is further returned from the calling method i.e. getVendorIDNotesPONotifications and hence the logic in LoadExtraVendorIDNotes should work as expected.

Store DataSet when ending looping in C#

I am using this code to fill DataSet
var ds = new DataSet();
List<string> list = new List<string>();
foreach (DataGridViewRow row in grdInterPOList.Rows)
{
DataGridViewCheckBoxCell chk = row.Cells[0] as DataGridViewCheckBoxCell;
if (Convert.ToBoolean(chk.Value) == true)
if (row.Cells.Count >= 2 && row.Cells[1].Value != null)
{
list.Add(row.Cells[1].Value.ToString());
}
}
foreach (var _PO_No in list)
{
ds= ShippingData_Export(_PO_No);
}
private DataSet ShippingData_Export(string X_PO_NO)
{
var ds = new DataSet();
var sqlConn = new SqlConnection(strConStr);
try
{
sqlConn.Open();
var cmd = new SqlCommand("proc_TBL_PO_M_ShippingExcel", sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#X_PO_NO", X_PO_NO);
var da = new SqlDataAdapter(cmd);
da.Fill(ds);
sqlConn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
return ds;
}
If we select more than 1 _PO_No , then we only get the last value after looping.
So my question is how to store DataSet value after end loop.
For example: If we select 2 _PO_No : GV01 and GV02 then after ending loop . We will get both 2 _PO_No not only the last one (GV02)
The problem in your code is:
foreach (var _PO_No in list)
{
ds = ShippingData_Export(_PO_No);
}
You are looping through your list and calling ShippingData_Export on each item in the list but you're then overwriting ds each time you call this method. This is why only the last value is stored.
If you want to have a single DataSet contain multiple results, you need to pass your DataSet to the ShippingData_Export method and add your results to it instead of creating a new one each time.
The result would be something like this:
foreach (var _PO_No in list)
{
ShippingData_Export(_PO_No, ds);
}
private void ShippingData_Export(string X_PO_NO, DataSet ds)
{
var sqlConn = new SqlConnection(strConStr);
try
{
sqlConn.Open();
var cmd = new SqlCommand("proc_TBL_PO_M_ShippingExcel", sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#X_PO_NO", X_PO_NO);
var da = new SqlDataAdapter(cmd);
da.Fill(ds);
sqlConn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
return ds;
}
A couple of other small issues with your code:
Add { } to the block after the line if (Convert.ToBoolean(chk.Value) == true). Get used to using { } explicitly to make your code more readable.
You are manually opening your SQL Connection but you don't close it when an exception happens. Look into the using statement to make sure you always close your connection even when things go wrong.

how to fill DataTable from stored procedure

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();
}

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;
}
}

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).

Categories