Prevent double booking ASP.NET C# - asp.net

I 'am trying to create a vacation planner in asp.net with a SQL Server Express database.
When you add a vacation, you have to add a begintime, endtime and of course who you are.
Is there a way to show an error when there is already vacation with the same time and the same employee as the one they try to add.
This way the system will prevent double vacations. Otherwise Gridview table shows two the same name in two rows with the same days. I want to prevent people from double booking.
Let's say Person A has already booked StartDate = 2016-03-06 and EndDate = 2016-03-10. Than means I want to prevent someone else to book the same StartDate, EndDate, between start and end date, not even earlier date and date between start and end date, I mean for example between 2016-03-01 and 2016-03-06 , 2016-03-07, 2016-03-08... because those dates are between start and end dates.
I tried to create a method like following and then I don't know what to do. I would appreciate for any help
// Noe edited to insert button instead method:
protected void btnInsertVacation_Click(object sender, EventArgs e)
{
string cs = ConfigurationManager.ConnectionStrings["ResursplaneringConnectionString"].ConnectionString;
TextDateTime.Text = Calendar1.SelectedDate.ToShortDateString();
using (SqlConnection con = new SqlConnection(cs))
{
string check = "SELECT EmployeeId, StartDate, EndDate FROM vacation WHERE (EmployeeId = #EmployeeId) AND(StartDate <= #NewEndDate) AND(EndDate >= #NewStartDate)";
SqlCommand cmd = new SqlCommand(check, con);
cmd.Parameters.AddWithValue("#EmployeeId", DropDownEmployee.SelectedValue);
cmd.Parameters.AddWithValue("#NewEndDate", txtStartDate.Text);
cmd.Parameters.AddWithValue("#NewStartDate", txtEnd.Text);
con.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
if (rdr.HasRows)
{
Response.Write("Dubbel booking");
}
else
{
string insertVacation = "INSERT INTO Planering (StartDate, EndDate, EmployeeId ) VALUES (#StartDate, #EndDate, #EmployeeId)";
SqlCommand cmd2 = new SqlCommand(insertVacation, con);
SqlParameter paramPlaneringStart = new SqlParameter("#StartDate", txtStartDate.Text);
cmd2.Parameters.Add(paramPlaneringStart);
SqlParameter paramPlaneringEnd = new SqlParameter("#EndDate", txtEnd.Text);
cmd2.Parameters.Add(paramPlaneringEnd);
SqlParameter paramEmployeeId = new SqlParameter("#EmployeeId", DropDownEmployee.SelectedValue);
cmd2.Parameters.Add(paramEmployeeId);
con.Open();
cmd2.ExecuteNonQuery();
}
}
}
}
}

For some clarity, let's name the arguments of your method NewStartDate and NewEndDate, to avoid confusion with StartDate and EndDate we'll use for the columns in the database table.
What you want to do in your SQL query is:
SELECT EmployeeId, StartDate, EndDate FROM vacation
WHERE
(EmployeeId = #EmployeeId)
AND (StartDate <= #NewEndDate) AND (EndDate >= #NewStartDate)
Try to draw possible overlapping as segments on a piece of paper, and you should see this will do it.
If this returns any rows, do not allow to create the new booking.

Related

How to use select query with textbox value as compare wiith database DATE column

My .aspx markup:
<ajaxToolkit:CalendarExtender ID="CalendarExtender1" PopupButtonID="imgpopup" runat="server" TargetControlID="TextBox11" Format="MM-yyyy" DefaultView="Months" />
<asp:TextBox ID="TextBox11" runat="server">
</asp:TextBox>
.aspx.cs code behind:
string text = "Textbox11.Text";
string s = "SELECT * FROM Stock WHERE MONTH(Date) = 09 AND YEAR(Date) = 2018";
SqlCommand cmd = new SqlCommand(s, conn);
SqlDataAdapter dr = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
dr.Fill(dt);
GridView1.DataSource = dt;
GridView1.DataBind();
This is the query I got everywhere on net, but I don't want to specify the fixed date.... I want to allow the user to select text and according to that date select query should work
Please help me with is problem, as I'm new here.
Please forgive me if I asked the same question again which was asked previously by someone else
I suggest a parameterized query with an inclusive start date and exclusive end date for date range queries. It is best to avoid applying functions to SQL Server columns in a WHERE clause as that prevents indexes (if any) from used efficiently. For example:
var date = DateTime.Now;
if(!DateTime.TryParse(Textbox11.Text, out date))
{
//invalid date handing
return;
}
var startDate = new DateTime(date.Year, date.Month, 1);
var endDate = startDate.AddMonths(1);
string s = "SELECT * FROM Stock WHERE Date >= #StartDate AND Date < #EndDate;";
SqlCommand cmd = new SqlCommand(s, conn);
SqlDataAdapter dr = new SqlDataAdapter(cmd);
cmd.Parameters.Add("#StartDate", SqlDbType.DateTime).Value = startDate;
cmd.Parameters.Add("#EndDate", SqlDbType.DateTime).Value = endDate;
DataTable dt = new DataTable();
dr.Fill(dt);
Also, consider specifying an explict column list with only those columns needed rather than SELECT * as that will reduce unnecessary data transfer and give SQL Server more query optimization options.
Be aware that freeform date parsing is ambiguous. It would be better to restrict user input to a specific date format or use a date picker control that does that for you, allowing TryParseExact on the server side.

Counter variable not working Asp.net webform

I have a method to check if the id exists in a table and if it does insert into another database table; additionally I have label that will display the number of data entered. The query to insert into the database and select from the database works fine; but my problem is I'm not able to count, only receiving a count of 1 at all times. It is not incrementing; my question how do I get the counter to increment rather than just showing 1 at all times. This is what I have so far
protected void btnComplete_Click(object sender, EventArgs e)
{
string id = txtid.Text;
string user = lblUsername.Text;
string date = lblDate.Text;
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["TWCL_OPERATIONSConnectionString"].ToString());
//commands identifying the stored procedure
SqlCommand cmd = new SqlCommand("selectId", conn);
SqlCommand cmd1 = new SqlCommand("CreateUserId", con);
// execute the stored procedures
cmd.CommandType = CommandType.StoredProcedure;
cmd1.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#id", id);
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
cmd1.Parameters.Add("#id", SqlDbType.NVarChar).Value = barcode);
cmd1.Parameters.Add("#Username", SqlDbType.NVarChar).Value = user;
cmd1.Parameters.Add("#Date", SqlDbType.DateTime).Value = date;
counter = counter + 1;
}
reader.Close();
cmd1.ExecuteNonQuery();
lblCount.Text = counter.ToString();
}
else
{
lblError.Text = barcode + " does not exist!!";
}
}
Before you are asked for stored procedures and before giving you a concrete answer, you should fix some problems in your code
1) use using{} blocks with command and connection to make sure they are disposed.
2) In while loop, you are adding parameters to cmd1. Think what will happen if while loop runs more than 1 time!!
Now, if you want to show max in counter, just get max from database !!
In your cmd object you are sending some ID to stored procedure. It will always return 1 record if id is unique in your table.
So your counter is always 1
Solution
Not modifying much of your code, add count(id) as counter in your storedprocedure query returning result of cmd.
And in the while loop assign that to counter variable.
counter = Convert.ToInteger(reader[“counter”].ToString());
Above is not best solution though. As it will count records for all rows and will reduce performance over time.
For best solution, you need to make another command object that executes a query like select count(id) from YourTableName
This will give you number of records in your table.
Edit
From your comment.
You only want total records after inserting from cmd1.
Just do following:
1) In your storedProcedure for cmd 1, write Select Isnull(count(*),0) from YourTableNameHere
2) In your code, use ExecuteScalar instead of ExecuteNonQuery.
var result = cmd1.ExecuteScalar();
lblCount.Text = result.ToString();
Edit 2
You want to keep track of number of records inserted in current session. Use viewstate or session, depending on your requirement to save counter for session Or only untill user stays on current page.
var recordsAdded = cmd1.ExecuteNonQuery();
if(Session[“counter”] == null)
{
Session[“counter”] = 0;
}
if(recordsAdded>0)
{
Session[“counter”] = Convert.ToInteger(Session[“counter”]) + 1;
}
lblCount.Text = Session[“counter”];
Above will keep track of inserted records in session

How can I select a single column from a table in SQL Server & put that value into a variable?

I have a table Registration with many columns. I need to get customer_id and put that value into a variable for use that in a session for moving & use between ASP Webforms. How can I do this?
You can use ExecuteScalar method to get single column value. Below is a very basic example of how to get single column value.
string connectionString = ConfigurationManager.ConnectionStrings["YourConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand("select Customer_id from Registration", con);
con.Open();
string id = cmd.ExecuteScalar().ToString();
}
Here is how you can store the value in session
Session["CustomerID"] = id;
And here is how you can retrieve the value on second page
int ID = 0;
int.TryParse((string)Session["CustomerID"], out ID);

using the querystring parameter in my where clause to generate insert operation

here,using request.Querystring i find the companyname and job title of particular Job.when user logsin using username in texbix.i want the Companyname,jobtitle and username in the same row of a table.But when i generate my query it inserts the (companyName & jobtitle) in the first row and username in second row.How can i fulfill my task.Some people said,i have to keep the companyname and jobtitle in a variable...then execute.
is it a parfect solution?
if it is,how can i do that?
code:
protected void ButtonApply_Click(object sender, EventArgs e) {
String str = Request.QueryString.Get("JobNo");
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
conn.Open();
string apply = "INSERT INTO Company (CompanyName,JobTitle) select CompanyName,JobTitle from Jobs where JobNo='"+str+"'" ;
SqlCommand insertApply = new SqlCommand(apply, conn);
try {
insertApply.ExecuteScalar();
conn.Close();
Response.Redirect("ApplyJob.aspx?JobNo="+str);
}
in the apply.aspx i have following code:
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
conn.Open();
string apply = "INSERT INTO Company (CandidateInformation) Values (#CandidateInformation)" ;
SqlCommand insertApply = new SqlCommand(apply, conn);
insertApply.Parameters.AddWithValue("#CandidateInformation", TextBoxaun.Text);
insertApply.ExecuteNonQuery();
conn.Close();
Response.Redirect("CompanyInfo.aspx");
Inserting two times will always result in two new rows.
You can do it all in the first insert statement:
string apply = "INSERT INTO Company (CompanyName,JobTitle, CandidateInformation) select
CompanyName,JobTitle, #CandidateInformation from Jobs where JobNo=#JobNo ;
SqlCommand insertApply = new SqlCommand(apply, conn);
insertApply.Parameters.AddWithValue("#CandidateInformation",
TextBoxaun.Text);
insertApply.Parameters.AddWithValue("#JobNo", str);
try
{
insertApply.ExecuteScalar();
conn.Close();
Response.Redirect("CompanyInfo.aspx");
}
Then you won't need the second page.
Use
Update Company Set CandidateInformation = #CandidateInformation where JobNo='"+str+"'" ;
instead of
string apply = "INSERTINTO Company (CandidateInformation) Values
(#CandidateInformation)" ;
If you will use Insert statement again, then it will always create new record in the table.
Update is used to update an already existing record of the table.

update database based on gridview value

I have a grid view which uses SQL to calculate information that I need! I want to put this calculated total into another table in database.
SELECT StaffDetails.StaffID, SUM(HolidayRequests.RequestTotalDays) AS Expr1
FROM HolidayRequests INNER JOIN StaffDetails ON HolidayRequests.Username = StaffDetails.UserName
WHERE (StaffDetails.StaffID = #staffID)
GROUP BY StaffDetails.StaffID, HolidayRequests.ApprovalStatus
HAVING (HolidayRequests.ApprovalStatus = N'approved')
It basically calculates the total number of approved holiday requests for a staff member which is correct. I want this number to then update the HolidayTaken field in another table each time a holiday is approved.
I have tried an update however you cannot use an aggregate in an update as I have found...Any idea how else I can do this..
you could load the executed Query into a "DataReader" or "DataSet" and from there load the information to the new Table.
you could do:
SqlConnection conn = "You connection";
SqlCommand cmd = new SqlCommand(conn);
cmd.CommandText = "YOUR QUERY";
SqlDataReader rdr = cmd.ExecuteReader();
String UpdateQuery;
while (rdr.Read())
{
UpdateQuery = "UPDATE table set col1=#Expr1 WHERE condition"
cmd.Parameters.AddWithValue("#Expr1", rdr["Expr1"].ToString());
//run update query
cmd.CommandText = UpdateQuery;
cmd.ExecuteNonQuery();
return;
}
cmd.Connection.Close();
Try this
UPDATE StaffDetails a
SET a.HolidayTaken = (SELECT SUM(HolidayRequests.RequestTotalDays)
FROM HolidayRequests INNER JOIN StaffDetails b ON HolidayRequests.Username = b.UserName
WHERE (b.StaffID = #staffID)
GROUP BY b.StaffID, HolidayRequests.ApprovalStatus
HAVING (HolidayRequests.ApprovalStatus = N'approved'))
WHERE a.StaffID = #staffID

Resources