C# Winform - OracleDataAdapter not persisting the records immediately - c#

I am making changes in an old C# WinForm application.
In the code below, when the DoWork() function is invoked. It calls the two functions PerformOperation1 and PerformOperation2. Both functions have a very similar body. The only difference is that they both update different fields of the same database table.
Finally, when they both have performed their job, we fetch the records using an OracleDataAdapter.
The count of the rows returned is 0 in the code. However, if I execute the query on the database straight away, it returns some rows. This means somehow the records updated by the PerformOperation2 are not pushed to the database at the time when we call the Fill on the DataAapter.
public void DoWork()
{
PerformOperation1();
PerformOperation2();
var sql = "select * from results where result_id = 1 and is_valid = 'Y'";
var table = new DataTable();
var data = new OracleDataAdapter(new OracleCommand(sql, base.Connection));
data.Fill(table);
var count = data.Rows.Count; //It returns 0 But when execute the same query on database, it returns rows.
}
public void PerformOperation1()
{
string sql = "select seq_1, product, count_1, count_2 from results where result_id = 1";
string updateSQL = "update results set count_1 = :count_1, count_2 = :count_2 WHERE seq_1 = :seq_1";
var selectCmd = new OracleCommand(sql, base.Connection);
selectCmd.CommandType = CommandType.Text;
var adapter = new OracleDataAdapter(lCmd);
adapter.UpdateCommand = new OracleCommand(updateSQL, base.Connection);
adapter.UpdateCommand.CommandType = CommandType.Text;
AddCommandParameter(adapter.UpdateCommand, ":count_1", DbType.Double, 11, "count_1");
AddCommandParameter(adapter.UpdateCommand, ":count_2", DbType.Double, 11, "count_2");
var data = new DataSet();
adapter.Fill(data);
foreach (var row in data.Tables[0].Rows)
{
row["count_1"] = GetCount1(row["seq1"]); //Returns Count1
row["count_2"] = GetCount2(row["seq1"]); //Returns Count2
//Forces an immediate garbage collection of all generations
GC.Collect();
GC.WaitForPendingFinalizers();
}
adapter.Update(data);
data.AcceptChanges();
}
public void PerformOperation2()
{
string sql = "select seq_1, product, is_valid from results where result_id = 1";
string updateSQL = "update results set is_valid = :is_valid WHERE seq_1 = :seq_1";
//Does exactly the samething like PerformOperation1 function above.
//100% same code.
//Only difference is that it updates different column named is_valid (with value 'Y' or 'N')
}
public void AddCommandParameter(DbCommand aoCommand, string asParamName, DbType aoDataType, int aiSize, string asColName)
{
if (aoCommand is OracleCommand)
{
OracleCommand loCommand = (OracleCommand)aoCommand;
OracleType loOraDataType = ConvertToOracleType(aoDataType);
loCommand.Parameters.Add(asParamName, loOraDataType, aiSize, asColName);
}
}
Any idea why this is happening, please?

I figured it out. It was not a coding issue. It was some underlying database view problem.

Related

how to make the datagridview load faster with Multiple Row query - c# SQL

The code:
private void suppPopulate()
{
byCode.Text = "Supplier Code";
byDesc.Text = "Supplier";
DGViewListItems.Columns.Add("custcode", "Customer Code");
DGViewListItems.Columns.Add("customer", "Customer");
DGViewListItems.Columns[0].ReadOnly = true;
DGViewListItems.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
DGViewListItems.Columns[1].ReadOnly = true;
DGViewListItems.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
using (SqlConnection con = db.Connect())
{
SqlDataReader rd;
SqlCommand cmd = new SqlCommand("SELECT DISTINCT custcode, custname FROM Customers WHERE type = 'V';", db.Connect());
rd = cmd.ExecuteReader();
int i = 0;
if (rd.HasRows)
{
while (rd.Read())
{
DGViewListItems.Rows.Add();
DGViewListItems.Rows[i].Cells["custcode"].Value = rd["custcode"].ToString().Trim();
DGViewListItems.Rows[i].Cells["customer"].Value = rd["custname"].ToString().Trim();
}
}
cmd.Connection.Close();
}
}
The SSMS output:
The output form loads slowly, the rows for the query are over 1000 so I think the cause of the slow load is the number of returned rows? If yes, how do make the load of the datagridview faster?
NOTE - this answer relates to the first part of the original question regarding why only a single row of the DataGrid being populated.
Incrementing the counter "i" at the bottom of the while loop looks like it may fix the problem.
You are adding rows, but only updating the first.

How to return a data from while c# using oracle 11g

I need to return all values from my table. How to write a code inside while?
var stringConnection = "Data Source = X; User Id = X; Password = X";
var sql = "SELECT * FROM TABLE";
OracleConnection _oracleConnection = new OracleConnection(stringConnection);
_oracleConnection.Open();
OracleCommand cmd = new OracleCommand(sql, _oracleConnection);
var dr = cmd.ExecuteReader();
var list = new List<dynamic>();
while(dr.Read())
{
// my doubt is here
}
return list;
var stringConnection = "Data Source = X; User Id = X; Password = X";
var sql = "SELECT * FROM TABLE";
OracleConnection _oracleConnection = new OracleConnection(stringConnection);
_oracleConnection.Open();
OracleCommand cmd = new OracleCommand(sql, _oracleConnection);
var dr = cmd.ExecuteReader();
var list = new List<dynamic>();
while(dr.Read())
{
// **** read column name data from table ****
string Id = (string)dr["Id"];
string company = (string)dr["company"];
string city = (string)dr["City"];
var objItem = new { Id = Id, company = company, city = "city" };
list.Add(objItem);
}
return list;
You have several options. 1 - load DataSet from OracleDataReader. There you will have all your data.
2 - you can still use select *... but you need a model. Then create List<SomeModel> instead of List<dynamic> with
while (reader.Read())
{
model.Property = reader["columnName"]; // will need convert type and take care of DB null. Can use existing extnsions
. . . .
}
3 - For arbitrary number of columns use OracleDataReader.FieldCount and some storage like List<object[]>
var data = new List<object[]>();
var fCnt = reader.FieldCount;
while (reader.Read())
{
var arr = new Object[fCnt];
for(int i = 0; i < fCnt; i++)
arr[i] = reader[i];
data.Add(arr);
}
The unfortunate part with #3 is that in the end you can get jagged array and not 2-dimentional one but you now have enough info to convert it. But I don't remember when I needed to do #3. So think about #1 and #2
And one more thing - absolutely no need for dynamic here. Stay away.

Undesired result from SQL query in C# console application

In my C# application I am trying to read data within my Accounts table, read the data as a decimal, preform a a calculation on it, and then update the same row.
Right now it reads the correct data in the column but two things go wrong when trying to update.
It sets all of the data in the AccountTotal column to the same value. This value is correct for the first row, but incorrect for the rest.
I believe the second problems occurs in calculating the data that is to be updated. When I try to update the DB, it sets the value twice as high as I am wanting it to be. For example: In my CalculateIncome method I wan't to add 100 to the account total, It adds 200.
What is causing these two problems?
Here is the program:
class Program
{
static void Main(string[] args)
{
//Need to change when deploying on real database.
const string DB_NAME = "Bank.sdf";
const string DB_PATH = #"C:\Users\Lucas\eBankRepository\eBank\App_Data\" + DB_NAME; // Use ".\" for CWD or a specific path
const string CONNECTION_STRING = "Data Source=" + DB_PATH;
decimal AccountTotal;
var conn = new SqlCeConnection(CONNECTION_STRING);
SqlCeDataReader reader = null;
try
{
conn.Open();
//Basic Query of all accounts
SqlCeCommand Query = new SqlCeCommand("SELECT * FROM Accounts", conn);
reader = Query.ExecuteReader();
while (reader.Read())
{
AccountTotal = reader.GetDecimal(2); //Column in DB for Account Total
AccountTotal += CalculateIncome();
//Update Total
SqlCeCommand UpdateTotal = new SqlCeCommand("UPDATE Accounts SET AccountTotal = #UpdatedTotal", conn); // Error when using WHERE Clause "WHERE AccountName= # Savings"
UpdateTotal.Parameters.AddWithValue("#UpdatedTotal", AccountTotal);
UpdateTotal.Connection = conn;
UpdateTotal.ExecuteNonQuery();
}
}
finally
{
if (reader != null)
{
reader.Close();
}
if (conn != null)
{
conn.Close();
}
}
}
public static decimal CalculateIncome()
{
return 100;
}
}
EDIT:
Here is the code I had before that included the WHERE clause in the command. With this code, it now only updates the the rows where it has an account name of "Savings," but it still sets the value in each of the rows to be the same for AccountTotal
while (reader.Read())
{
AccountTotal = reader.GetDecimal(2); //Column in DB for Account Total
AccountTotal += CalculateIncome();
//Update Total
SqlCeCommand UpdateTotal = new SqlCeCommand("UPDATE Accounts SET AccountTotal = #UpdatedTotal WHERE AccountName= #Savings", conn); // Error when using WHERE Clause "WHERE AccountName= # avings"
UpdateTotal.Parameters.AddWithValue("#UpdatedTotal", AccountTotal);
UpdateTotal.Parameters.AddWithValue("#Savings", "Savings");
UpdateTotal.Connection = conn;
UpdateTotal.ExecuteNonQuery();
}
Here is a visual as well for before and after the program is being run.
Before
After
Working Code
while (reader.Read())
{
AccountTotal = reader.GetDecimal(2); //Column in DB for Account Total
//Console.WriteLine(AccountTotal);
AccountTotal += CalculateIncome();
//Console.WriteLine(AccountTotal);
//Update Total
SqlCeCommand UpdateTotal = new SqlCeCommand("UPDATE Accounts SET AccountTotal = #UpdatedTotal WHERE AccountName = #Savings AND AccountID = #ID", conn);
UpdateTotal.Parameters.AddWithValue("#UpdatedTotal", AccountTotal);
UpdateTotal.Parameters.AddWithValue("#Savings", "Savings");
UpdateTotal.Parameters.AddWithValue("#ID", reader.GetInt32(0));
UpdateTotal.Connection = conn;
UpdateTotal.ExecuteNonQuery();
AccountTotal = 0; //Reset
}
Your two issues are:
It's updating all the rows to be the same value
This is because there isn't a where clause in your update statement.
It's making the value double up.
This is because of the line AccountTotal += CalculateIncome();
What this does is in the first run make it be 100 and the second loop around it makes it be 200.

Pulling a SELECT query into a datatable and accessing it

I'm writing a small ASP.net C# web page and it keeps giving me an error stating:
There is no row at position 0.
I'm probably doing it wrong but here is some of my code:
string SqlQuery = "SELECT * ";
SqlQuery += " FROM main_list";
SqlQuery += " WHERE ID = #FindID";
SqlConnection conn = new SqlConnection("server=???;database=contacts;User
ID=???;Password=???;");
conn.Open();
SqlCommand SqlCmd = new SqlCommand(SqlQuery, conn);
SqlCmd.Parameters.Add("#FindID",searchID);
SqlDataAdapter da = new SqlDataAdapter(SqlCmd);
try {
da.Fill(dt);
fillData(p);
}
catch {
txtId.Text = "ERROR";
}
And FillData is the following:
protected void fillData(int pos) {
txtId.Text = dt.Rows[pos]["ID"].ToString();
txtCompany.Text = dt.Rows[pos]["Company"].ToString();
txtFirstName.Text = dt.Rows[pos]["First_Name"].ToString();
txtLastName.Text = dt.Rows[pos]["Last_Name"].ToString();
txtAddress1.Text = dt.Rows[pos]["Address1"].ToString();
txtAddress2.Text = dt.Rows[pos]["Address2"].ToString();
txtCity.Text = dt.Rows[pos]["City"].ToString();
txtState.Text = dt.Rows[pos]["State"].ToString();
txtZipCode.Text = dt.Rows[pos]["ZipCode"].ToString();
txtPhoneNum1.Text = dt.Rows[pos]["Phone_Num"].ToString();
txtPhoneNum2.Text = dt.Rows[pos]["Phone_Num2"].ToString();
txtFax.Text = dt.Rows[pos]["Fax_Num"].ToString();
txtEmail.Text = dt.Rows[pos]["Email"].ToString();
txtNotes.Text = dt.Rows[pos]["Notes"].ToString();
txtCategory.Text = dt.Rows[pos]["Category"].ToString();
txtSubCategory.Text = dt.Rows[pos]["SubCategory"].ToString();
txtDateAdded.Text = dt.Rows[pos]["DateAdded"].ToString();
txtDateModified.Text = dt.Rows[0]["DateModified"].ToString();
}
Here is the call that errors out:
protected void btnPrev_Click(object sender, EventArgs e) {
p--;
lblPage.Text = p.ToString();
fillData(p-1);
}
protected void btnNext_Click(object sender, EventArgs e) {
p++;
lblPage.Text = p.ToString();
fillData(p-1);
}
I'm trying to cycle thru the Rows[0] to Rows[1] or however many there is but it gives me the error about no row at position 0 or position 1. It only fills once and then errors out.
EDIT:
I'm trying to access the second row returned by the database after already accessing one row already. For example: Rows[0] is accessible fine but then when I try to read Rows[1] it errors and says it doesn't have a row in position 1. I can revise the code to return Rows[1] and it works but when I try to access Rows[0] it breaks. This is why I pass the variable (p) to fillData so it can show only that Rows value. Thanks!
EDIT 2: I believe it's because there is a postback that wipes the values retrieved by the database. Is there a way to get the database entries to stay even after a postback? If not I am guessing I will have to query the database every time.
The error message indicates there are no rows being returned by SQL. Are you sure there is data to be returned.
When you use dt.Rows[0] you are effectively saying "take the first row that comes back, and get a value from it." If the DataTable doesn't have any rows (i.e. your SQL query returns no matches), that's like saying "Here is a plate that contains no apples. Take the first apple and tell me what colour it is" - see? Doesn't make sense.
What you should do is check whether there are any rows before you try to read them...
if(dt.Rows.Count > 0)
{
// do stuff here.
}
Use Linq and a stored procedure it is much nicer
datacontext context = new datacontext();
var result = context.MyStoredProc(searchID).FirstOrDefault();
Try changing
SqlCmd.Parameters.Add("#FindID",searchID);
to
SqlCmd.Parameters.AddWithValue("#FindID",searchID);
Check your query on your database, make sure rows are actually being returned. Also, it's bad practice to put your query directly into your code like that, especially when using parameters. You might want to try something like this:
private Int32 CallStoredProcedure(Int32 FindId)
{
using (var dt = new DataTable())
{
using (var conn = new SqlConnection(ConnectionString))
{
using (var sqlCmd = new SqlCommand("SEL_StoredProcedure", conn))
{
using (var sda = new SqlDataAdapter(sqlCmd))
{
sqlCmd.CommandType = System.Data.CommandType.StoredProcedure;
sqlCmd.Parameters.AddWithValue("#FindId", FindId);
sqlCmd.Connection.Open();
sda.Fill(dt);
}
}
}
if (dt.Rows.Count == 1)
return Convert.ToInt32(dt.Rows[0]["ID"]);
else if (dt.Rows.Count > 1)
throw new Exception("Multiple records were found with supplied ID; ID = " + studentId.ToString());
}
return 0;
}
To set up your stored procedure, on your database run this:
CREATE procedure [dbo].[SEL_StoredProcedure]
#FindId int = null
as
SELECT * FROM main_list where ID = #FindId
Just remove the index identifier from the code:
e.g.
txtId.Text = dt.Rows["ID"].ToString();

c# and stored procedures, unique code per SProc?

I'm writing an application which first connect to the database and retrieves a dt containing a list of all the stored procedures, inputs and their associated datatypes. The user then selected a SProc from the combobox and has to enter in the necessary inputs. The app will then connect to the database and run the selected SProc with the user specified inputs and return the results in a datatable.
What I'm unsure about is if I need to write a specific method for each SProc. I'm assuming so since I don't see how I could state what the parameters are otherwise.
Apologies for not making this clear the first time. Let me know if this still isn't clear enough.
Example is shown below (this is someone else's code)
public static GetDaysDTO GetDays(int offset)
{
GetDaysDTO ret = new GetDaysDTO { TODAY = DateTime.Now, TOMORROW = new DateTime(2012, 01, 01) };
SqlConnection con = new System.Data.SqlClient.SqlConnection(#"Server = FrazMan-pc\Programming; Database = master; Trusted_Connection = True");
SqlCommand cmd = new System.Data.SqlClient.SqlCommand
{
CommandText = "GetDays",
CommandType = System.Data.CommandType.StoredProcedure,
CommandTimeout = 1,
Connection = con,
Parameters = { new System.Data.SqlClient.SqlParameter("#offset", System.Data.SqlDbType.Int) { Value = offset } }
};
using (con)
{
con.Open();
using (System.Data.SqlClient.SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
ret.TODAY = DateTime.Parse(reader[0].ToString());
ret.TOMORROW = DateTime.Parse(reader["TOMORROW"].ToString());
}
}
}
return ret;
}
What you're looking for is a design pattern called Factory and a way to tell which typed data table to create on each SP call
If you have the list of the parameters for each procedure, u could instantiate the Parameters object via a loop:
This class will be used to fill the params of the sp received from the db
class ParamData
{
public object Data;
public SqlDbType type;
public string ParamName;
}
and then later on, when calling the sp, u should also pass thie ParamData object to the method, and used it to fill the params of ur sp dynamicly in a loop:
List<ParamData> list = new List<ParamData>();
//initialize command here as u did
SqlCommand cmd;
foreach (ParamData param in list)
{
SqlParameter sqlParam = new SqlParameter(param.ParamName, param.type);
sqlParam.Value = param.Data;
cmd.Parameters.Add(sqlParam);
}
//execute the command
//fill the datatable with result
DataTable dt = GetTableBySPName("GetDays");
SqlDataReader reader = cmd.ExecuteReader();
dt.Load(reader);
The only thing you need to add is the mapping between ur typed datatables and the returned table by the procedure.
You can add a method to do this:
private DataTable GetTableBySPName(string name)
{
DataTable dt = null;
switch (name)
{
case "GetDays":
{
dt = new GetDatsDTO();
break;
}
}
return dt;
}

Categories