I'm currently creating a small application using Windows Forms and SQLite. After reading some tutorials I implemented this method for data retrieval:
public DataTable GetDataTable(ref SQLiteDataAdapter adapter, string sql)
{
DataTable dt = new DataTable();
// Connect to database.
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
// Create database adapter using specified query
using (adapter = new SQLiteDataAdapter(sql, connection))
// Create command builder to generate SQL update, insert and delete commands
using (SQLiteCommandBuilder command = new SQLiteCommandBuilder(adapter))
{
// Populate datatable to return, using the database adapter
adapter.Fill(dt);
}
return dt;
}
(As well as another GetDataTable which doesn't take an SQLiteDataAdapter as parameter)
I have three classes, let's call them UI, Link and Database. The UI does nothing but displaying the data and raising events upon user interaction. The Link creates the Database and a SQLiteDataAdapter, retrieves a data table through the method mentioned above, and binds it to a data grid view on the UI. The user cannot alter the table through the data grid view, but should do so through some text boxes. (does this make binding the table to the dgv obosolete?)
What's the best way to get the user input from the text boxes to the database, using the adapter? Or should I use DataReader and some Insert method instead of an adapter?
As of know, the UI exposes its controls through Get-methods. Is there a better solution?
private void Initialize()
{
// Subscribe to userInterface events
userInterface.DataGridViewSelectionChanged += new EventHandler(userInterface_DataGridViewSelectionChanged);
userInterface.NewClicked += new EventHandler(userInterface_NewClicked);
userInterface.SaveClicked += new EventHandler(userInterface_SaveClicked);
// Get dataGridView from userInterface and bind to database
bindingSource = new BindingSource();
bindingSource.DataSource = database.GetDataTable(ref adapter, "SELECT * FROM SomeTable");
userInterface.GetDataGridView().DataSource = bindingSource;
}
void userInterface_DataGridViewSelectionChanged(object sender, EventArgs e)
{
if (userInterface.GetDataGridView().SelectedRows.Count != 0)
{
DataGridViewRow row = userInterface.GetDataGridView().SelectedRows[0];
userInterface.GetIDTextBox().Text = row.Cells["PrimaryKey].Value.ToString();
userInterface.GetOtherIDTextBox().Text = row.Cells["ForeignKey"].Value.ToString();
DataTable dt = database.GetDataTable("SELECT * from SomeTable WHERE ForeignKey=" + row.Cells["ForeignKey"].Value);
userInterface.GetLastNameTextBox().Text = dt.Rows[0]["LastName"].ToString();
userInterface.GetFirstNameTextBox().Text = dt.Rows[0]["FirstName"].ToString();
userInterface.GetCompanyTextBox().Text = dt.Rows[0]["Company"].ToString();
}
}
void userInterface_NewClicked(object sender, EventArgs e)
{
// Get all text boxes and clear them
// Let the UI take care of this by itself?
}
void userInterface_SaveClicked(object sender, EventArgs e)
{
// Get text/data from all text boxes and insert (or update if editing table) into database
// adapter.Update(...)?
}
Cheers!
INSERT, UPDATE and DELETE operations are the working of a DbCommand. You need a different method that takes the sql string and a collection of SQLiteParameter that you use for the INSERT.
I will try to write some pseudocode for the INSERT operation
public class MyHelperClass
{
public static int InsertCommand(string sql, SQLiteParameter[] parameters)
{
int result = 0;
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
using (SQLiteCommand cmd = new SQLiteCommand(sql, connection))
{
cmd.Parameters.AddRange(parameters);
result = cmd.ExecuteNonQuery();
}
return result;
}
}
Now you have to build the parameter array to pass to the help method and this should be done from your UI code
string sqlCommand = "INSERT INTO table1 (FirstName, LastName) VALUES (#fName, #lName)";
SQLiteParameter[] p = new SQLiteParameter[2];
p[0] = new SQLiteParameter("#fName", TextBox1.Text);
p[1] = new SQLiteParameter("#lName", TextBox2.Text);
int rowAdded = MyHelperClass,InsertCommand(sql, p);
The operation for the UPDATE and DELETE command are similar. Also I suggest you to add a version of your GetDataTable that accepts a parameter array instead of building sql commands with string concatenation. As repetead innumerable times here string concatenation leads to errors and, worst of all, to weak code easily exposed to sql injection.
Related
For reference, I am new to C#/WPF/PostgreSQL and I am trying to create a project for practice, however I've hit a bit of a roadblock. I found this earlier and tried following along with the answers (I understand it isn't 1 to 1) with my own code: Retrieving data from database in WPF Desktop application but it didn't work in my case.
I am creating a simple recipe app where a user can create a recipe (e.g., put in the title, steps, things they need, etc.) and on the home screen, they can see a link to the recipe that was saved, which would take them to the Recipe Screen to be displayed if clicked. I am using PostgreSQL for my database and I do see the correct information on there after the user would submit all of the necessary info, I just need to retrieve it and put it in a data grid possibly? Unless there is a better way other than a data grid.
Regardless, I plan to have it shown as a list of just the title of the recipe, where a user can click on it and it would load up the page, but that's something I can tackle another time if that is outside of the scope in regards to my question.
Here is a visual idea of what I'm trying to accomplish:
Here is my code for the submit button found in the Create Screen if it helps, however I have no idea what to do in terms of actually retrieving that data and then displaying it on my Home Screen.
private static NpgsqlConnection GetConnection()
{
return new NpgsqlConnection(#"Server=localhost;Port=5432;User Id=postgres;Password=123;Database=RecipeProj;");
}
private void SubmitButton_Click(object sender, RoutedEventArgs e)
{
Recipe recipe = new Recipe();
recipe.Title = TitleBox.Text;
recipe.Step1 = StepBox1.Text;
recipe.Step2 = StepBox2.Text;
recipe.Step3 = StepBox3.Text;
recipe.Step4 = StepBox4.Text;
recipe.Step5 = StepBox5.Text;
recipe.Step6 = StepBox6.Text;
recipe.Ingredients = IngredientBox.Text;
recipe.Tools = ToolBox.Text;
recipe.Notes = NoteBox.Text;
void InsertRecord()
{
using (NpgsqlConnection con = GetConnection())
{
string query = #"insert into public.Recipes(Title, Ingredients, Tools, Notes, StepOne, StepTwo, StepThree, StepFour, StepFive, StepSix)
values(#Title, #Ingredients, #Tools, #Notes, #StepOne, #StepTwo, #StepThree, #StepFour, #StepFive, #StepSix)";
NpgsqlCommand cmd = new NpgsqlCommand(query, con);
cmd.Parameters.AddWithValue("#Title", recipe.Title);
cmd.Parameters.AddWithValue("#Ingredients", recipe.Ingredients);
cmd.Parameters.AddWithValue("#Tools", recipe.Tools);
cmd.Parameters.AddWithValue("#Notes", recipe.Notes);
cmd.Parameters.AddWithValue("#StepOne", recipe.Step1);
cmd.Parameters.AddWithValue("#StepTwo", recipe.Step2);
cmd.Parameters.AddWithValue("#StepThree", recipe.Step3);
cmd.Parameters.AddWithValue("#StepFour", recipe.Step4);
cmd.Parameters.AddWithValue("#StepFive", recipe.Step5);
cmd.Parameters.AddWithValue("#StepSix", recipe.Step6);
con.Open();
int n = cmd.ExecuteNonQuery();
if (n == 1)
{
MessageBox.Show("Record Inserted");
TitleBox.Text = IngredientBox.Text = ToolBox.Text = NoteBox.Text = StepBox1.Text = StepBox2.Text = StepBox3.Text = StepBox4.Text = StepBox5.Text = StepBox6.Text = null;
}
con.Close();
}
}
InsertRecord();
}
string query = #"select * from Recipes";
NpgsqlCommand cmd = new NpgsqlCommand(query, con);
con.Open();
var reader = cmd.ExecuteReader();
var recipes = new List<Recipe>();
while(reader.Read()){
//Recipe is just a POCO that represents an entire
//row inside your Recipes table.
var recipe = new Recipe(){
Title = reader.GetString(reader.GetOrdinal("Title")),
//So on and so forth.
//...
};
recipes.Add(recipe);
}
con.Close();
You can use this same exact query to fill in a List of titles and a DataGrid that shows all the contents of a recipe.
I am working in C#. I have a windows form with a data grid view that lists items for the user to review. The data source for the data grid view is a data table
public DataTable GetSFNoDFInventory()
{
// DECLARATIONS
using (DataTable toReturn = new DataTable())
{
//string updateProcName;
//SqlCommand cmdUpdate;
// Create and prepare a command object to be used for the SELECT statement of the data adapter.
using (SqlCommand cmdSelect = new SqlCommand
{
Connection = new SqlConnection(ConString),
CommandType = CommandType.StoredProcedure,
CommandText = "usr.uspSFNoDFGetInventoryReview"
})
{
using (SqlDataAdapter da = new SqlDataAdapter(cmdSelect))
{
da.Fill(toReturn);
}
}
return toReturn;
}
}
The rows in the data grid view have two check boxes for the user to select to update the row. One check box drops the row from further processing. The other flags the record to be included in an email message. I'm trying to collect the updated rows to update the SQL database table using a data view and SQL data adapter.
private void btnSaveDropsAndEmails_Click(object sender, EventArgs e)
{
DataView vwDropSCCF = new DataView(Shared.DtReviewClaims.DefaultView.ToTable("drpSCCF"), "DropRcd = 1", "", DataViewRowState.ModifiedCurrent);
if (dgReviewClaims.EndEdit())
{
Shared.SqlWrapper.UpdateDropEmail(vwDropSCCF);
}
}
However, the count property for vwDropSCCF is zero (0). What am I doing wrong?
I'm new to databases and I'm facing a roadblock. I'm getting an error when trying to update my database.
Also note that my database is SQL Server CE 4.0, if that helps.
private void Form1_Load(object sender, EventArgs e)
{
// Create a connection to the file datafile.sdf in the program folder
string dbfile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\userDtbs.sdf";
SqlCeConnection connection = new SqlCeConnection("datasource=" + dbfile);
// Read all rows from the table test_table into a dataset (note, the adapter automatically opens the connection)
SqlCeDataAdapter adapter = new SqlCeDataAdapter("SELECT * FROM history", connection);
DataSet data = new DataSet();
adapter.Fill(data);
// Add a row to the test_table (assume that table consists of a text column)
data.Tables[0].Rows.Add(new object[] { null, "Google", "http://www.google.com" });
// Save data back to the databasefile
adapter.Update(data);
// Close
connection.Close();
}
This code's outline is from another question's answer, and I'm trying to test it and get it to work properly. Thanks!
The programs I am using are WAMP server (and its mysql feature in particular) and MS Visual Studio 2010 and I am programming in C#
Basically, here is what I need and can currently do with my application.
I have several datagridview's throughout the project and the first is simple, it loads all data from a specific table in the database at the push of a button. I have another form which I can insert records and have somehow managed to make a delete function which asks the user for 2 fields (first name and last name) and then it places these into a query and carries out the command.
What do I need to do?
I need to be able to implement some way for the form to update the database. I have chosen to do this through a datagridview control so the user can see what they are editting whilst they edit it.
I have the following code which I have tried to update the database based on the data in the datagridview control.
string connString = "server=localhost;User Id=root;database=collegelist;";
MySqlConnection conn = new MySqlConnection(connString);
string selectSQL = "SELECT * FROM collegeemployee";
conn.Open();
MySqlDataAdapter da = new MySqlDataAdapter(selectSQL, conn);
MySqlCommandBuilder builder = new MySqlCommandBuilder(da);
DataTable table = new DataTable();
try
{
dgView2.Rows.RemoveAt(dgView2.CurrentRow.Index);
da.Update(table);
}
catch (Exception exceptionObj)
{
MessageBox.Show(exceptionObj.Message.ToString());
}
the problem with this code (listed in a method obviously) is that while the grid is able to be modified, it is unable to pass the data back to the database.
Instead of updating your database with the empty table what you should do is.
i.Get the datasource . like
ii. Update/synchronize the data source and data adapter
Here is the code it should work, if it doesn't please comment and tell me the problem.
string connString = "server=localhost;User Id=root;database=collegelist;";
MySqlConnection conn = new MySqlConnection(connString);
string selectSQL = "SELECT * FROM collegeemployee";
conn.Open();
MySqlDataAdapter da = new MySqlDataAdapter(selectSQL, conn);
MySqlCommandBuilder builder = new MySqlCommandBuilder(da);
BindingSource BindingSourceToUpdate = (BindingSource)dgView2.DataSource; // because direct casting to data table was failing in VS2o1o
try
{
dgView2.Rows.RemoveAt(dgView2.CurrentRow.Index);
da.Update((DataTable)BindingSourceToUpdate.DataSource);
}
catch(exception)
{
}
conn.close();
Sorry in advance im going to try and explain this as best as possible....
I have 2 asp.net pages one named membermaster and the second named memberdetails. I created a class library which contains 2 functions
My first function returns a list depending on the search result...
I added a linkbutton to the gridviews first column which when clicked it passes through querystring the membershipgen. What i wanted to do is for my second function i created this
public DataTable GetMembers(int MEMBERSHIPGEN)
{
DataTable table = null;
SqlConnection con = null;
SqlCommand cmd = null;
SqlDataAdapter ad = null;
SqlParameter prm = null;
try
{
table = new DataTable();
using (con = new SqlConnection(connectionString))
{
using (cmd = new SqlCommand("usp_getmemberdetail", con))
{
using (ad = new SqlDataAdapter(cmd))
{
prm = new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int);
prm.Value = MEMBERSHIPGEN;
cmd.Parameters.Add(prm);
ad.Fill(table);
}
}
}
}
catch (Exception ex)
{
//write your exception code here
}
return table;
}
In the attempt to try and send the membershipgen to this and it return the results. But once i compile the DLL and add it to my project I am not sure how i would reference this function to populate individual textboxes and labels with the information.
What I am trying to do is when a user clicks the viewdetails button on the gridview I can then use that membershipgen that I passed through querystring to populate the page through a stored procedure but the smarts would be stored in a DLL.
You probably want your method to return a value. Currently the return type is void, so the values it populates internally just go away when the call stack leaves the method. It sounds like you want something like this:
public DataTable GetMembers(int MEMBERSHIPGEN)
Then, in your method, after you've populated the DataTable and exited the using blocks, you'd do something like this:
return table;
This would return the DataTable to whatever called the method. So your page would have something like this:
DataTable table = GetMembers(membershipgen);
So the page would be responsible for:
Get the membershipgen value from the input (query string)
Call the method and get the result of the method
Display the result from the method (bind to a grid? or whatever you're doing to display the data)
And the method is responsible for:
Interact with the database
This is a good first step toward the overall goal of "separation of concerns" which is a very good thing to do. You can continue down this path by always asking yourself what each method, class, etc. should be responsible for. For example, your GetMembers method should also be responsible for ensuring that the value passed to it is valid, or that the value returned from it is not null.
You need to change GetMembers to return data instead of void. If you want to use DataTables, you can just modify your code to this:
public DataTable GetMembers(int MEMBERSHIPGEN)
{
DataTable table = new DataTable();
SqlConnection con = new SqlConnection(connectionString);
using (SqlCommand cmd = new SqlCommand("usp_getmemberdetail", con))
{
using (SqlDataAdapter ad = new SqlDataAdapter(cmd))
{
SqlParameter prm = new SqlParameter("#MEMBERSHIPGEN", SqlDbType.Int);
prm.Value = MEMBERSHIPGEN;
cmd.Parameters.Add(prm);
ad.Fill(table);
return table;
}
Then in your Page_Load it might be something like this (more robust than this hopefully):
{
DataTable table = yourDll.GetMembers(Convert.ToInt32(Request.QueryString["membership"]));
label1.Text = Convert.ToString(table.rows[0]["Name"]);
}
One way to go might be to construct the button so that it navigates to a url along the lines of:
http://localhost/DetailPage.aspx?membershipgen=4
Then in the load of the DetailPage.aspx:
Page_Load(Object sender, EventArgs e)
{
if (!this.IsPostback)
{
int membershipgen;
if (int.TryParse(Request.QueryString["membershipgen"], out membershipgen)
{
//Get the data (replace DataAccess with the name of your data access class).
//Also, you probably want to change GetMembers so it returns the data.
DataTable table = DataAccess.GetMembers(membershipgen);
//TODO: Display the results
}
}
else
{
//Display an error
}
}