OK I keep getting this error after about 3-4 minutes of churning:
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Source Error:
Line 93:
Line 94: DataSet getData;
Line 95: getData = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString, CommandType.StoredProcedure, "Course_NewReportGet_Get_Sav", objPara);
Line 96:
Line 97: foreach (DataRow dr in getData.Tables[0].Rows)
Here is the code, I think I am not doing something properly, I set the timeout to 5000 seconds though so it must be something else. You'll notice it's quite the nested loop of procedure calls. I am getting every Company, then getting every Course assigned to each Company, then for each course I am getting a report of all the users activity. There is about 250 companies, anywhere from 2-70 courses per comapany, and from 8 to 1000 users per course report, per company... so we're dealing with a lot of data here. That final call to the get report is a pretty massive stored procedure also...
I am trying to transform the data into a new form that will make it faster and easier to work with later but for now I have to parse through what we have and post it in the new way. It is all in the same database but I am not exactly sure how I would do this all in just SQL. Basically am using a stored procedure that is used by our reporting tools to get the data to post to the new table. But I need to run the procedure for each course for each company and then post the data for each user returned in the report from each course from each company... It's huge...
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Data.SqlClient;
using Mexico.Data;
public partial class admin_tools_Optimus : System.Web.UI.Page
{
protected int step = 0;
protected string[] companies = new string[260];
protected string[] coursestrings = new string[260];
protected int total = 0;
protected int[] totalcourses = new int[260];
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Proceed(object sender, EventArgs e)
{
DataSet getCompanies = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString, CommandType.StoredProcedure, "Companies_All_Get");
int counter = 0;
foreach (DataRow dr in getCompanies.Tables[0].Rows)
{
lstData.Items.Add(dr["companyid"].ToString() + ": " + dr["companyname"].ToString());
companies[counter] = dr["companyid"].ToString();
counter++;
}
lblCurrentData.Text = counter.ToString() + " companies ready, click next to get all company courses.";
total = counter;
GetCompanies();
}
protected void GetCompanies()
{
string[,] courses = new string[260, 200];
for (int i = 0; i < total-1; i++)
{
DataSet getBundles = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString, CommandType.StoredProcedure, "CompanyCourses_ByCompanyID_Get_Sav", new SqlParameter("#companyid", companies[i]));
int counter = 0;
foreach (DataRow dr in getBundles.Tables[0].Rows)
{
courses[i, counter] = dr["bundleid"].ToString();
counter++;
}
string allID = "";
allID += courses[i, 0];
for (int ii = 0; ii < counter; ii++)
{
allID += "," + courses[i, ii];
}
Response.Write(allID + " <br/>");
coursestrings[i] = allID;
totalcourses[i] = counter;
}
GetUsers();
}
protected void GetUsers()
{
for (int i = 0; i < total - 1; i++)
{
SqlParameter[] objPara = new SqlParameter[10];
objPara[0] = new SqlParameter("#CompanyID", companies[i]);
objPara[1] = new SqlParameter("#CourseID", coursestrings[i]);
objPara[2] = new SqlParameter("#DateRangeType", 1);
//objPara[3] = new SqlParameter("#StartDate", startDate);
//objPara[4] = new SqlParameter("#EndDate", System.DateTime.Now.ToString("MM/dd/yyyy"));
objPara[5] = new SqlParameter("#UserName", "");
objPara[6] = new SqlParameter("#StartIndex", 1);
objPara[7] = new SqlParameter("#MaximumRows", 100000);
DataSet getData;
getData = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings["connstr"].ConnectionString, CommandType.StoredProcedure, "Course_NewReportGet_Get_Sav", objPara);
foreach (DataRow dr in getData.Tables[0].Rows)
{
Response.Write("user: " + dr["userid"].ToString() + " / course: " + dr["bundleid"].ToString() + " - progress: " + dr["viewed"].ToString() + " - scored: " + dr["scored"].ToString() + "<br/><br/>");
}
}
}
}
PAGE CODE:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Optimus.aspx.cs" Inherits="admin_tools_Optimus" Debug="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblCurrentData" runat="server" Text="Click next to get all Companies"/><br />
<asp:Button ID="btnNext" runat="server" Text="Next" OnClick="Proceed" />
<br/>
<asp:ListBox ID="lstData" runat="server" height="300" Width="300" />
</div>
</form>
</body>
</html>
There are five different timeouts you have to know about when talking to a database from an asp.net page:
The database connection timeout Set on your SQLConnection object, possibly via the connection string.
The database command timeout Set on your SQLCommand object.
The ASP.Net Script Timeout Set on your page via Server.ScriptTimeout.
Other IIS timeouts Imposed on your page by IIS. See this link: http://msdn.microsoft.com/en-us/library/ms525386.aspx?ppud=4
The internet browser timeout The browser/client will only wait so long. You don't control this so there's no point worrying about it, but you should still know it exists.
Make sure you check the first 4 I listed.
Normally I'd also say something to the effect that 3+ minutes is way to long to wait for a page to load. You might have enough data to justify the query time, but if so that's too much data for a user to really evaluate in one page. Consider breaking it up. But in this case it sounds like you're building a 'report' that needs to be run infrequently. While I still question the merits of such reports (it's just too much data to go through by hand — dump it into a fact table or somewhere similar for additional data mining instead), I understand that businesses often want them anyway.
Also, looking at your code I can see where you might be able to write that all as one big query instead of a bunch of little ones. That would be a lot more efficient for the database at the expense of some additionally complexity in assembling your results, but the result will run much faster. But for something that doesn't run often, when you already have the stored procedures built to do it this way, you may not be able to justify re-writing things.
So I'll give you a pass on the long-running page... this time.
It looks like the timeout is happening when you're running the sql stored procedure (Your exception is of type SqlException). You would need to increase the Timeout of your Sql Stored Procedure execution, but I don't think you can do that with the SqlHelper class.
You'll need to go with the SqlCommand class, and set the timeout there.
This is an example of an extremely bad re-use of code. Instead of reusing the stored proc which makes you loop through all input variables you want, write a new set-based proc that selects the information based on joins. It will be much faster (assuming you have properly indexed of course). In accessing databases, you do not want to do something through a loop or a cursor if a set-based alternative is possible.
Related
I have a multiple GridView generate dynamically placed inside an update panel where its data source is from the database.
What i want to do is change the color of the Gridview cells that contains all the 90 random number generated every three seconds. The part where i am having problem is that the page waits untill all the random numbers are generated and displays the final output instead of updating the Gridview cells color every time the random number is being generated.I know the problem is with the page lifecycle thing but, i dont know how to approach this problem. Can someone please guide me?
namespace validation
{
public partial class index : System.Web.UI.Page
{
int countTik;
protected void Page_Load(object sender, EventArgs e)
{
//Calling javascript
// ScriptManager.RegisterClientScriptBlock(this,GetType(), "mykey", "myFunction();", true);
// GridView gridview = new GridView();
//HiddenField1.Value = "2";
DataAccessForUserInterface ds = new DataAccessForUserInterface();
countTik = ds.selectAll();
for (int k = 1; k <= countTik; k++)
{
UpdatePanel updatePanel = new UpdatePanel();
updatePanel.ID = "panel"+k;
string Ticketname = "TicketNo" + k;
GridView gridview = new GridView();
RetriveTickets RetTik = new RetriveTickets();
List<RetriveTickets> Ticketdata = ds.GetMasterDetails(Ticketname).ToList();
RetTik.row1 = Ticketdata[0].row1;
RetTik.row2 = Ticketdata[0].row2;
RetTik.row3 = Ticketdata[0].row3;
DataTable dataTable1 = new DataTable();
dataTable1.Columns.Add("1");
dataTable1.Columns.Add("2");
dataTable1.Columns.Add("3");
dataTable1.Columns.Add("4");
dataTable1.Columns.Add("5");
dataTable1.Columns.Add("6");
dataTable1.Columns.Add("7");
dataTable1.Columns.Add("8");
dataTable1.Columns.Add("9");
//rows
// List<int> first = new List<int>();
foreach (var item in Ticketdata[0].row1)
{
var row = dataTable1.NewRow();
row["1"] = item.C0;
row["2"] = item.C1;
row["3"] = item.C2;
row["4"] = item.C3;
row["5"] = item.C4;
row["6"] = item.C5;
row["7"] = item.C6;
row["8"] = item.C7;
row["9"] = item.C8;
dataTable1.Rows.Add(row);
}
DataTable dataTable2 = new DataTable();
dataTable2.Columns.Add("1");
dataTable2.Columns.Add("2");
dataTable2.Columns.Add("3");
dataTable2.Columns.Add("4");
dataTable2.Columns.Add("5");
dataTable2.Columns.Add("6");
dataTable2.Columns.Add("7");
dataTable2.Columns.Add("8");
dataTable2.Columns.Add("9");
//rows
foreach (var item in Ticketdata[0].row2)
{
var row = dataTable2.NewRow();
row["1"] = item.C0;
row["2"] = item.C1;
row["3"] = item.C2;
row["4"] = item.C3;
row["5"] = item.C4;
row["6"] = item.C5;
row["7"] = item.C6;
row["8"] = item.C7;
row["9"] = item.C8;
dataTable2.Rows.Add(row);
}
DataTable dataTable3 = new DataTable();
dataTable3.Columns.Add("1");
dataTable3.Columns.Add("2");
dataTable3.Columns.Add("3");
dataTable3.Columns.Add("4");
dataTable3.Columns.Add("5");
dataTable3.Columns.Add("6");
dataTable3.Columns.Add("7");
dataTable3.Columns.Add("8");
dataTable3.Columns.Add("9");
//rows
foreach (var item in Ticketdata[0].row3)
{
var row = dataTable3.NewRow();
row["1"] = item.C0;
row["2"] = item.C1;
row["3"] = item.C2;
row["4"] = item.C3;
row["5"] = item.C4;
row["6"] = item.C5;
row["7"] = item.C6;
row["8"] = item.C7;
row["9"] = item.C8;
dataTable3.Rows.Add(row);
}
dataTable2.Merge(dataTable3);
dataTable1.Merge(dataTable2);
gridview.ID = "gridview" + k;
gridview.CssClass = "table table-bordered table-dark";
//gridview.AutoGenerateColumns = true;
//gridview.HeaderStyle.CssClass = "table-primary";
gridview.ShowHeader = false;
gridview.DataSource = dataTable1;
// gridview.DataBound += new EventHandler(groupHeader);
gridview.RowDataBound += new GridViewRowEventHandler(Gv_RowDataBound);
gridview.DataBind();
GridViewRow rows = new GridViewRow(0, 0, DataControlRowType.Header, DataControlRowState.Normal);
TableHeaderCell cell = new TableHeaderCell();
cell.Text = "Customer Name";
cell.ColumnSpan = 5;
rows.Controls.Add(cell);
cell = new TableHeaderCell();
cell.ColumnSpan = 4;
cell.Text = "Ticket Number";
rows.Controls.Add(cell);
// row.BackColor = Color.BlanchedAlmond;
gridview.HeaderRow.Parent.Controls.AddAt(0, rows);
updatePanel.ContentTemplateContainer.Controls.Add(gridview);
grid1.Controls.Add(updatePanel);
}
StartCounting();
}
//fill color of already generated random number stored in database and that exist on the gridview on page load
private void Gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
DataAccessForUserInterface da = new DataAccessForUserInterface();
int[] randomList = da.getRandom();
if (e.Row.RowType == DataControlRowType.DataRow)
{
for (int r = 0; r < 90; r++)
{
for (int i = 0; i <= 8; i++)
{
int num = Convert.ToInt32(e.Row.Cells[i].Text);
if (num != 0 && num == randomList[r])
{
//e.Row.BackColor = Color.Red;
e.Row.Cells[i].BackColor = Color.Green;
}
}
}
}
}
public int[] StartCounting()
{
int[] random = new int[90];
{
int[] randomNumber = new int[90];
int count = 0;
for (int i = 0; i < randomNumber.Length; i++)
{
Random rnd = new Random();
int num = rnd.Next(1, 91);
if (randomNumber.Contains(num))
{
i--;
}
else
{
randomNumber[i] = num;
int countingNumber = randomNumber[i];
count++;
DataAccessForUserInterface da = new DataAccessForUserInterface();
da.insertRandom(countingNumber); // Store Random number generated on Database
Task.Delay(3 * 1000).Wait();
for(int GridCount=1;GridCount<=countTik;GridCount++)
{
GridView gv = (GridView)FindControl("gridview"+GridCount);
foreach (GridViewRow row in gv.Rows)
{
for (int cellIndex = 0; cellIndex < 9; cellIndex++)
{
if (row.Cells[cellIndex].Text.Equals(countingNumber))
{
row.BackColor = Color.Green;
}
}
}
}
}
}
return randomNumber;
}
}
}
}
Well, when you post-back, even in a update panel, the web page travels up to the server. The code behind runs - all of it. WHILE that code runs, the web page is up on the server. ANY change of a control is NOT seen by the user (they have a copy of the web page just sitting on their desktop).
The user ONLY will see ANY updates from your code behind ONLY AFTER the code is 100% done, finished and exits. When the code exists, then the WHOLE page (or update panel ) now travels back down to the client side, and is updated.
So, clearly, any code behind updates - ALL OF them will appear to occur in "one shot" or "one update".
A basic grasp of how web pages work is required here.
You have this:
NOTE SUPER careful in the above - note how NO WEB page is active on the web server side.
You do NOT have this setup:
And you do NOT have this setup:
so, you JUST have a web page sitting on the client side.
If the user clicks a button, or even a button in a update panel, then that post-back starts, you have this:
The page is sent to the web server (it is just sitting there - waiting for ANY user to post back a page - NOT JUST YOUR page!!!!
So, page travels up to web server.
You now have this:
Now, to be fair, in above a copy of the web page is ALSO still sitting on the users desktop - but they see the little spinner - page is waiting for ALL OF AND 100% of the code behind to finish.
You in fact NEVER directly interact with the user with code behind.
Your server side code ONLY interacts with the web page - and for the SHORT time the page is up on the server.
While the code behind runs, you are updating the copy of the browswer on the server side - any chances cannot be seen by the user. In fact, in most cases the order in which you update things don't matter, since ALL of the changes will have to complete.
Once and ONLY once your code behind is done running, then the whole page (or the udpate panel) now travels back to the client side browser, page loads, JavaScript starts to run, and NOW you see the updates from your code behind.
So, they will ALWAYS appear to update in one shot - not control by control.
And EVEN if you put delays in your code - all you will accomplish is to delay the page sitting up on the server for a LONGER period of time - but no updates will be seen client side.
So, after the updates occur (all code behind is done running), then, and only then does the page travel back to the client side, like this:
At that point, the server TOSSES out your web page, destryos the page class, and even all your code varaibles now go out of scope. Very much like when you exit a subroutine call - the local vars and values of that sub (or now the web page) go out of scope.
The web server is now just sitting waiting for ANY user to post back a page (or partial post back via a udpate panel - but the process is the same either way).
So, as a result, you can't put in code delays.
Your have to let the code behind finish.
So, if you want the user to "see" the page update (the squares in your case), then you have to use one of serveral appraoches:
One way:
You put in a JavaScript timer, and have it click a update button (even a hidden one) in the update paneal. Each time you click (say every half second), then you update one square in code behind, and then the update panel will show those results).
Another way is to have a JavaScript timer, it calls a JavaScript routin every second (or maybe half secord, or whatever).
That js routine will thus call a server side web method, get the value back of ONE control or text box, and the update it. This is typically referred to a ajax call.
This can be a complex bit of code. Worse yet, your squares to update are nested in a gridview, making the js code even more diffiuclt.
The most easy way?
Setup a code behind routine, use a session() counter (or view state).
Drop in a timer control into the update panel.
Have the timer control call your update to the grid routine, have it call it say once ever second. When the timer routine triggers, you update 1 box, increment the counter (to update the next box), and code behind exits.
So, you have to setup a routine that can modify ONLY ONE of the text boxes, and exit. You also need some counter that increments.
So for each timer trigger, your timer code calls the udpate routine - updates one text box, increment counter, and is done.
When the timer code figures out you JUST updated the last box, then the timer event can turn off the timer trigger, and the refreshing of the page will stop.
A REALLY simple example would be to have a text box count from 1 to 10, and update each time.
If we just run a for/next loop, put 1 to 10 in the text box, when the user clicks the button, the code can run, shove 1, then 2 etc into the text box. but due to the post-back and round trip diagrams above, the user will ONLY ever see "10" as the final result.
So, lets do the 1 to 10, use a update panel, and a simple text box.
So, we have this markup:
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<br />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TextBox ID="txtCount" runat="server"
Height="42px"
Width="42px"
Font-Size="XX-Large"
Text="0"
style="text-align: center">
</asp:TextBox>
<br />
<asp:Button ID="Button1" runat="server" Text="Start Timer" CssClass="btn" OnClick="Button1_Click"/>
<asp:Timer ID="Timer1" runat="server"
Enabled="False"
Interval="1000" OnTick="Timer1_Tick"></asp:Timer>
</ContentTemplate>
</asp:UpdatePanel>
And our code behind can be this:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Session["MyCount"] = 0;
Timer1.Interval = 1000; // tick our timer each second
Timer1.Enabled = true;
}
protected void Timer1_Tick(object sender, EventArgs e)
{
int? MyCount = Session["MyCount"] as int?;
if (MyCount < 10 )
{
MyCount = MyCount + 1;
txtCount.Text = MyCount.ToString();
Session["MyCount"] = MyCount;
}
else
{
// we are done, stop the timer
Timer1.Enabled = false;
}
}
And we now see/have this:
So, I suggest you try this on a test page. Drop in that text box, the timer, and the update panel.
Note that if you JUST runt he loop one to 10 in code behind (without the timer), then the code does run, does udpate the text box (1 to 10), but you NEVER see the results, since you just have to refer to the above post-back diagrams to grasp how this works.
So, you need to "run" that code you have to create the new values, but you need a timer, a counter of some time, and each time you increment the timer, you update ONE box in the grid, and then each time the timer fires, you then have to update another box.
When the counter has updated all of teh new changes, then your code behind can set the timer enabled = false to stop this updating process.
We are running a reporting web application that allows the user to select a few fields and a crystal report is generated based off of the fields selected. The SQL that is generated for the most complex report will return the data in < 5 seconds, however it takes the report and average of 3 minutes to run, sometimes longer causing a time out. We are running VS2010. The reports are basically set up out of the box with no real manipulations or computations being done, just displaying the data in a nice format. Is there anything we can try to speed it up, pre-loading a dummy report to load the dlls, some hack to make crystal run faster, anything?
EDIT: Code Added to show the databinding
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string strFile = Server.MapPath(#"AwardStatus.rpt");
CrystalReportSource1.Report.FileName = strFile;
DataTable main = Main();
CrystalReportSource1.ReportDocument.SetDataSource(main);
CrystalReportViewer1.HasCrystalLogo = false;
CrystalReportSource1.ReportDocument.ExportToHttpResponse(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat, Response, false, "pmperformance");
}
}
private DataTable Main()
{
Guid guidOffice = Office;
CMS.Model.ReportsTableAdapters.ViewACTableAdapter rptAdapter = new CMS.Model.ReportsTableAdapters.ViewACTableAdapter();
Reports.ViewAwardedContractsDataTable main = new Reports.ViewAwardedContractsDataTable();
if (Office == new Guid())
{
IEnumerable<DataRow> data = rptAdapter.GetData().Where(d => UserPermissions.HasAccessToOrg(d.guidFromId, AuthenticatedUser.PersonID)).Select(d => d);
foreach (var row in data)
{
main.ImportRow(row);
}
}
else if (guidOffice != new Guid())
{
main = rptAdapter.GetDataByOffice(guidOffice);
}
else
{
main = new Reports.ViewACDataTable();
}
return main;
}
private Guid Office
{
get
{
string strOffice = Request.QueryString["Office"];
Guid guidOffice = BaseControl.ParseGuid(strOffice);
if (!UserPermissions.HasAccessToOrg(guidOffice, AuthenticatedUser.PersonID))
{
return Guid.Empty;
}
else
{
return guidOffice;
}
}
}
protected void CrystalReportSource1_DataBinding(object sender, EventArgs e)
{
//TODO
}
This may be a bit flippant, but possibly consider not using crystal reports... We had a fair bit of trouble with them recently (out of memory errors being one), and we've moved off to other options and are quite happy...
Here's what I would do:
Put clocks from the time you get the field choices from the user, all the way to when you display the report. See where your processing time is going up.
When you look at the clocks, there can be various situations:
If Crystal Reports is taking time to fill the report, check how you're filling it. If you're linking the report fields directly to your data table, CR is probably taking time looking up the data. I suggest creating a new table (t_rpt) with dynamic columns (Field1, Field2,..FieldN) and pointing your report template to that table. I don't know if you're already doing this.
If it's taking time for you to lookup the data itself, I suggest creating a view of your table. Even though a memory hog, this will make your lookup quick and you can delete the view once you're done.
If it's none of the above, let us know what your clocks show.
In terms of loading any large amount of data, you'll always want to use a stored procedure.
Outside of that, you WILL see a delay in the report running the first time the Crystal DLLs load. Yes, you can preload them as you mentioned and that will help some.
I have an auction site, not like ebay (as the most think).
Now my problem is follow: On the startpage are a lot of auctions displayed (ASP.NET). All Auction (let's say min. 10, max 50) have a timer. Now I must have a look nearly every second for each auction (filter is not started and not ended) if the timer is reseted (what happens when someone bid).
So I will have a query with a lot of amount back every second, must update 10-50 Textboxes every second and that for every user that visit the page.
Any ideas who solve it performance-good?
You don't have to query SQL every second to change the second counters in your textboxes. You should use Javascript to automatically adjust the seconds left from the start time.
The start time should only get changed when the start time in the SQL tables has been reset. You can use AJAX to update the Textboxes.
You do not have to query SQL once a second to tell when your timers have been reset. You should use a SQL cache dependency. Basically, a SQL Dependency tells you when when it's data has changed, instead of you having to ask SQL every second if anything has changed.
The workflow is as follows:
Make initial SQL query, with a SQL Dependency object, specifying a "callback" method
Cache the result
Return the cached result to your pages until the SQL data has changed
When the SQL data has changed, the SQL Dependency calls the "callback" method you defined
Callback method clears your SQL cache, and re-queries SQL
Repeat steps 2 - 6.
It's a little complicated if you're not used to working with Delegates and dependencies, but it's well worth the time spent because it eliminates repetitive querying.
Sql Cache Dependency Example C# ASP.Net 2.0
In your Global.aspx page, add this code:
<script runat="server">
string mySqlConnection = "<<Enter your connection string here.>>";
void Application_Start(object sender, EventArgs e)
{
// Start subscribing to SQL Server 2005 Notification Services.
System.Data.SqlClient.SqlDependency.Start(mySqlConnection);
}
void Application_End(object sender, EventArgs e)
{
// Stop subscribing to SQL Server 2005 Notification Services.
System.Data.SqlClient.SqlDependency.Stop(mySqlConnection);
}
</script>
In your Default.aspx page, add this code:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblCacheDisplay" runat="server" />
<br />
<asp:Label ID="lblWasQueryExecuted" runat="server" />
</div>
</form>
</body>
</html>
The Default.aspx page will display your data, and will say whether the data came from the Application Cache, or from SQL.
In your Default.aspx.cs page, add this code:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page
{
private static SqlDependency sqlDependency;
private static bool cacheIsValid = false; // cacheIsValid is set to "false" when the SQL data is changed.
private static string mySqlConnection = "<<Enter you connection string here. It MUST use a different user than the Global.aspx connection string.>>";
protected void Page_Load(object sender, EventArgs e)
{
string myCachedData = (string)HttpContext.Current.Cache.Get("myCachedData");
if (myCachedData == null || !cacheIsValid) // Remember that cached objects can be removed from the cache at any time by the garbage collector, you cannot assume that they exist!
{
myCachedData = GetMyDataFromSql();
cacheIsValid = true;
lblWasQueryExecuted.Text = "SQL was queried for this data.";
}
else
{
lblWasQueryExecuted.Text = "This data came from the Application-level cache. It should be deleted if the SQL data changes.";
}
lblCacheDisplay.Text = myCachedData;
}
public static string GetMyDataFromSql()
{
string returnSqlData = String.Empty;
string storedProcedureName = "<<Enter you stored procedure name here.>>";
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedProcedureName;
cmd.Connection = new SqlConnection(mySqlConnection);
cmd.Connection.Open();
SqlDataReader sdr = cmd.ExecuteReader();
if (sdr.Read())
{
returnSqlData = sdr.GetString(1);
}
// Only one Sql dependency needs to be created per application start. Since each Sql dependency keeps a connection
// to the Sql database open at all times, we want to make sure we have only one Sql dependency at any given time.
if (!cacheIsValid)
{
HttpContext.Current.Cache.Remove("myCachedData");
}
object hasSqlDependency = HttpContext.Current.Cache.Get("myCachedData");
if (hasSqlDependency == null)
{
CreateSqlDependency();
HttpContext.Current.Cache.Add("myCachedData", returnSqlData, null, DateTime.MaxValue,
TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Normal, null);
}
cmd.Connection.Close();
return returnSqlData;
}
public static void SqlDependency_OnChange(Object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
// The Sql data has changed, so the current cache is out-dated. Therefore, mark it as being invalid.
cacheIsValid = false; // We do not have access to HttpContext.Current right now, so we cannot clear the cache ourselves right now. Instead, we have to mark it as being invalid, and let other code update it.
// Recreate the Sql dependency, since it disappears after sqlDependency_OnChange is called. This will keep the
// connection to the Sql database open, so we can continue to be notified if the SQL data changes.
CreateSqlDependency();
}
}
private static void CreateSqlDependency()
{
SqlConnection sqlConn = new SqlConnection(mySqlConnection);
sqlConn.Open();
// If any tables in this query are modified (data changes, table definition changes, etc.), SqlDependency_OnChange will be called.
SqlCommand cmdDependency = new SqlCommand("<<SELECT column FROM myTable>>", sqlConn);
sqlDependency = new SqlDependency(cmdDependency);
sqlDependency.OnChange += new OnChangeEventHandler(SqlDependency_OnChange);
// Even though we don't do anything with the results of this query, it still needs to be executed in order to set the Sql dependency.
// If you comment out this code, the Sql dependency will not be properly created, and SqlDependency_OnChange will never be notified of
// changes to the SQL data.
SqlDataReader objReader = cmdDependency.ExecuteReader();
objReader.Close();
sqlConn.Close();
}
}
Make the appropriate changes marked by the "<<" and ">>", and you should be good to go.
Note that the two SQL user accounts must have very specific SQL 2005 permissions. Here's how you can setup the SQL user permissions.
All of the timers on the page should automatically decrement one tick every second (if it is below the "display seconds" threshold - otherwise update minute once a minute). This is handled through client-side javascript using a timer to trigger the updates.
Whenever an auction's time is reset (due to a bid) this is updated in the db, and also in a server-side cache. The cache is stores the time reset, the auctionID and the new end-time for the auction
Once every second or so, the page sends an Ajax request (JSON, preferably) back to the server, asking for all of the auctionIDs and new end-times for all auctions whose time has been reset since the last time this page requested it (a value that is stored on the client-side at every request). Based on the return value, only the updated auctions are updated on the client side. And the DB is only queried on the initial page load - all subsequent update requests hit the cache.
All you need to get it which textbox has been reset, the rest can use a gui timer.
so you need to make a reset log table and put a row each time someone reset (put a bid).
now - each sec you select * from log where lognumber > LAST_NUMBER_THAT_YOUR SCREEN_ALREADY_GOT
you get the list of only changed items.
I think it might do the work most efficient...
just make sure to delete old records from this log table....
I want to keep track of the number of visitors to my site.
I tried the following code in the Global.asax class,
<script runat="server">
public static int count = 0;
void Application_Start(object sender, EventArgs e)
{
Application["myCount"] = count;
}
void Session_Start(object sender, EventArgs e)
{
count = Convert.ToInt32(Application["myCount"]);
Application["myCount"] = count + 1;
}
</script>
I am retrieving the value in the aspx page as follows:
protected void Page_Load(object sender, EventArgs e)
{
int a;
a = Convert.ToInt32((Application["myCount"]));
Label4.Text = Convert.ToString(a);
if (a < 10)
Label4.Text = "000" + Label4.Text ;
else if(a<100)
Label4.Text = "00" + Label4.Text;
else if(a<1000)
Label4.Text = "0" + Label4.Text;
}
The above coding works fine. It generates the Visitors properly but the problem is when I restart my system, the count variable again starts from 0 which logically wrong.
I want the value of count to be incremented by 1 from the last count value.
So can anyone tell me how to accomplish this task?
Please help me out!
Thanks in advance!
If you want the count to keep incrementing over application restarts, you'll need to store the value somewhere - in a database or a file somewhere, and load that value up when the application starts.
Also, you can use the following to ensure your displayed count is always at least 4 characters:
int a;
a = Convert.ToInt32(Application["myCount"]);
Label4.Text = a.ToString("0000");
See Custom Numeric Format Strings for more info.
Edit to respond to comment
Personally, I'd recommend using a database over writing to the file system, for at least the following reasons:
Depending on your host, setting up a database may well be a lot easier than enabling write access to your file system.
Using a database will allow you to store it as an int rather than a string.
Under heavy traffic, you'll have issues with multiple threads trying to open a text file for write access - which will cause a lock on the file, and cause a bottle neck you don't need.
Various resources will tell you how to connect to a database from your code, a good place to start would be this How To: Connect to SQL Server, and looking into the methods under "What are the alternatives" for details on how to query and update the database.
C# code is show below:
protected void Page_Load(object sender, EventArgs e)
{
this.countMe();
enter code here
DataSet tmpDs = new DataSet();
tmpDs.ReadXml(Server.MapPath("~/counter.xml"));
lblCounter.Text = tmpDs.Tables[0].Rows[0]["hits"].ToString();
}
private void countMe()
{
DataSet tmpDs = new DataSet();
tmpDs.ReadXml(Server.MapPath("~/counter.xml"));
int hits = Int32.Parse(tmpDs.Tables[0].Rows[0]["hits"].ToString());
hits += 1;
tmpDs.Tables[0].Rows[0]["hits"] = hits.ToString();
tmpDs.WriteXml(Server.MapPath("~/counter.xml"));
}
Then you need to have an xml file in the root directory to make the code work as well. The XML file will look like this:
<?xml version="1.0" encoding="utf-8" ?>
<counter>
<count>
<hits>0</hits>
</count>
</counter>
In First Answer U had declare count variable globally,that's why in every new session count starts with 0.for better result ,increment application[] variable inside session_start method.
Usually you use other Tools for that Task (weblog analyser).
As you store your value in Memory (Application["myCount"]) this value will not survive a server restart. So you have to store it in a
database
plain textfile
whatever
I was just wondering about the correct way to return a reader from a class?
My code below works, but I'm unsure if this is correct.
Also. I can't close the the connection in my class method and still access it from my ascx page, is
that OK?
// In my class I have the following method to return the record/reader -- it's a single record in this case.
public SqlDataReader GetPost()
{
SqlConnection conn = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("con_spPost", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#blogid", blogid);
try
{
conn.Open();
return cmd.ExecuteReader();
}
finally
{
// conn.Close();
}
}
//I then call the GetPost method in my ascx page like so:
protected void Page_Load(object sender, EventArgs e)
{
//instantiate our class
MyClass DB = new MyClass();
//pass in the id of the post we want to view
DB.PostID = Int32.Parse(Request.QueryString["p"]);
///call our GetPost method
SqlDataReader reader = DB.GetPost();
//output the result
reader.Read();
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
this.viewpost.InnerHtml += reader["BlogText"].ToString();
reader.Close();
}
I'd appreciate any comments on my code or tips, thanks.
Melt
Generally speaking it's fine to return a reader from a method, but the reader's consumer needs to take control of all the disposable objects that will be used during the reader's lifetime.
To do this, you'd pass an IDbConnection into the GetPost method, then make sure your caller disposes both the connection and reader. The using keyword is the most convenient way to do this:
protected void Page_Load(object sender, EventArgs e) {
// Create the DB, get the id, etc.
using (IDbConnection connection = new SqlConnection(connectionString))
using (IDataReader reader = DB.GetPost(connection)) {
reader.Read();
this.viewpost.InnerHtml = reader["BlogText"].ToString();
// finishing doing stuff with the reader
}
}
As others have pointed out, this starts to clutter your application's presentation layer with way too much data-access infrastructure - so it isn't appropriate here. Until you find yourself with a performance problem or need to display an unreasonable amount of data, you shouldn't be handling data readers in your presentation layer. Just make DB.GetPost return a string, and encapsulate all the data-access code in there.
To make sure that the connection is closed, replace the ExecuteReader call with the following:
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
You should also remove te try / finally block.
Also, in your Page_Load handler, you should use a using statement, like this:
using (SqlDataReader reader = DB.GetPost()) {
//output the result
reader.Read();
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"
+ "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>"
+ reader["BlogText"].ToString();
}
Also, you should check that the SQL query actually returned something, like this:
if (!reader.Read()) {
Something's wrong
}
Finally, and most important by far, you should escape your HTML to prevent XSS holes by calling Server.HtmlEncode.
For example:
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"
+ "<span class='blogheads'>" + Server.HtmlEncode(reader["BlogTitle"].ToString()) + "</span><p><p>"
+ Server.HtmlEncode(reader["BlogText"].ToString());
You really shouldn't be mixing data access with the presentation layer.
Consider returning a typed DataSet, or building business objects and returning those to your control.
Here is a tutorial:
http://www.asp.net/learn/data-access/tutorial-01-cs.aspx
There is a problem. Your connection is not being closed. As you know you can't close it within your GetPost because then you won't have the data any more, due to the nature of the DataReader. One way to address this is to include a parameter in your ExecuteReader method like this:
cmd.ExecuteReader(CommandBehavior.CloseConnection)
Then, when your reader is closed, the connection will close.
There is a fundamental problem with having a datareader returned by encapsulated code, in that the connection has to be open through all this, making error handling tricky. Consider (A) using a datatable instead, which is almost as efficient for small sets of data. This way you can close your connection right away in you GetPost method and not worry about it, with very simple error handling. Or (B) Pass the connection into GetPost, so all the Using/Dispose syntax and error handling for the connection is explicit in one place. I would suggest option A.
This is a very simple architecture. As CSharpAtl has suggested, you could make it more sophisticated. However, this seems to work for you.
One important addition I would make would be to use try-finally blocks. Putting the Close in the finally will ensure that the connection is released even if an exception occurs during processing.
SqlDataReader reader;
try
{
///call our GetPost method
reader = DB.GetPost();
//output the result
reader.Read();
this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
this.viewpost.InnerHtml += reader["BlogText"].ToString();
}
finally
{
reader.Close();
}
Why are you having the web page know about the database at all? Why not abstract away the database knowledge and just return a list or an object with the data from the database? Just seems like a lot of responsibility mixing, and you could make it easier on yourself.
This article by Dan Whalin might be a good resource for you to read through. It shows the basics of creating an n-layered application. You create a data access component, an entity object, a business layer and a presentation layer. He also uses sql data readers like you're asking about, and he shows a nice way of having a object build helper method.
If you don't like reading article, he also has a pretty good video on the same subject and a code example which you can download and see the different variations of this method of creating data driven applications.
Good luck and hope this helps some.