So i have this project that needs to print data from the database.
I do this with a simple foreach loop:
public void LoadDatabase()
{
_connection.Open();
_dataAdapter.Fill(_dataTable);
try
{
foreach (DataRow row in _dataTable.Rows)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to LoadDatabase()" + ex.Message);
}
_connection.Close();
}
The problem is that it will loop anywhere from 2 times to 6 times meaning that it prints everything at least twice.
So at the moment my database contains Animal_Id's id:1 and id:2.
Now i get in my listbox (1, 2, 1, 2) or more depending on the amount of loops. I have no idea why this is happening and how to fix this. So all help would be highly appreciated
ps: if more code or information is needed please let me know.
pss: this is for a windows mobile 6.5 device with .net 3.5 build in Visual studio 2008. also im useing sqlite (not the newest version)
EDIT: After some testing it looks like my other 2 foreach loops in this project have the same problem.
Edit: So with al you guy's help i was able to fix it.
public void GetData()
{
try
{
SQLiteConnection Connection = new SQLiteConnection(#"Data Source = \Program Files\Mobile\Resources\Database\PPP_DB");
Connection.Open();
SQLiteCommand Command = new SQLiteCommand(Query, Connection);
Command.ExecuteNonQuery();
Data_Adapter = new SQLiteDataAdapter("SELECT * FROM Animal_Info", Connection);
Data_Set.Reset();
Data_Adapter.Fill(Data_Set);
Data_Table = Data_Set.Tables[0];
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
foreach (DataRow row in Data_Table.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
Connection.Close();
Program.AnimalInfo.Refresh();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
So it seems that it messed up with the DataTable somewhere, and that made it loop multiple times. ps: i tried to set all items with stuff like Databound, but didn't really work out for me, thats the reason im still doing it this way.
Add the following two lines at the top of your function.
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
_dataTable.Clear();
This will ensure that you do not double up any row data.
Assuming that there is no unique constraint on "Animal_ID" field in the database (you din't answer to my comment), check for items duplication:
if (!Program.AnimalInfo.Info_ID_ListBox.Items.Contains(row["Animal_ID"].ToString())
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
Well first, LoadDatabase() should return a DataTable (or DataSet), this make sthe LoadDatabase() function useful for other data requests, Also you should never need to use a 'for loop' to add items to a ListBox. you can bind the ListBox directly to the source.... something like this should do it
listBox1.DataSource = _dataTable;
listBox1.ValueMember = "Animal_ID";
listBox1.DisplayMember = "Animal_ID";
There are some examples using SqlDataReader and SqlDataAdapter here http://gsidev.somee.com/#2&2AD97ECBE2AE41D08191F6E4C773D8A9&cs
Unique contraints are not needed here. But the listbox.Items collection might have have to be cleared.
Possibly you are (or the system, depends on where the code is, for example in a paint event handler) calling the 'LoadDatabase()' function twice!
Just place a break point in the _dataAdapter.fill() and press F11 to let the app run over this and then right click the _dataTable to inspect it's contents.
Although you can explictely set the datasource of the ListBox, that is not needed and may lead to other side effects you are currently not aware of. It is OK to start with simple code before letting the system do unknwon stuff in the background.
Try that:
public void LoadDatabase()
{
_connection.Open();
_dataAdapter.Fill(_dataTable);
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
try
{
foreach (DataRow row in _dataTable.Rows)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to LoadDatabase()" + ex.Message);
}
_connection.Close();
}
Related
I got two standard projects in a solution. The UI and the Logic.
As usual, you need to take the inputs from the UI and do whatever you want with them in the back end part.
So in the UI class, I have this
private void btnAddItems_Click(object sender, RoutedEventArgs e)
{
item_name = lbl_item_name.Text;
item_quantity = lbl_item_quantity.Text;
store_ime = store_Name.Text;
logika.storeInDb(store_ime, item_name, item_quantity);
}
It just stores the input in variables and then sends them to this
public void storeInDb(string store_name, string item_name, string item_quantity)
{
using (MySqlConnection mySqlConn = new MySqlConnection(Logic.connStr))
{
dbInsert($"INSERT INTO soping(store_name, item_name, item_quantity, payment_type, date) VALUES('{store_name}', '{item_name}', '{item_quantity}', 'visa', 'danas')");
}
}
And this is the dbInsert method
public void dbInsert(string query)
{
using (MySqlConnection mySqlConn = new MySqlConnection(Logic.connStr))
{
try
{
mySqlConn.Open();
MySqlCommand cmd = new MySqlCommand(query, mySqlConn);
cmd.ExecuteNonQuery();
mySqlConn.Close();
}
catch (MySqlException e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}
}
It doesn't store anything. And when I use breakpoints, it seems like the button method runs after storeInDb, even though the variables in the query are perfectly fine. And I can't find anything wrong with the code that would make it behave weird like this.
This code have some issues:
1- You should use parameters instead of direct strings in your sql query;
2- You don't need a connection outside your dbInsert Method
However, this code should work. I guess the problem you are having is located elsewhere, not in the code you posted here. Something simpler, maybe connectionstring problem (saving in other place where you don't expect to) or bad uses of threads...Maybe hitting deadlocks, long processing or something like that (the only way i can think of having button click apparently happenning after the code it calls).
I am going through a YouTube video, to learn ASP.NET. I've gotten it to work for the most part, with a somewhat major caveat: I can't retrieve a value from a hidden field ID. Because of that, I don't have a value to send to a stored procedure to create or update.
The commented out line is the original statement. When I have that then execute
.ExecuteNonQuery, I get the following error:
Procedure or function 'ResourceCreateOrUpdate' expects parameter '#ResourceID', which was not supplied
When I try to display hfResourceID, I have nothing when trying to pass 0, for a create, or the ResourceID value, i.e. 1. That value however, doesn't get there. I know the stored procedure works because I can execute it in SQL Server Management.
I tried moving hfResourceID to a string, then a integer value, but I seem to be having problems creating the if/else: everything is marked as an error. When I hover over the lines, I get the following message, which pretty much leave me clueless:
"Embedded statement cannot be a declaration or labeled statement".
Would I be able to get any pointers on how to clear up my error, please? Thanks.
2017-10-13 # 10:38: code updated
<asp:HiddenField ID="hfResourceID" runat="server" />
protected void btnSave_Click(object sender, EventArgs e)
{
int intResourceID = 0;
bool boolIDHasValue = true;
try
{
intResourceID = Convert.ToInt32(hfResourceID.Value);
}
catch (Exception ex)
{
lblErrorMessage.Text = ex.Message;
boolIDHasValue = false;
}
if (boolIDHasValue)
{
if (sqlconnODRConnection.State == System.Data.ConnectionState.Closed)
sqlconnODRConnection.Open();
SqlCommand sqlcmdCreateOrUpdate = new SqlCommand("ResourceCreateOrUpdate", sqlconnODRConnection);
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#ResourceID", intResourceID);
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#Status", txtStatus.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#FirstName", txtFirstName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#MiddleName", txtMiddleName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#LastName", txtLastName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#NickName", txtNickName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#Gender", txtGender.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#USCitizen", txtUSCitizen.Text.Trim());
sqlcmdCreateOrUpdate.ExecuteNonQuery();
sqlconnODRConnection.Close();
string strResourceID = hfResourceID.Value;
Clear();
if (strResourceID == "")
lblSuccessMessage.Text = "Saved Successfully";
else
lblSuccessMessage.Text = "Updated Successfully";
FillGridView();
}
}
There are a few issues with the code you copied from that video. But here a snippet as to how it should be done. I've added 3 ways to convert from the HiddenField value to an actual int. Which one you use can depend on how you want to handle errors, 0 values etc. Not included in the snippet, but I like to check for IsNullOrEmpty while using Trim(), that gets rid of spaces that might make the value non-convertible if (!string.IsNullOrEmpty(hfResourceID.Value.Trim())).
int intResourceID = 0;
//this will try to convert but you won't see exeptions when failed
Int32.TryParse(hfResourceID.Value, out intResourceID);
//checks if there is a value in the hiddenfield, but throws yellow screen if not convertible
if (!string.IsNullOrEmpty(hfResourceID.Value))
{
intResourceID = Convert.ToInt32(hfResourceID.Value);
}
//catch an error when the value is not convertible, can be wrapped with !string.IsNullOrEmpty(hfResourceID.Value)
try
{
intResourceID = Convert.ToInt32(hfResourceID.Value);
}
catch (Exception ex)
{
//handle the error, can be seen with ex.Message
}
//if the hidden value is still 0 (for whatever reason) you might not want to execute the query
//so the next part will return and stop executing the rest of the code
if (intResourceID == 0)
{
return;
}
//update the database, using 'using' will ensure proper closure of the connection and disposing of any objects
using (SqlConnection connection = new SqlConnection("myConnectionString"))
using (SqlCommand command = new SqlCommand("ResourceCreateOrUpdate", connection))
{
//set the command type and add the parameters
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#ResourceID", SqlDbType.Int).Value = intResourceID;
try
{
//open the database connection and execute the command
connection.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
//there was an error opening the database connection or with the command, can be viewed with ex.Message
}
}
Your error regarding the embedded statement is because you a declaring
if (strResourceID == "")
int intResourceID = 0;
else
int intResourceID = (Convert.ToInt32(hfResourceID.Value));
When decalring a variable directly after an If or else then you need your curly brackets. So...
if (strResourceID == "")
{
int intResourceID = 0;
}
else
{
int intResourceID = (Convert.ToInt32(hfResourceID.Value));
}
As for your other issues I would need to see your client side code.
Take a look on ASP code, specifically the way you are setting hidden field value. I guess it is properly marked as runat=server but maybe some problem is going on in your asp.net code, try to debug client code by using: console.log function and see the output in your console browser.
I am using visual studio 2010 ultimate with SQL Server 2012, When I click save button first time it works fine, but after changing values it shows above mentioned exception. I m failed to solve this problem. please help me.
Thanks
Abdul Muttalib
public int AddStudent(StudentManager studentManager)
{
try
{
sqlCom.Connection = sqlCon;
sqlCon.Open();
sqlCom.CommandType = CommandType.StoredProcedure;
sqlCom.CommandText = "spAddStudent";
sqlCom.Parameters.AddWithValue("#Name", studentManager.Name);
sqlCom.Parameters.AddWithValue("#Father", tudentManager.Father);
sqlCom.Parameters.AddWithValue("#Gender", tudentManager.Gender);
sqlCom.Parameters.AddWithValue("#Religion", studentManager.Religion);
sqlCom.Parameters.AddWithValue("#MotherTongue", studentManager.Tongue);
sqlCom.Parameters.AddWithValue("#BirthDate", studentManager.BrithDate);
sqlCom.Parameters.AddWithValue("#Class", studentManager.StudentClass);
sqlCom.Parameters.AddWithValue("#Section", studentManager.Section);
sqlCom.Parameters.AddWithValue("#Shift", studentManager.Shift);
sqlCom.Parameters.AddWithValue("#RollNo", studentManager.RollNo);
sqlCom.Parameters.AddWithValue("#Fee", studentManager.Fee);
sqlCom.Parameters.AddWithValue("#FatherOccupation", studentManager.FatherOccupation);
sqlCom.Parameters.AddWithValue("#FatherCNIC", studentManager.Cnic);
sqlCom.Parameters.AddWithValue("#AddWithValueressRes", studentManager.AddressRes);
sqlCom.Parameters.AddWithValue("#PhoneRes", studentManager.PhoneRes);
sqlCom.Parameters.AddWithValue("#PhoneOff", studentManager.PhoneOff);
sqlCom.Parameters.AddWithValue("#Mobile", studentManager.Mobile);
sqlCom.Parameters.AddWithValue("#PostalCode", studentManager.PostalCode);
sqlCom.Parameters.AddWithValue("#Email", studentManager.Email);
sqlCom.Parameters.AddWithValue("#OtherInfo", studentManager.OtherInfo);
int i = sqlCom.ExecuteNonQuery();
sqlCon.Close();
return i;
}
catch (Exception ex)
{
throw ex;
}
}
}
}enter image description here
You could resolve this by calling sqlCom.Parameters.Clear() at the start of AddStudent to prevent accumulating redundant parameters whenever the method is called.
Also, using AddWithValue is easy and common, but it can be problematic.
I have a method that queries a table for the count of its records. QA has discovered an "edge case" where if a particular operation is canceled in a particular order and speed (as fast as possible), the GUI "forgets" about the rest of the records in that table (the contents of the tables are uploaded to a server; when each one finishes, the corresponding table is deleted).
To be clear, this table that is having records deleted from it and then queried for count ("workTables") is a table of table names, that are deleted after they are processed.
What I have determined (I'm pretty sure) is that this anomaly occurs when a record from the "workTables" table is in the process of being deleted when the workTables table is queried for the count of its records. This causes an exception, which causes the method to return -1, which in our case indicates we should cuase the GUI to not display those records.
Is there a way to check if a table is in the process of having a record deleted from it, and wait until after that operation has completed, before proceeding with the query, so that it won't throw an exception?
For those interested in the specifics, this method is the one that, under those peculiar circumstances, throws an exception:
public int isValidTable(string tableName)
{
int validTable = -1;
string tblQuery = "SELECT COUNT(*) FROM ";
tblQuery += tableName;
openConnectionIfPossibleAndNecessary();
try
{
SqlCeCommand cmd = objCon.CreateCommand();
cmd.CommandText = tblQuery;
object objcnt = cmd.ExecuteScalar();
validTable = Int32.Parse(objcnt.ToString());
}
catch (Exception ex)
{
validTable = -1;
}
return validTable;
}
...and this is the method that deletes a record from the "workTables" table after the corresponding table has had its contents uploaded:
private void DropTablesAndDeleteFromTables(string recordType, string fileName)
{
try
{
WorkFiles wrkFile = new WorkFiles();
int tableOK = 0;
DataSet workfiles;
tableOK = wrkFile.isValidWorkTable(); // -1 == "has no records"
if (tableOK > 0) //Table has at least one record
{
workfiles = wrkFile.getAllRecords();
//Go thru dataset and find filename to clean up after
foreach (DataRow row in workfiles.Tables[0].Rows)
{
. . .
dynSQL = string.Format("DELETE FROM workTables WHERE filetype = '{0}' and Name = '{1}'", tmpType, tmpStr);
dbconn = DBConnection.GetInstance();
dbconn.DBCommand(dynSQL, false);
populateListBoxWithWorkTableData();
return;
} // foreach (DataRow row in workfiles.Tables[0].Rows)
}
}
catch (Exception ex)
{
SSCS.ExceptionHandler(ex, "frmCentral.DropTablesAndDeleteFromTables");
}
}
// method called by DropTablesAndDeleteFromTables() above
public int isValidWorkTable() //reverted to old way to accommodate old version of DBConnection
{
// Pass the buck
return dbconn.isValidTable("workTables");
}
I know this code is very funky and klunky and kludgy; refactoring it to make more sense and be more easily understood is a long and ongoing process.
UPDATE
I'm not able to test this code:
lock (this)
{
// drop the table
}
...yet, because the handheld is no longer allowing me to copy files to it (I get, "Cannot copy [filename.[dll,exe] The device has either stopped responding or has been disconnected" (it is connected, as shown by ActiveStync))
If that doesn't work, I might have to try this:
// global var
bool InDropTablesMethod;
// before querying that database from elsewhere:
while (InDropTablesMethod)
{
Pause(500);
}
UPDATE 2
I've finally been able to test my lock code (copies of binaries were present in memory, not allowing me to overwrite them; the StartUp folder had a *.lnk to the .exe, so every time I started the handheld, it tried to run the buggy versions of the .exe), but it doesn't work - I still get the same conflict/contention.
UPDATE 3
What seems to work, as kludgy as it may be, is:
public class CCRUtils
{
public static bool InDropTablesMethod;
. . .
if (CCRUtils.InDropTablesMethod) return;
CCRUtils.InDropTablesMethod = true;
. . . // do it all; can you believe somebody from El Cerrito has never heard of CCR?
CCRUtils.InDropTableMethod = false;
UPDATE 4
Wrote too soon - the bug is back. I added this MessageBox.Show(), and do indeed see the text "proof of code re-entrancy" at run-time.
while (HHSUtils.InDropTablesMethod)
{
MessageBox.Show("proof of code re-entrancy");
i++;
if (i > 1000000) return;
}
try
{
HHSUtils.InDropTablesMethod = true;
. . .
}
HHSUtils.InDropTablesMethod = false;
...so my guess that code re-entrancy may be a problem is correct...
I know what I asking might not make a lot of sense for C# experts but I'll explain what I want to do and then you can suggest me how to do it in a better way if you want ok?
I have a C# class called DatabaseManager that deals with different MySQL queries (ado.net NET connector, not linq or any kind of ActiveRecord-ish library).
I am doing something like
categories = db_manager.getCategories();
The list of categories is quite small (10 items) so I'd like to know what's the best way of accessing the retrieved information without a lot of additional code.
Right now I'm using a Struct to store the information but I'm sure there's a better way of doing this.
Here's my code:
public struct Category
{
public string name;
}
internal ArrayList getCategories()
{
ArrayList categories = new ArrayList();
MySqlDataReader reader;
Category category_info;
try
{
conn.Open();
reader = category_query.ExecuteReader();
while (reader.Read())
{
category_info = new Category();
category_info.name = reader["name"].ToString();
categories.Add(category_info);
}
reader.Close();
conn.Close();
}
catch (MySqlException e)
{
Console.WriteLine("ERROR " + e.ToString());
}
return categories;
}
Example:
public IEnumerable<Category> GetCategories()
{
using (var connection = new MySqlConnection("CONNECTION STRING"))
using (var command = new MySqlCommand("SELECT name FROM categories", connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return new Category { name = reader.GetString(0) };
}
}
}
}
Remarks:
Let ADO.NET connection pooling do the right work for you (avoid storing connections in static fields, etc...)
Always make sure to properly dispose unmanaged resources (using "using" in C#)
Always return the lowest interface in the hierarchy from your public methods (in this case IEnumerable<Category>).
Leave the callers handle exceptions and logging. These are crosscutting concerns and should not be mixed with your DB access code.
The first thing I would do is to replace you use of ArrayList with List that will provide compile-time type checkig for your use of the category list (so you will not have to type cast it when using it in your code).
There's nothing wrong with returning them in an like this. However, a few things stand out:
Your catch block logs the error but
then returns either an empty array or
a partially populated array. This
probably isn't a good idea
If an exception is thrown in the try
block you won't close the connection
or dispose of the reader. Consider
the using() statement.
You should use the generic types
(List<>) instead of ArrayList.
From your code I guess you are using .NET 1.1, becuase you are not using the power of generics.
1) Using a struct that only contains a string is an overkill. Just create an arraylist of strings (or with generics a List )
2) When an exception occurs in your try block, you leave your connection and reader open... Use this instead:
try
{
conn.open();
//more code
}
catch (MySqlException e) { // code
}
finally {
conn.close()
if (reader != null)
reader.close();
}