System.Data.DataRowView error - c#

So, I.ve actually googled a lot about that error, but some of the code that had a solution I couldn't understand, mainly because I'm new at c#, so I'll just put the problem as it is.
My professors called it "complex winform". That's basically data from 2 different tables that are linked with an inner join. So far so good.
I work with postgresql btw.
I have 2 main tables. Student (with idstudent, registrationid, yearofstudy) and Persons( with idperson, Name, Telephone, Email, etc). (idstudent = idperson)
There are around 20 PERSONS in my database and 7 STUDENTS. Students are also persons (duuh), ergo idstudent=idperson.
So, I have a combobox where i put a disctinct yearofstudy of all my students and it looks like this.
private void frmComplex1_Load(object sender, EventArgs e)
{
OdbcConnection conexiune;
conexiune = new OdbcConnection();
conexiune.ConnectionString = "Driver={PostgreSQL ANSI};database=postgres;server=localhost;port=5432;uid=postgres;sslmode=disable;readonly=0;protocol=7.4;fakeoidindex=0;showoidcolumn=0;rowversioning=0;showsystemtables=0;fetch=100;unknownsizes=0;maxvarcharsize=255;maxlongvarcharsize=8190;debug=0;commlog=0;usedeclarefetch=0;textaslongvarchar=1;unknownsaslongvarchar=0;boolsaschar=1;parse=0;extrasystableprefixes=dd_;lfconversion=1;updatablecursors=1;trueisminus1=0;bi=0;byteaaslongvarbinary=0;useserversideprepare=1;lowercaseidentifier=0;gssauthusegss=0;xaopt=1;pwd=irimia96";
conexiune.Open();
OdbcCommand comanda;
comanda = new OdbcCommand();
comanda.CommandText = "SELECT DISTINCT anstudiu from studenti ORDER BY anstudiu asc ";
comanda.Connection = conexiune;
OdbcDataReader cititor;
cititor = comanda.ExecuteReader();
DataSet dsDate;
dsDate = new DataSet();
DataTable tblStudenti;
tblStudenti = new DataTable("studenti");
tblStudenti.Load(cititor);
dsDate.Tables.Add(tblStudenti);
this.cboComplex1.DataSource = dsDate.Tables["studenti"];
this.cboComplex1.DisplayMember = "anstudiu";
this.cboComplex1.ValueMember = "anstudiu";
conexiune.Close();
}
So what im trying to do is, whenever I select a year (1/2/3) from that combobox, to get in return, in my first DataGrindView Information about students that are year 1/2/3 from BOTH STUDENT table and PERSON TABLE. For example: the students from yearofstudy 2 with. IdPerson, Name, Telephone, Email, RegistrationId, Student Id. (I know Student Id and Person Id will get the same value, but I dont care, first let it work)
So i type the script, and get this the datarawview error
private void cboComplex1_SelectedIndexChanged(object sender, EventArgs e)
{
OdbcConnection conexiune;
OdbcCommand comanda;
DataSet dsDate;
OdbcDataReader cititor;
DataTable tblPersoane;
conexiune = new OdbcConnection();
conexiune.ConnectionString = " Driver={PostgreSQL ANSI};database=postgres;server=localhost;port=5432;uid=postgres;sslmode=disable;readonly=0;protocol=7.4;fakeoidindex=0;showoidcolumn=0;rowversioning=0;showsystemtables=0;fetch=100;unknownsizes=0;maxvarcharsize=255;maxlongvarcharsize=8190;debug=0;commlog=0;usedeclarefetch=0;textaslongvarchar=1;unknownsaslongvarchar=0;boolsaschar=1;parse=0;extrasystableprefixes=dd_;lfconversion=1;updatablecursors=1;trueisminus1=0;bi=0;byteaaslongvarbinary=0;useserversideprepare=1;lowercaseidentifier=0;gssauthusegss=0;xaopt=1;pwd=irimia96";
conexiune.Open();
comanda = new OdbcCommand();
comanda.CommandText = "SELECT * from persoane INNER JOIN studenti on persoane.idpersoana = studenti.idstudent WHERE anstudiu =?";
comanda.Connection = conexiune;
comanda.Parameters.Clear();
comanda.Parameters.AddWithValue("anstudiu", cboComplex1.SelectedValue.ToString());
cititor = comanda.ExecuteReader();
tblPersoane = new DataTable("persoane");
tblPersoane.Load(cititor);
dsDate = new DataSet();
dsDate.Tables.Add(tblPersoane);
dGComplex.DataSource = dsDate;
dGComplex.DataMember = "persoane";
dGComplex.Refresh();
}
Srry for the long post, i'll give you a potato at the end.

Just invert the order of the settings for DisplayMember/ValueMember in relation to the setting of the DataSource property
this.cboComplex1.DisplayMember = "anstudiu";
this.cboComplex1.ValueMember = "anstudiu";
// Move this line after setting the Disply/ValueMember property
this.cboComplex1.DataSource = dsDate.Tables["studenti"];
This should ensure the proper binding of the strings used for Display/ValueMember against the field names of the datatable.
I should add that this mode doesn't catch an error if you mistype one of your field names (for example "anstdiu" will be accepted).
On the contrary, if you set the DataSource before the Display/ValueMember, trying to write an invalid name will get you a runtime exception.

Related

Updating a database from selected row in another form

I am having issues updating a selected row on a datagridview that pulls from a database in another form.
I used this to get the information from the datagridview into the textboxes on the other form:
private void updateAppointmentButton_Click(object sender, EventArgs e)
{
UpdateAppointment updateAppointment = new UpdateAppointment();
updateAppointment.mainFormObject = this;
updateAppointment.customerIdBox.Text = appointmentCalendar.CurrentRow.Cells[0].Value.ToString();
updateAppointment.customerNameBox.Text = appointmentCalendar.CurrentRow.Cells[1].Value.ToString();
updateAppointment.typeBox.Text = appointmentCalendar.CurrentRow.Cells[2].Value.ToString();
updateAppointment.startTimeBox.Value = Convert.ToDateTime(appointmentCalendar.CurrentRow.Cells[4].Value.ToString());
updateAppointment.endTimeBox.Value = Convert.ToDateTime(appointmentCalendar.CurrentRow.Cells[3].Value.ToString());
updateAppointment.Show();
MySqlConnection c = new MySqlConnection(SqlUpdater.conString);
MySqlCommand updateCmd = new MySqlCommand();
updateCmd.Connection = c;
c.Open();
updateCmd.CommandText = $"UPDATE customer SET customerName = '{customerNameBox.Text}'";
updateCmd.ExecuteNonQuery();
c.Close();
MessageBox.Show("Appointment Updated");
I figure its the SQL query, but not sure how to limit it to JUST the information on the selected row. Right now, it'll update everyone on the datagridview and database.
Any ideas?
Thanks!
I've tried putting
MainForm.appointmentCalendar.CurrentRow.Cells[1].Value.ToString();
as WHERE in the SQL query, but it returns an "object reference is required" error.
have a column with unique customer ID, then in your query you want
Update customer SET customerName = '{customerNameBox.Text}' where customerID = 'UniqueID'
----- (whatever the ID that you are trying to update is)
probably something like int.Parse(otherDataGrid.selectedRows[0].Cells["ID"].Value.ToString())
#edit
I don't really understand what you're trying to say. You might want to try with parameteres. this would be your query:
Update appointment set type = #type, start = #start, end = #end where customerId = #id
then before you execute the command you say:
updateCmd.Parameters.AddWithValue("#type", typeBox.Text);
and do that for all other parameters too.
Also make sure that your text boxes are not empty, because they most likely are if your query is deleting the data (maybe it's updating it with an empty string)

Passing integer variables into SQL statement from another form

I'm working with a school project where we are supposed to build a simple booking system that utilizes SQL database. Used language is C# and environment Visual Studio Community 2017. I'm trying to build a function where user selects a row from dataGridView1 and clicks 'Add new invoice button'. The booking_id is extracted from dataGridView1 and passed to Form2 where booking-related data is searched with booking_id. This data is then presented in dataGridView2 which lists all services included in the one user-specified booking.
Database contains three relevant tables; Booking, Service and BoughServices.
Booking contains column booking_id (INT)
Services contains columns service_id (INT), name (VARCHAR) and price (INT)
BoughtServices, contains columns Booking.booking_id (INT),
Service.service_id (INT) and amount (INT)
Code on Form1:
// Establish a class for data.
public static class DataToForm2
{
public static int booking_id;
}
// User clicks button 'Add new invoice'.
private void button_CreateInvoice_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
//Note: CurrentRow.Cells[0] contains booking_id.
DataToForm2.booking_id = Convert.ToInt32(dataGridView1.CurrentRow.Cells[0].Value);
f2.Show();
}
Code on Form2:
private void Form2_Load(object sender, EventArgs e)
{
using(SqlConnection connection = new SqlConnection(connectionString))
{
// Set SQL query string.
string query = "SELECT Service.name, Services.price, BoughtServices.amount " +
"FROM Service " +
"INNER JOIN Service.service_id ON BoughtServices.service_id " +
"INNER JOIN Bookings ON Bookings.booking_id = BoughtServices.booking_id " +
"WHERE " +
"Booking.booking_id = #booking_id";
SqlCommand command = new SqlCommand(query, connection);
// Set query parameters.
command.Parameters.Add("#booking_id", SqlDbType.Int).Value = Form1.DataToForm2.booking_id;
// Run SQL query
var dataAdapter = new SqlDataAdapter(query, connection);
DataSet ds = new DataSet();
dataAdapter.Fill(ds);
dataGridView1.ReadOnly = true;
dataGridView2.DataSource = ds.Tables[0];
}
}
However, this approach is non-functional and crashes with an error System.Data.SqlClient.SqlException: Must declare the scalar variable "#booking_id. I have spent an increasing number of hours to figure this out and find solution from tutorials but so far they all have failed. Interestigly, everything is working just fine when I add booking_id directly to SQL statement. In otherwords, WHERE Booking.booking_id = 2 works like a charm.
Could someone help me and propose how I should format my code so that I am able to perform desired actions? Thank you in advance!
Almost right. The only problem is the SqlDataAdapter that receives the sql string and not the command where you have defined the parameter. Just change
// Run SQL query
var dataAdapter = new SqlDataAdapter(command);
As you can see there is a SqlDataAdapter's constructor that receives a SqlCommand where you could define parameters and set the connection.
You're using the parameterized SQL query incorrectly. It functions more like string.Replace than an assignment. Try
command.Parameters.Add("#booking_id",
Form1.DataToForm2.booking_id);

Display plan name but hold plan ID and retrieve ID's value in combobox

I saw many many articles on this but none helped so far.
My ComboBox name is cbPlan. I want to Retrieve PlanName in it's display but want to actually hold it PlanID.
Following code displays both Names and IDs. I tried ValueMember, DisplayMember, properties but couldn't get it sorted yet.
Finally, even if this works out, how will I get to insert PlanID in another table? Will i use Convert.ToString(cbPlan.Text) - which would bring the PlanName and not the ID.
Please help on this - A big thank you in advance! :)
P.S. PlanID's data type is int.
private void cbPlan_Click(object sender, EventArgs e)
{
cbPlan.Items.Clear();
string pullsub = "select PlanID,PlanName from fbkPlanMaster(nolock)";
string connString = ConfigurationManager.ConnectionStrings["Dbconn"].ToString();
SqlConnection connection = new SqlConnection(connString); // defining sql connection
SqlCommand cmd = new SqlCommand(pullsub, connection);
cmd.CommandText = pullsub;
connection.Open();
SqlDataReader drd = cmd.ExecuteReader();
while (drd.Read())
{
cbPlan.Items.Add(drd["PlanID"]);
cbPlan.Items.Add(drd["PlanName"]);
cbPlan.ValueMember = "PlanID";
cbPlan.DisplayMember = "PlanName";
}
}
First of all modifying your approach to add items to your combo-box you should not use Reader. Second, why are you adding PlanID if you don't want to display it?
This code may help you ...
cbPlan.DataSource = dt;
cbPlan.ValueMember = "PlanID";
cbPlan.DisplayMember = "PlanName";
You should first get your data from database into some datatable or a dataset as above cbPlan.DataSource = dt;
This will hold your id as ValueMember as an ID and its displayed text will be PlanName. Hope this helps you.

add row to a BindingSource gives different autoincrement value from whats saved into DB

I have a DataGridView that shows list of records and when I hit a insert button, a form should add a new record, edit its values and save it.
I have a BindingSource bound to a DataGridView. I pass is as a parameter to a NEW RECORD form so
// When the form opens it add a new row and de DataGridView display this new record at this time
DataRowView currentRow;
currentRow = (DataRowView) myBindindSource.AddNew();
when user confirm to save it I do a
myBindindSource.EndEdit(); // inside the form
and after the form is disposed the new row is saved and the bindingsorce position is updated to the new row
DataRowView drv = myForm.CurrentRow;
avaliadoTableAdapter.Update(drv.Row);
avaliadoBindingSource.Position = avaliadoBindingSource.Find("ID", drv.Row.ItemArray[0]);
The problem is that this table has a AUTOINCREMENT field and the value saved may not correspond the the value the bindingSource gives in EDIT TIME.
So, when I close and open the DataGridView again the new rowd give its ID based on the available slot in the undelying DB at the momment is was saved and it just ignores the value the BindingSource generated ad EDIT TIME,
Since the value given by the binding source should be used by another table as a foreingKey it make the reference insconsistent.
There's a way to get the real ID was saved to the database?
I come up with this solution
First added a GetNextID() method directly to the table model:
SELECT autoinc_next
FROM information_schema.columns
WHERE (table_name = 'Estagio') AND (column_name = 'ID')
and whener I need a new row to be added I do
EstagioTableAdapter ta = new EstagioTableAdapter ();
nextID = ta.GetNextID();
row = (DataRowView)source.AddNew();
row.Row["ID"] = nextID;
(...)
source.EndEdit();
The same thing happens with Access databases. There is a great article (with solution) here. Basically, the TableAdapter normally sends 2 queries in a batch when you save the data. The first one saves the data and the second one asks for the new ID. Unfortunately, neither Access nor SQL CE support batch statements.
The solution is to add an event handler for RowUpdated that queries the DB for the new ID.
based on my answer on concurrency violation, use da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord.
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = new SQLiteCommand(
#"insert into product(product_id, product_name, abbrev) values(:_product_id, :_product_name, :_abbrev);
select product_id /* include rowversion field here if you need */
from product where product_id = LAST_INSERT_ROWID();", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
bds.DataSource = dt;
grd.DataSource = bds;
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
here's the sample table on SQLite:
CREATE TABLE [product] (
[product_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[product_name] TEXT NOT NULL,
[abbrev] TEXT NOT NULL
)
[EDIT Nov 19, 2009 12:58 PM CN] Hmm... I guess my answer cannot be used, SQLCE does not allow multiple statements.
anyway, just use my answer when you use server-based MSSQL or if you use SQLite. or perhaps, encapsulate the two statements to a function that returns scope_identity(integer):
da.InsertCommand = new SQLiteCommand(
#"select insert_to_product(:_product_id, :_product_name, :_abbrev) as product_id", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
use RowUpdated (shall work on SQLCE and RDBMS that doesn't support multi-statements):
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = b.GetInsertCommand();
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
da.RowUpdated += da_RowUpdated;
bds.DataSource = dt;
grd.DataSource = bds;
}
void da_RowUpdated(object sender, System.Data.Common.RowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
int ident = (int)(long) new SQLiteCommand("select last_insert_rowid()", c).ExecuteScalar();
e.Row["product_id"] = ident;
}
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
I haven't had a chance to use SQLiteConnection class but I do used SQLConnection and SQLCommand class. SqlCommand has a method ExecuteScalar that return the value of the first row and first column of your t-sql statement. You can use it to return the Auto-Identity column. Also, in SQL Server 2005 there is a keyword named OUTPUT you may also check it too.
I've come across this: all you need to do is set your autoincrement seed to -1 and have it "increment" by -1 too. This way all your datarows will have unique ids that DON'T map to anything in the real database. If you're saving your data with a DataAdapter, then after the save your datarow and any other rows with a datarelation pointing to that id will be updated

Invalid Object Name: Beginner using the AdventureWords db from a class

I'm trying to learn some C#.net. I'm just trying to expose the AdventureWorks database included in my C# class via a web interface. Here's the setup:
I've got a DropDownList in on my ASPX page with an id of tableNameDropDown. It gets populated on Page_Load like this:
protected void Page_Load(object sender, EventArgs e)
{
conn.Open();
String table_names_sql = "select Name from sysobjects where type='u' ORDER BY name";
SqlCommand cmd = new SqlCommand(table_names_sql, conn);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
tableNameDropDown.Items.Add(reader[0].ToString());
}
conn.Close();
tableNameDropDown.AutoPostBack = true;
}
And that works just fine, I get a nice long list of the tables in the DB. When someone selects a table from the list, I want to display that table in a GridView control with an id of grid. This is what I've got:
protected void tableNameDropDown_SelectedIndexChanged(object sender, EventArgs e)
{
DataSet dataSet = new DataSet();
String tableName = columnNameDropDown.SelectedItem.ToString();
String table_sql = String.Format("SELECT * FROM {0};", tableName);
SqlDataAdapter adapter = new SqlDataAdapter(table_sql, conn);
adapter.Fill(dataSet, tableName);
grid.DataSource = dataSet;
grid.DataMember = tableName;
}
When I debug the page, I get an error on the adapter.Fill(dataSet, tableName); line: SqlException: Inlvalid object name '{tableName}'.
The tables in the DB are the following:
dbo.AWBuildVersion
.... more dbo. tables
HumanResources.Department
HumanResources.Employee
.... more HumanResources tables
Person.Address
Person.AddressType
.... more Person tables
... Other prefixes are "Pdoduction, Purchasing, Sales"
There are probably ~50+ tables, and I get all their names (without the prefixes) into my DropDownList no problem, but I can't seem to query them.
Any ideas?
You've already answered yourself: you need to use also the prefix in the select statement you're executing, like
Select * From Person.Address
Beside that you should not use the sysobject tables, from SQL Server 2005 you have system views that helps you, so you can write a better statement to select tables:
select * From INFORMATION_SCHEMA.TABLES
Check also this article.
Regards
Massimo

Categories