ASP.Net C# - Using a DataTable - c#

I hope this makes sense. I'm familiar with using DataReaders, however I think using a DataTable in this instance makes sense. I have a database table that includes 'questions' and I want to implement a facility to be able to browse through the questions on a website.
So, simplified, there's a label to output the question (lblQuestion) and a back button and a forward button to go to the previous/next question.
Is the following code/saving the DataTable in a Session object the most efficient way to do this?
protected void Page_Load(object sender, EventArgs e)
{
if (!(Page.IsPostBack))
{
getQuestions();
int questionCounter = 1;
ViewState["questionCounter"] = questionCounter;
lblCounter.Text = questionCounter.ToString();
}
}
protected void getQuestions()
{
string connStr = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
MySqlConnection conn = new MySqlConnection(connStr);
MySqlDataReader reader;
string cmdText = "SELECT * FROM questions WHERE approved='Y' AND module_id=1";
MySqlCommand cmd = new MySqlCommand(cmdText, conn);
try
{
conn.Open();
reader = cmd.ExecuteReader();
DataTable dt1 = new DataTable();
dt1.Load(reader);
reader.Close();
Session["dt1"] = dt1;
lblQuestion.Text = dt1.Rows[0]["question"].ToString();
}
catch
{
lblError.Text = "Database connection error - failed to get questions.";
}
finally
{
conn.Close();
}
}
protected void btnNext_Click(object sender, EventArgs e)
{
int questionCounter = Convert.ToInt32(ViewState["questionCounter"]);
DataTable dt1 = (DataTable)Session["dt1"];
if (questionCounter < dt1.Rows.Count)
{
questionCounter++;
lblQuestion.Text = dt1.Rows[questionCounter-1]["question"].ToString();
ViewState["questionCounter"] = questionCounter;
lblCounter.Text = questionCounter.ToString();
}
else
{
lblQuestion.Text = "the end of questions.";
}
}
protected void btnBack_Click(object sender, EventArgs e)
{
int questionCounter = Convert.ToInt32(ViewState["questionCounter"]);
DataTable dt1 = (DataTable)Session["dt1"];
if (questionCounter > 1)
{
questionCounter--;
lblQuestion.Text = dt1.Rows[questionCounter-1]["question"].ToString();
ViewState["questionCounter"] = questionCounter;
lblCounter.Text = questionCounter.ToString();
}
else
{
lblQuestion.Text = "the beginning of questions.";
}
}

In general you shouldn't really store data in the session object. The reason for this is that every user will have that datatable stored against them. That means if the table is 1MB and you have 100 users, then you will have 100MB of session objects. Obviously this is not very good.
Instead what you can do is use the Application Cache. This is different to the session store as it is shared across all users (and is cleared upon IIS Application Pool recycle). It's as easy as saving to the session object, but you just use the Cache instead.
In your code you should check to see if the cache contains the questions table already. If it does then just return it, otherwise do your SQL query to populate it. If your questions tend to change regularly then consider using expiration timers that will automatically remove the object after a period of time you define. Then the next time a user queries the question table, your code will detect that it has been removed and request it again.

Related

The variable is not incremental after click the "Next" button using C# with asp.net web application

I am trying to filter records from database and show them on textfields one by one by clicking the "Next" button. I put the code "pos++" to increase the index whenever the "Next" button is clicked, so that each record will show on the textfield.
But it only navigates the first 2 records on textfield, it stops when I click "Next" button again. For the code below, "..." means some code is omitted for easy reading purpose.
I try to catch the index with lbpos.Text = pos.ToString();, the index is up to 1, it is not incremented, I think this is the reason it shows only 2 records on textfield.
I use session to retain the state of the choosing records in dataTable Session["session"] = dt;, and retrieve it with dt = (DataTable)Session["session"];
I try to see if datasource has data with
if (Session["session"] == null)
lbsession.Text = "Null";
but it seems it is not null because it does not show the string "null".
Code:
namespace WebApplication1
{
public partial class mypage : System.Web.UI.Page
{
private DataSet ds = new DataSet();
private SqlDataAdapter adapter;
DataTable dt = new DataTable();
string connString = #"Data Source=DESKTOP-
J12F5GP\sqlexpress;Initial Catalog=16ThingsDB;Integrated Security=True";
int pos;
...
protected void btnSearch_Click(object sender, EventArgs e)
{
...
else if (droplistsearchby.SelectedValue == "Talked")
{
string query = "select * from [dbo].[Students] WHERE
Talked like '%' + #Talked + '%'";
com.CommandText = query;
com.Connection = con;
com.Parameters.AddWithValue("Talked", txtsearchby.Text);
adapter = new SqlDataAdapter(com);
adapter.Fill(dt);
GridView1.DataSource = dt;
Session["session"] = dt;
GridView1.AutoGenerateColumns = true;
GridView1.DataBind();
lbrequestrows.Text = "No of records in this table: " + GridView1.Rows.Count.ToString();
showData(pos);
con.Close();
}
}
public void showData(int index)
{
txtid.Text = dt.Rows[index][0].ToString();
txtfirstname.Text = dt.Rows[index][1].ToString();
txtlastname.Text = dt.Rows[index][2].ToString();
...
txttitle.Text = dt.Rows[index][4].ToString();
txtdept.Text = dt.Rows[index][5].ToString();
txtcompany.Text = dt.Rows[index][6].ToString();
}
protected void btnNext_Click(object sender, EventArgs e)
{
dt = (DataTable)Session["session"];
pos++;
lbpos.Text = pos.ToString();
if (Session["session"] == null)
lbsession.Text = "Null";
lblError.Text = (dt.Rows.Count).ToString();
if (pos >= dt.Rows.Count)
pos = 0;
showData(pos);
}
}
I expect to navigate the records on textfields when button "Next".
When an HTTP request comes in, an instance of the Page class is created. When the page is finished processing, an HTTP response is sent to the client and that instance of the page class disappears. When the postback occurs, the a new instance of the Page class is created. Thus storing variables in local fields does not make sense in a Page class. You will need to store your state elsewhere.
ViewState, Session, hidden form values on the page, a database. These are all options available for storing application state.
You can Try by Initializing the pos variable to int pos=0;

How to run a table from a database and create an object with each field of it

I have a table from which I want to take all fields and convert them in an object, I want to pass from a data to another instead of just show the first one.
This code makes the query and it's supposed to take ALL the table fields.
public static DataTable Search()
{
DataTable db = new DataTable();
try
{
NpgsqlConnection connection;
connection = new NpgsqlConnection("Server=localhost;Port=5432;User Id=postgres;Password=root;Database=test;");
connection.Open();
string consultation = "SELECT boxnum, partnum, quantity, date, nivel, row, deep FROM market ma INNER JOIN connection cn ON ma.boxnumm = cn.boxnum;";
NpgsqlCommand com = new NpgsqlCommand(consultation, connection);
NpgsqlDataAdapter adap = new NpgsqlDataAdapter(com);
adap.Fill(db);
connection.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
return db;
}
Then in a timer I try to start the object and reload the object from the database, but it just shows the first data and then it creates infinite objects with it.
private void timer2_Tick(object sender, EventArgs e)
{
DataTable dt = getTable.search();
if (dt.Rows.Count > 0)
{
DataRow row = dt.Rows[0];
partnum = Convert.ToString(row["partnum"]);
quantity = Convert.ToInt32(row["quantity"]);
deep = Convert.ToInt32(row["deep"]);
row = Convert.ToInt32(row["row"]);
boxnum = Convert.ToInt32(row["boxnum"]);
aux = Convert.ToDateTime(row["date"]);
boxs.Add(new Box(partnum,quantity,deep,aux,row,boxnum));
}
else
{
}
}
I want to check the table and create an object from each field of it, until now, my code check the table and create the object but it's creating a new one over the old each second.

Insert INTO error when transferring data from DataTable to Access Database

I have a datatable with 5 columns, (Song, Artist, Album, Genre, Time) the table allows for me to enter as many rows as i want to create a playlist of music, when the user sees fit they can click the button export the data to access. My access database has a table named "Playlist" with the same 5 columns as the data table. When trying to transfer the data, i keep getting the exception error for the Insert INTO statement and I have no idea why because i am using a commandBuilder. I have attached my class and method thats performing this action.
Please advise!
public void ExportPlaylistToAccess(DataTable playlist)
{
// open connection to the database pathed to
String connection = #"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data source= D:\CIS 465\Final Project\VirtualiPod\iTunesPlaylistDatabase.accdb";
using (OleDbConnection con = new OleDbConnection(connection))
{
var adapter = new OleDbDataAdapter();
adapter.SelectCommand = new OleDbCommand("SELECT * from [Playlist]", con);
var cbr = new OleDbCommandBuilder(adapter);
cbr.GetDeleteCommand();
cbr.GetInsertCommand();
cbr.GetUpdateCommand();
try
{
con.Open();
adapter.Update(playlist);
}
catch (OleDbException ex)
{
MessageBox.Show(ex.Message, "Database Error");
}
catch (Exception x)
{
MessageBox.Show(x.Message, "Exception Error");
}
}
dataTable creation
private void createPlaylist_Click(object sender, EventArgs e)
{
if (playlist.Rows.Count == 0)
{
playlist.Columns.Add("Song");
playlist.Columns.Add("Artist");
playlist.Columns.Add("Album");
playlist.Columns.Add("Genre");
playlist.Columns.Add("Time");
dataGridView1.DataSource = playlist;
}
else if (playlist.Rows.Count > 0)
{
MessageBox.Show("Please clear your current playlist to create a new one.");
}
}
// adds song to playlist for user upon click
private void addToPlaylist_Click(object sender, EventArgs e)
{
IITTrackCollection tracks = app.LibraryPlaylist.Tracks;
IITTrack currentTrack = app.CurrentTrack;
DataRow newRow;
newRow = playlist.NewRow();
newRow["Song"] = currentTrack.Name;
newRow["Artist"] = currentTrack.Artist;
newRow["Album"] = currentTrack.Album;
newRow["Genre"] = currentTrack.Genre;
newRow["Time"] = currentTrack.Time;
playlist.Rows.Add(newRow);
dataGridView1.DataSource = playlist;
}
Time is a reserved word. For some reason the command builder does not surround fields that are database reserved words (time, date, long etc.) with brackets [time] which would allow the insert query to work correctly. Without the brackets the insert will fail as the SQL compiler does not know if the string time is a sql command or a field name. The only solution I've found is to rename your database fields so that they are not in conflict with the database reserved names. Hopefully MS will eventually fix this mistake.

Update datagridview from SQL in the form close event

I have a form with a dataGridView1 that is updated from SQL database.
What i am trying to do is: when the user presses the Close button I am checking a variable and if condition is true then I cancel the from close (e.Cancel = true;) and i want to display some data in the datagrid.
Whatever I do the grid is not updating. I am calling a "private void update()" to update the grid from SQL but after I cancel the form close event but it does not seem to work.
I have tried refreshing the form, refreshing the datagrid with no result.
After the form_Close even finishes, and the datagrid is empty, if i press a button that calls the same "private void update()" it works and the data is shown in the datagrid.
Thank you for your help.
EDIT1: to give you more details
I tried the code on FormClosing but I get no result.
The code I'm using is:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
getdata_fromSQL();//this private void gets some data from sql into the datagrid
if (dataGridView1.RowCount > 0)//check if i have at least one row retreved
{
// Cancel the Closing event and tell the user
MessageBox.Show("Please check the data before leaving.");
e.Cancel = true;
getdata_fromSQL();// retrieve the data again for the user to see (this part is not working
}
}
This is how the data is retrieved.
private void getdata_fromSQL()
{
SqlConnection con = new SqlConnection("connection string"); //defining connection
con.Open();
string sql_command = "Select * from Test_Table where [Check] is null";
SqlCommand command = new SqlCommand(sql_command, con); // defining the command
DataSet set = new DataSet("SQL_table");
SqlDataAdaptersda = new SqlDataAdapter(command); //defining the adapter and make it accept changes
sda.AcceptChangesDuringFill = true;
sda.AcceptChangesDuringUpdate = true;
set.Clear(); //just to make sure my adapter is empty
cmdBuilder = new SqlCommandBuilder(sda); //creating the command builder so I can save the changes
sda.Fill(set, "SQL_table"); // fill the dataset
dataGridView1.DataSource = set;
dataGridView1.DataMember = "SQL_table"; //fill datagrid
dataGridView1.CellValueChanged -= dataGridView1_CellValueChanged;
dataGridView1.CellValueChanged += dataGridView1_CellValueChanged; //look for cell value changed (I am using this in other scope)
}
After I cancel the close and try to update the datagrid again, it remains blank.
EDIT2: #Gami
your code does this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (i == 1)
{
MessageBox.Show("Hello");
e.Cancel = true;
getRefresh();
}
}
and your refresh is this:
private void getRefresh()
{
SqlConnection con = new SqlConnection(#"user id=testuser;" +
"password=testpass;Data Source=SERVER;" +
// "Trusted_Connection=yes;" +
"Initial Catalog=Partner_database; " +
"connection timeout=30"); //defining connection
con.Open();
SqlCommandBuilder cmdBuilder;
string sql_command = "Select * from Test_table where [Check] is null";
SqlCommand command = new SqlCommand(sql_command, con); // defining the command
DataSet set = new DataSet("SQL_table");
SqlDataAdapter sda = new SqlDataAdapter(command); //defining the adapter and make it accept changes
sda.AcceptChangesDuringFill = true;
sda.AcceptChangesDuringUpdate = true;
set.Clear(); //just to make sure my adapter is empty
cmdBuilder = new SqlCommandBuilder(sda); //creating the command builder so I can save the changes
sda.Fill(set,"SQL_table"); // fill the dataset
dataGridView1.DataSource = set;
dataGridView1.DataMember = "SQL_table"; //fill datagrid
}
My code is the one above. We are both using the FormClosing event, we both cancel the close process, and then call the refresh.
This is the SQL table:
This is the datasource for my datagrid:
Try to write code in Form1_FormClosing() event of form
example is
namespace canceldemo
{
public partial class Form1 : Form
{
int i = 1;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (i == 1)
{
MessageBox.Show("Hello");
e.Cancel = true;
}
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = (i+10).ToString();
}
}
}
I have found the issue.
Looks like it was a mistake on my side, I still don't know why is not working the way it is, but know the cause.
I am using code to get the logged user name from Windows, and when i filter the SQL table i filter on the user as well.
This is the code to retrieve the user:
private string getUserDisplayName()
{
var username = new StringBuilder(1024);
uint userNameSize = (uint)username.Capacity;
// try to get display name and convert from "Last, First" to "First Last" if necessary
if (0 != GetUserNameEx(3, username, ref userNameSize))
return Regex.Replace(username.ToString(), #"(\S+), (\S+)", "$2 $1");
// get SAM compatible name <server/machine>\\<username>
if (0 != GetUserNameEx(2, username, ref userNameSize))
{
IntPtr bufPtr;
try
{
string domain = Regex.Replace(username.ToString(), #"(.+)\\.+", #"$1");
DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, domain);
DomainController dc = DomainController.FindOne(context);
if (0 == NetUserGetInfo(dc.IPAddress,
Regex.Replace(username.ToString(), #".+\\(.+)", "$1"),
10, out bufPtr))
{
var userInfo = (USER_INFO_10)Marshal.PtrToStructure(bufPtr, typeof(USER_INFO_10));
return Regex.Replace(userInfo.usri10_full_name, #"(\S+), (\S+)", "$2 $1");
}
}
finally
{
NetApiBufferFree(out bufPtr);
}
}
return String.Empty;
}
In my FormClosing block i am calling this private void and use this as filter to sql.
user_name = getUserDisplayName();
string sql_command = "Select * from Test_table where [Check] is null and [User Name] = '" + user_name + "'";
When removing the getUserDisplayName() call it works. When i call it it does not refresh the grid even if it runs with no error.
Does it cut my connections when i press the close button? I think this is another question and here is off topic.

How to use SqlCommand and DataAdapter in order to manipulate SQL Server with C#

I create table called Department with 2 columns Department ID which is auto increment and Department Name
I create Navigate_Department() in order to walk through the department rows
System.Data.SqlClient.SqlConnection con;
DataSet Dep_ds;
System.Data.SqlClient.SqlDataAdapter Dep_da;
int Dep_MaxRows = 0;
int Dep_inc = 0;
private void ILS_Load(object sender, EventArgs e)
{
con = new System.Data.SqlClient.SqlConnection();
con.ConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\ILS_DB.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
con.Open();
Dep_ds = new DataSet();
string sql2 = "select * from Department order by DepartmentID";
Dep_da = new System.Data.SqlClient.SqlDataAdapter(sql2, con);
Dep_da.Fill(Dep_ds, "Department");
Navigate_Department();
Dep_MaxRows = Dep_ds.Tables["Department"].Rows.Count;
}
private void Navigate_Department()
{
DataRow dRow = Dep_ds.Tables["Department"].Rows[Dep_inc];
Dep_ID.Text =dRow.ItemArray.GetValue(0).ToString();
Dep_Name.Text = dRow.ItemArray.GetValue(1).ToString();
}
private void Move_Next_Click(object sender, EventArgs e)
{
if (Dep_inc != Dep_MaxRows-1)
{
Dep_inc++;
Navigate_Department();
}
else
{
MessageBox.Show("No More Records");
}
}
private void Move_back_Click(object sender, EventArgs e)
{
if (Dep_inc > 0)
{
Dep_inc--;
Navigate_Department();
}
else
{
MessageBox.Show("First Record");
}
}
private void Dep_Clear_Click(object sender, EventArgs e)
{
Dep_ID.Clear();
Dep_Name.Clear();
}
private void Dep_Add_Click(object sender, EventArgs e)
{
try
{
SqlCommand insCmd = new SqlCommand("insert into dbo.Department (DepartmentName) values ('" + Dep_Name.Text + "')", con);
Dep_da.InsertCommand = insCmd;
Dep_MaxRows = Dep_MaxRows + 1;
Dep_inc = Dep_MaxRows - 1;
Dep_Max.Text = Dep_MaxRows.ToString();
Dep_Current.Text = (Dep_MaxRows).ToString();
}
catch (Exception exceptionObject)
{
MessageBox.Show(exceptionObject.Message);
}
The problem is:
After I click clear button, I insert the department name into Dep_Name textbox then click add button. The name that I inserted didn’t get saved in the database, and if I click move back then move next in order to see what I inserted, I get a Index out of range exception in the Navigate_Department() method.
So did I make any mistake?
The reason the name you're inserting isn't saved in the database is because you never execute the insert command. Add:
int ret = Dep_da.Update(Dep_ds);
after the
Dep_da.InsertCommand = insCmd;
The variable ret will contain the number of rows successfully updated (inserted) - which in your case should be 1.
To add to what #N. Warfield wrote, if you simply append raw data into your SQL string that a user (or another system, for that matter) provides, you leave yourself wide-open to SQL injection attacks.
Rather than create the insert statement like this you should use the data adapter with an insert command, add a new DataRow instance to the table then use the data adapter to execute the update.
Alternatively you could execute the insert command within Dep_Add_Click by replacing the call to "Dep_da.InsertCommand = insCmd" with "insCmd.ExecuteNonQuery", however this would mean that you would need to re-run the select statement and repopulate the dataset from the database to get the data from the database into the dataset.

Categories

Resources