I'm building a web app that to build a report, but there are too many arguments to give each one a name, and I want to save them indexed by numbers so I can handle them with loops later on throughout the application.
However, I'm getting an index out of range on the very first session item(0)...as I understand it, I don't have to instantiate a session myself and this should work right?
Session[0] = txtComplianceCaseID.Text;
Session[1] = ddlState.SelectedValue;
Session[2] = txtActingSupervisor.Text;
Session[3] = ddlRiskTolerance.SelectedValue;
etc...
The Session object is a string dictionary; you should store objects in it with string keys.
Writing Session[0] will get or set the first item in session state.
Since Session state starts empty, it throws an exception.
Instead, you should use strings, like this:
Session["Compliance ID"] = txtComplianceCaseID.Text;
Session["State"] = ddlState.SelectedValue;
Session["Supervisor"] = txtActingSupervisor.Text;
Session["Risk Tolerance"] = ddlRiskTolerance.SelectedValue;
You can also call the Add method.
Read more about Asp.net Session Object and how to fill it with information here
Related
We appear to have a problem with MDriven generating the same ECO_ID for multiple objects. For the most part it seems to happen in conjunction with unexpected process shutdowns and/or server shutdowns, but it does also happen during normal activity.
Our system consists of one ASP.NET application and one WinForms application. The ASP.NET app is setup in IIS to use a single worker process. We have a mixture of WebForms and MVC, including ApiControllers. We're using a rather old version of the ECO packages: 7.0.0.10021. We're on VS 2017, target framework is 4.7.1.
We have it configured to use 64 bit integers for object id:s. Database is Firebird. SQL configuration is set to use ReadCommitted transaction isolation.
As far as I can tell we have configured EcoSpaceStrategyHandler with EcoSpaceStrategyHandler.SessionStateMode.Never, which should mean that EcoSpaces are not reused at all, right? (Why would I even use EcoSpaceStrategyHandler in this case, instead of just creating EcoSpace normally with the new keyword?)
We have created MasterController : Controller and MasterApiController : ApiController classes that we use for all our controllers. These have a EcoSpace property that simply does this:
if (ecoSpace == null)
{
if (ecoSpaceStrategyHandler == null)
ecoSpaceStrategyHandler = new EcoSpaceStrategyHandler(
EcoSpaceStrategyHandler.SessionStateMode.Never,
typeof(DiamondsEcoSpace),
null,
false
);
ecoSpace = (DiamondsEcoSpace)ecoSpaceStrategyHandler.GetEcoSpace();
}
return ecoSpace;
I.e. if no strategy handler has been created, create one specifying no pooling and no session state persisting of eco spaces. Then, if no ecospace has been fetched, fetch one from the strategy handler. Return the ecospace. Is this an acceptable approach? Why would it be better than simply doing this:
if (ecoSpace = null)
ecoSpace = new DiamondsEcoSpace();
return ecoSpace;
In aspx we have a master page that has an EcoSpaceManager. It has been configured to use a pool but SessionStateMode is Never. It has EnableViewState set to true. Is this acceptable? Does it mean that EcoSpaces will be pooled but inactivated between round trips?
It is possible that we receive multiple incoming API calls in tight succession, so that one API call hasn't been completed before the next one comes in. I assume that this means that multiple instances of MasterApiController can execute simultaneously but in separate threads. There may of course also be MasterController instances executing MVC requests and also the WinForms app may be running some batch job or other.
But as far as I understand id reservation is made at the beginning of any UpdateDatabase call, in this way:
update "ECO_ID" set "BOLD_ID" = "BOLD_ID" + :N;
select "BOLD_ID" from "ECO_ID";
If the returned value is K, this will reserve N new id:s ranging from K - N to K - 1. Using ReadCommitted transactions everywhere should ensure that the update locks the id data row, forcing any concurrent save operations to wait, then fetches the update result without interference from other transactions, then commits. At that point any other pending save operation can proceed with its own id reservation. I fail to see how this could result in the same ID being used for multiple objects.
I should note that it does seem like it sometimes produces id duplicates within one single UpdateDatabase, i.e. when saving a set of new related objects, some of them end up with the same id. I haven't really confirmed this though.
Any ideas what might be going on here? What should I look for?
The issue is most likely that you use ReadCommitted isolation.
This allows for 2 systems to simultaneously start a transaction, read the current value, increase the batch, and then save after each other.
You must use Serializable isolation for key generation; ie only read things not currently in a write operation.
MDriven use 2 settings for isolation level UpdateIsolationLevel and FetchIsolationLevel.
Set your UpdateIsolationLevel to Serializable
In my page1.aspx i am generating a report from database by using thread.
//on button click
Hashtable ht = (Hashtable)Session["ReportParam"];
ReportThreadClass rth = new ReportThreadClass(ht);
Thread thread = new System.Threading.ThreadStart(rth .Run);
thread.Start();
In my thread class's rum method i am updating values in Hashtable that how many pages i have created.
//in thread' method
public virtual void Run()
{
int pagecount=0;
while(done)
{
//loading data from DB and generating html pages
ht["Total_Pages"] = pagecount;
}
}
At my Page2.aspx i am reading values from Session Variable
Hashtable ht = (Hashtable)Session["ReportParam"];
int TotalPages = (int) ht["Total_Pages"];
When i run above code in InProc mode every thing is working fine i am getting updated values from session.
Because every thing is stored in static variable, and ht is referenced by Session so it automatically get updated in session (HashTable not needed to reassign it to session back).
But when i run code in State server (OutProc mode) It need to store session data in different process by Serializing Hash-table.
But the value of Total_Pages is not getting updated in Page2.aspx even after Thread run completely.
So is there any event or method which get fired to store all updates in session variable to State-Server , if yes then pls tell me . if not then pls suggest me some idea to get updated value in page2.aspx.
I would explictely SET and GET SessionState like so:
In your thread Run
// no complex object like hastable, just a plain value...
Session["pageCount"] = pageCount;
In your page2.apsx:
var pageCount = (int) Session["pageCount"]??0;
The reason your report thread is not updating it's session value when using out-of-proc sessionstate is because the session has no way to detect the hashtable has a changed value, therefor it doesn't update the underlying storew with the serialized version of the hastable. When you explicity store one immutable object it will persist one it's value changed;
As the session might already be gone when your thread finishes a begtter option is to get hold of a reference to SqlSessionStateStore and call SetAndReleaseItemExclusive. Ultimately you might want to have an overloaded SessionStateProvider that can handle your scenario.
In Out Proc Mode Session is saved after some event so if your thread is updating your session variables then it won't persist in storage.
If u are using Inproc Mode then session store in Static Dictionary so if your thread updating it, u will get updated value to any page.
So u have two solutions for this situation
Use inProc mode
Maintain a dictionary in your thread class with key as Session id and value is your hash-table, So if page2.aspx wants to read value of hash-table then it will pass his session id to method and which will return required value.
Less efficient but I'd probably just ping the database for the page count on Page2.
Or create a separate session value for the page count on Page1, at the same time as doing everything else. (EDIT: Nevermind the second part, that's what Rene suggested below).
I have the following code that sets certain object(CarsCourse) values according to what the user selected in a web form.
The code works, but an associate of mine stated that this in the worst possible way of doing this. However, he couldn't offer any suggestions.
So is there a better way of accomplishing this?
Thanks
If Not String.IsNullOrEmpty(tbDisplayName.Text) Then CarsCourse.DisplayName = tbDisplayName.Text
If Not String.IsNullOrEmpty(tbDescription.Text) Then CarsCourse.Description = tbDescription.Text
If Not String.IsNullOrEmpty(tbOfficialStartDate.Text) Then CarsCourse.OfficialStartDate = DateTime.Parse(tbOfficialStartDate.Text)
If Not String.IsNullOrEmpty(tbOfficialEndDate.Text) Then CarsCourse.OfficialEndDate = DateTime.Parse(tbOfficialEndDate.Text)
If Not String.IsNullOrEmpty(tbBtmDatepicker1.Text) Then CarsCourse.VisibleStartDate = DateTime.Parse(tbBtmDatepicker1.Text)
If Not String.IsNullOrEmpty(tbBtmDatepicker2.Text) Then CarsCourse.VisibleEndDate = DateTime.Parse(tbBtmDatepicker2.Text)
If Not String.IsNullOrEmpty(ddlDepartment.SelectedValue) Then CarsCourse.SecondarySpecialtyName = ddlDepartment.SelectedValue
If Not String.IsNullOrEmpty(ddlOptionType.SelectedValue) Then CarsCourse.OptionType = ddlOptionType.SelectedValue
If Not String.IsNullOrEmpty(ddlOfficialName.SelectedValue) Then CarsCourse.OfficialCourseID = Guid.Parse((ddlOfficialName.SelectedValue))
I think your code is not very well designed. The way of achieving this properly would be with state and validation.
So primary you have a form, which holds the current values of an object. I'd recommend to bind the corresponding object directly to the web form. Regardless of being null or empty the values are actually set.
As soon as the user presses the OK button, a Save() or Validate() method is executed on the corresponding object and the values are checked for their validity. Cancel the save process if validation fails and tell the user to update his input accordingly.
We have a data driven ASP.NET website which has been written using the standard pattern for data caching (adapted here from MSDN):
public DataTable GetData()
{
string key = "DataTable";
object item = Cache[key] as DataTable;
if((item == null)
{
item = GetDataFromSQL();
Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300), TimeSpan.Zero;
}
return (DataTable)item;
}
The trouble with this is that the call to GetDataFromSQL() is expensive and the use of the site is fairly high. So every five minutes, when the cache drops, the site becomes very 'sticky' while a lot of requests are waiting for the new data to be retrieved.
What we really want to happen is for the old data to remain current while new data is periodically reloaded in the background. (The fact that someone might therefore see data that is six minutes old isn't a big issue - the data isn't that time sensitive). This is something that I can write myself, but it would be useful to know if any alternative caching engines (I know names like Velocity, memcache) support this kind of scenario. Or am I missing some obvious trick with the standard ASP.NET data cache?
You should be able to use the CacheItemUpdateCallback delegate which is the 6th parameter which is the 4th overload for Insert using ASP.NET Cache:
Cache.Insert(key, value, dependancy, absoluteExpiration,
slidingExpiration, onUpdateCallback);
The following should work:
Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300),
Cache.NoSlidingExpiration, itemUpdateCallback);
private void itemUpdateCallback(string key, CacheItemUpdateReason reason,
out object value, out CacheDependency dependency, out DateTime expiriation,
out TimeSpan slidingExpiration)
{
// do your SQL call here and store it in 'value'
expiriation = DateTime.Now.AddSeconds(300);
value = FunctionToGetYourData();
}
From MSDN:
When an object expires in the cache,
ASP.NET calls the
CacheItemUpdateCallback method with
the key for the cache item and the
reason you might want to update the
item. The remaining parameters of this
method are out parameters. You supply
the new cached item and optional
expiration and dependency values to
use when refreshing the cached item.
The update callback is not called if
the cached item is explicitly removed
by using a call to Remove().
If you want the cached item to be
removed from the cache, you must
return null in the expensiveObject
parameter. Otherwise, you return a
reference to the new cached data by
using the expensiveObject parameter.
If you do not specify expiration or
dependency values, the item will be
removed from the cache only when
memory is needed.
If the callback method throws an
exception, ASP.NET suppresses the
exception and removes the cached
value.
I haven't tested this so you might have to tinker with it a bit but it should give you the basic idea of what your trying to accomplish.
I can see that there's a potential solution to this using AppFabric (the cache formerly known as Velocity) in that it allows you to lock a cached item so it can be updated. While an item is locked, ordinary (non-locking) Get requests still work as normal and return the cache's current copy of the item.
Doing it this way would also allow you to separate out your GetDataFromSQL method to a different process, say a Windows Service, that runs every five minutes, which should alleviate your 'sticky' site.
Or...
Rather than just caching the data for five minutes at a time regardless, why not use a SqlCacheDependency object when you put the data into the cache, so that it'll only be refreshed when the data actually changes. That way you can cache the data for longer periods, so you get better performance, and you'll always be showing the up-to-date data.
(BTW, top tip for making your intention clearer when you're putting objects into the cache - the Cache has a NoSlidingExpiration (and a NoAbsoluteExpiration) constant available that's more readable than your Timespan.Zero)
First, put the date you actually need in a lean class (also known as POCO) instead of that DataTable hog.
Second, use cache and hash - so that when your time dependency expires you can spawn an async delegate to fetch new data but your old data is still safe in a separate hash table (not Dictionary - it's not safe for multi-reader single writer threading).
Depending on the kind of data and the time/budget to restructure SQL side you could potentially fetch only things that have LastWrite younger that your update window. you will need 2-step update (have to copy dats from the hash-kept opject into new object - stuff in hash is strictly read-only for any use or the hell will break loose).
Oh and SqlCacheDependency is notorious for being unreliable and can make your system break into mad updates.
This is an odd thing I've just run into.
I have a web application with a small DataTable stored in the ApplicationCache to reduce the amount of queries to a separate since the data is a lookup table that doesn't change often.
I access this DataTable twice within a given page. Once to bind the data to a drop down list in my Page_Load method:
dtDeptDivAct = GetAllDeptDivActCodes()
dtDeptDivAct.DefaultView.Sort = "LongDescription ASC"
ddlDeptDivAccount.DataSource = dtDeptDivAct.DefaultView
ddlDeptDivAccount.DataTextField = "LongDescription"
ddlDeptDivAccount.DataValueField = "Id"
ddlDeptDivAccount.DataBind()
...and once to retrieve additional data from the table when an index is selected in my ddlDeptDivAct_SelectedIndexChanged event:
Dim dtDeptDivAct As DeptDivActDataTable
If ddlDeptDivAccount.SelectedIndex > 0 Then
dtDeptDivAct = GetAllDeptDivActCodes()
dtDeptDivAct.DefaultView.RowFilter = "Id = " & ddlDeptDivAccount.SelectedValue
txtAddFundingDept.Text = DirectCast(dtDeptDivAct.DefaultView(0).Row, DeptDivActRow).Department.ToString.PadLeft(2, Char.Parse("0"))
txtAddFundingDiv.Text = DirectCast(dtDeptDivAct.DefaultView(0).Row, DeptDivActRow).Division.ToString.PadLeft(2, Char.Parse("0"))
txtAddFundingAct.Text = DirectCast(dtDeptDivAct.DefaultView(0).Row, DeptDivActRow).Activity.ToString.PadLeft(3, Char.Parse("0"))
Else
txtAddFundingDept.Text = ""
txtAddFundingDiv.Text = ""
txtAddFundingAct.Text = ""
End If
Note: The GetAllDeptDivActCodes() method is a simple method that returns the table from the ApplicationCache object.
The web page works fine. I can select my value and the proper values are insterted into the TextBox. However, when I go to a different page and come back to this page. My drop down list only has 1 value available for selection.
When I pulled up the debugger, I noticed that upon returning to the web page, when the GetAllDeptDivActCodes method returns the DataTable from the cache, the DefaultView RowFilter property was still applied to the DataTable, which was causing the problem.
I have fixed the issue for now by simply resetting the the DefaultView RowFilter once processing is done in the SelectedIndexChanged event, but why is the Application returning what appears to be a reference to the DataTable in the application cache when I was expecting a seperate copy (or value) of the object?
This is by design. Whenever you store an object in the Application State or Session State you are returned the actual object (or as you put it a reference to the object) when you access it. By design .NET objects are almost always passed by reference unless you specify otherwise. Forexample when passing objects to Functions they are passed by reference.