I am new in coding and i was recently chosen to start training in C# and Asp.Net. I am trying to update an entry into the database but to no avail. The delete, create and read functions work properly. (i know that my code lacks try and catch)
I 've tried different solutions that i found while googling but since i am still training i never fully understand if what i am following is completely correct.
Currently i am able to diplay the fields with their info when navigating to the "Article" i am trying to edit but when i click the Save button nothing happens.
This is in my Article Context file:
public Article EditSingleArticle(int id, int userID, string Title, string Body)
{
Article article = new Article();
using (MySqlConnection conn = GetConnection())
{
MySqlCommand cmdslct = new MySqlCommand("SELECT * FROM Article WHERE ID=#id", conn);
MySqlCommand cmdul = new MySqlCommand("UPDATE Article SET userID=#userID,Title=#Title,Body=#Body WHERE ID=#id", conn);
conn.Open();
cmdslct.Parameters.AddWithValue("#ID", id);
cmdslct.ExecuteNonQuery();
cmdul.Parameters.AddWithValue("#userID", article.userID);
cmdul.Parameters.AddWithValue("#Title", article.Title);
cmdul.Parameters.AddWithValue("#Body", article.Body);
cmdul.ExecuteNonQuery();
using (MySqlDataReader reader = cmdslct.ExecuteReader())
{
while (reader.Read())
{
article = new Article()
{
userID = reader.GetInt32("userID"),
ID = reader.GetInt32("ID"),
Title = reader.GetString("Title"),
Body = reader.GetString("Body")
};
}
}
conn.Close();
}
The code below is in my ArticleController file:
public IActionResult Edit(int id, int userID, string Title, string Body)
{
ArticleContext context = HttpContext.RequestServices.GetService(typeof(ArticleContext)) as ArticleContext;
if (ModelState.IsValid)
{
return View(context.EditSingleArticle(id, userID, Title, Body));
}
return View();
}
I don't get any error messages but the article i am trying to edit never updates
The issue is that article object only have the default empty fields because you are calling cmdslct.ExecuteNonQuery() that does not return data at all, only the number of rows affected by an insert, update, or delete.
So you should call ExecuteReader() that returns an object that can iterate over the entire result set while only keeping one record in memory at a time.
Your code should looks like the following code in order to work as expected:
Article article = new Article();
using (MySqlConnection conn = GetConnection())
{
MySqlCommand cmdslct = new MySqlCommand("SELECT * FROM Article WHERE ID=#id", conn);
MySqlCommand cmdul = new MySqlCommand("UPDATE Article SET userID=#userID,Title=#Title,Body=#Body WHERE ID=#id", conn);
conn.Open();
cmdslct.Parameters.AddWithValue("#ID", id);
using (MySqlDataReader reader = cmdslct.ExecuteReader())
{
while (reader.Read())
{
article = new Article()
{
userID = reader.GetInt32("userID"),
ID = reader.GetInt32("ID"),
Title = reader.GetString("Title"),
Body = reader.GetString("Body")
};
}
}
cmdul.Parameters.AddWithValue("#userID", article.userID);
cmdul.Parameters.AddWithValue("#Title", article.Title);
cmdul.Parameters.AddWithValue("#Body", article.Body);
cmdul.ExecuteNonQuery();
conn.Close();
}
Regards,
In your update query you are using a where condition which uses parameter #id. But you are not passing the ID argument to your query. Add a argument '#id' for the update query.
Add this line to the code
cmul.Parameters.AddWithValue("#id", id);
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'm trying to upgrade the db from users' input, but it doesn't work...
I'm using this:
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(strcon);
SqlCommand cmd = new SqlCommand();
SqlCommand ncmd = new SqlCommand("Update Utenti Set Nome = #vnome where [Indirizzo E-Mail]=#vem", con);
ncmd.Parameters.AddWithValue("#vem", Session["[Indirizzo E-Mail]"].ToString());
ncmd.Parameters.AddWithValue("#vnome", TextBox2.Text);
ncmd.Connection = con;
con.Open();
ncmd.ExecuteNonQuery();
con.Close();
Label2.Text = "Dati aggiornati con successo!";
Response.Redirect("~/ModificaDati.aspx");
}
When I click on the button it show me the Label2 text, but in the database the "Nome" is not changed, why?
Thanks before for the answers ^^
I would change your method as below
if (Session["[Indirizzo E-Mail]"] != null &&
!string.IsNullOrEmpty(Session["[Indirizzo E-Mail]"].ToString()) &&
!string.IsNullOrEmpty(TextBox2.Text))
{
string vem = Session["[Indirizzo E-Mail]"].ToString();
using (var con = new SqlConnection(strcon))
using (var ncmd = new SqlCommand("Update Utenti Set Nome = #vnome where [Indirizzo E-Mail]=#vem", con))
{
con.Open();
ncmd.Parameters.AddWithValue("#vem", vem);
ncmd.Parameters.AddWithValue("#vnome", TextBox2.Text);
int rows = ncmd.ExecuteNonQuery();
Label2.Text = rows + " Dati aggiornati con successo!";
}
}
Response.Redirect("~/ModificaDati.aspx");
Added input validation, session values can be null, better to check before you update database
when you create SqlCommand you can give the connection, no need to set it again
make sure your SQL is valid
use using statements for disposable objects like SqlConnection, SqlCommand
Your code looks ok. Just make sure you check if SQL is correct as Damith already suggested.
Another thing I’s recommend is additionally validating your parameters for data type correctness before executing the query.
Using this approach you’ll probably avoid a lot of unnecessary exceptions and also be able to provide more user friendly messages. Of course this only applies if user input is non text type
//Data Type verification
DateTime tmp;
if (!DateTime.TryParse(Label2.Text.Trim(), out tmp))
{
//Show error message that this is not a correct data type
}
Open your connection first
con.Open();
ncmd.Connection = con;
Hope it helps
I am trying to figure out how to pass an object that i have created in the dll file to the code behind file in my web application.
Here is the class I made:
public class BugReports
{
public object userRoleDropDown()
{
SqlConnection conn;
SqlCommand userRoleComm;
SqlDataReader reader;
string connectionSrting = ConfigurationManager.ConnectionStrings["BugReports"].ConnectionString;
conn = new SqlConnection(connectionSrting);
userRoleComm = new SqlCommand(
"SELECT UserRoleID, UserRoleName FROM userRoles", conn);
try
{
conn.Open();
reader = userRoleComm.ExecuteReader();
/*
addUserRollDropDownList.DataSource = reader;
addUserRollDropDownList.DataValueField = "UserRoleID";
addUserRollDropDownList.DataTextField = "UserRoleName";
addUserRollDropDownList.DataBind();*/
reader.Close();
}
finally
{
conn.Close();
}
return reader;
}
}
I then want to use the reader in my cs file but where do I start? I thought a simple;
BugReports reader = new BugReports();
would work but nothing comes up.
Assuming you have everything wired up correctly with your project reference to the dll and a using statement in your code file.
BugReports reader = new BugReports();
That line only gets an instance of your BugReports class, in order to make it do some work you need to call your method.
reader.userRoleDropDown();
I'm not sure why you are returning the SqlDataReader reader that you've already closed, it is no longer any use. Also you are selecting data by calling reader = userRoleComm.ExecuteReader(); but all the work is commented out, not sure if that is intentional.
Edit:
You may be better off using a SQLDataAdapter since your UI controls won't be visible to your class, and you can't access the data in the SQLDataReader after it's closed.
public DataSet userRoleDropDown()
{
string connectionSrting = ConfigurationManager.ConnectionStrings["BugReports"].ConnectionString;
string queryString = "SELECT UserRoleID, UserRoleName FROM userRoles";
using (SqlConnection connection = new SqlConnection(connectionSrting))
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand( queryString, connection);
adapter.Fill(dataset);
return dataset;
}
}
Then you can do what ever you like with the selected data from your application.
More info on the important classes used here: SqlDataAdapter DataSet
If you have built in an assembly, you should go to the ASP.Net Application and then add a reference to the assembly [dll file] and then add a using statement like the one below
namespace CustomLibrary;
{
public class BugReports
{
}
}
In the asp.net aspx.cs file,
using CustomLibrary;
BugReports reader = new BugReports();
Post here your understandings or any other updates on the implementation.
I want to ask more to show data from SQL Server to WinForm using a datagrid.
I've been creating a datagrid and the stored procedure to show data is
ALTER PROC [dbo].[SP_GetData]
AS
SELECT nama , nim
FROM tabledata
and I've created the function to access the database and the stored procedure in C#
string Sp_Name = "dbo.SP_GetData";
SqlConnection SqlCon = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DBMahasiswa;Data Source=.");
SqlCon.Open();
SqlCommand SqlCom = new SqlCommand(Sp_Name , SqlCon);
SqlCom.CommandType = CommandType.StoredProcedure;
List<mahasiswaData> listMahasiswa = new List<mahasiswaData>();
using (SqlDataReader sqlDataReader = SqlCom.ExecuteReader())
{
if (sqlDataReader.HasRows)
{
while (sqlDataReader.Read())
{
mahasiswaData DataMhs = new mahasiswaData();
DataMhs.Nama = sqlDataReader["Name"].ToString();
DataMhs.Umur = Convert.ToInt32(sqlDataReader["Age"]);
listMahasiswa.Add(DataMhs);
}
}
}
SqlCon.Close();
return listMahasiswa;
and finally, in the show button I add this code
dgvmahasiswa.DataSource = new MahasiswaDB().LoadMahasiswa();
Could somebody tell me where the fault is or the alternatives one?
Thank You So Much! :D
Some things to think about:
At the moment, if your code runs into exceptions, you'll leave a
SqlConnection hanging around; you've used the using pattern for your
SqlDataReader; you should extend it to all of your disposable
objects.
You are swallowing exceptions; if your query fails, the connection
cannot be made, or something else happens, you'll never really know - your function will just return null.
Is it possible for name or age to be null? Age to be non-numeric?
There's no test for any unexpected values, which you'll also never
know about.
If you don't have any records, you'll return an empty list. Is this
desired? Or would you prefer to know there were no records?
You might prefer to look at something like this:
public List<mahasiswaData> GetData(){
List<mahasiswaData> gridData = new List<mahasiswaData>();
try{
using(SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DBMahasiswa;Data Source=."))
{
using(SqlCommand command = new SqlCommand())
{
command.Connection = conn;
command.CommandType = CommandType.StoredProcedure;
command.Text = "dbo.SP_GetData";
using(SqlDataReader reader = command.ExecuteReader())
{
if(reader.HasRows){
while(reader.Read())
{
object rawName = reader.GetValue(reader.GetOrdinal("Name"));
object rawAge = reader.GetValue(reader.GetOrdinal("Age"));
if(rawName == DBNull.Value || rawAge == DBNull.Value)
{
//Use logging to indicate name or age is null and continue onto the next record
continue;
}
//Use the object intializer syntax to create a mahasiswaData object inline for simplicity
gridData.Add(new mahasiswaData()
{
Nama = Convert.ToString(rawName),
Umur = Convert.ToInt32(rawAge)
});
}
}
else{
//Use logging or similar to record that there are no rows. You may also want to raise an exception if this is important.
}
}
}
}
}
catch(Exception e)
{
//Use your favourite logging implementation here to record the error. Many projects use log4Net
throw; //Throw the error - display and explain to the end user or caller that something has gone wrong!
}
return gridData;
}
Note that if you are sure that age or name will never be null then you can simplify the middle section:
while (reader.Read())
{
//Use the object intializer syntax to create a mahasiswaData object inline for simplicity
gridData.Add(new mahasiswaData()
{
Nama = reader.GetString(reader.GetOrdinal("Name")),
Umur = reader.GetInt32(reader.GetOrdinal("Age"))
});
}
public ActionResult PostMessage(string message)
{
MessageController mc = new MessageController();
mc.postMessage(message);
}
What can I do here to prevent SQL injection in this string? This is the only input the user is given on the entire page. I am familiar with the some PHP techniques, but how would I protect myself in c#?
Thanks!
edit:
connection.Open();
SqlCommand command = new SqlCommand("[dbo].[tblMessages_Insert]", connection);
command.CommandType = CommandType.StoredProcedure;
// params
SqlParameter messageText = new SqlParameter("#messageText", SqlDbType.VarChar);
messageText.Value = message;
// add params
command.Parameters.Add(messageText);
rows = command.ExecuteNonQuery();
It seems to me that you're already protecting against injection; you're using parameters.