I have a simple web site built with asp.net. It typically only has 1 or 2 users at one time. My question is, is it ok to instantiate a class at the class level or should I be instantiating for each method. Here is an example. I have a class named Host with a name field and mac field. In my code behind for a specific page Is it ok to do this:
public partial class addhosts : Page
{
private Host host = new Host();
private HostDal dal = new HostDal();
protected void myMethod()
{
host.Name = "myname"
host.Mac = "mymac"
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
dal.AddHost(host)
}
}
First, what you are referring to are more typically referred to as global versus local variables.
In the simple case that you have listed, it would be better to create the variable on the submit click. The reason is if a user loads the object, but never calls the submit click, then you have instantiated the host object in memory when there was no need.
However, as many have said, it should not really matter one way or the other here. But, again, this is a simplistic example. Global variables can be dangerous and are often avoided as they can be modified from anywhere in your class. If one method expects a certain value that is then overrode, this can cause difficult to debug issues in more complex examples
Here is a wikipedia article that reiterates my above point:
They are usually considered bad practice precisely because of their
non-locality: a global variable can potentially be modified from
anywhere (unless they reside in protected memory or are otherwise
rendered read-only), and any part of the program may depend on it
To get rid of the globals, you could do this (using object initializers)
protected void btnSubmit_Click(object sender, EventArgs e)
{
var host = new Host
{
Name = "myname",
Mac = "mymac"
};
dal.AddHost(host)
}
It's completely OK to have user specific data as fields inside an ASP.Net Page instance. Every visit to a page creates a new instance of the Page class hence you'll not end up in a situation where data is incorrectly shared between users
It shouldn't matter.
Each request to your page is separate and has no knowledge of other requests therefore there is no chance of there being a "conflict" with other requests.
It's perfectly fine, since for each user new class is created (ASP.NET by design). It's in separate thread also, so even static variables would be acceptable in this scenario.
Cheers, Ivan
Related
Here is an article about the subject global-variables-aspnet
In a given example on page load there is an initialization of our static global variable:
protected void Page_Load(object sender, EventArgs e)
{
// 1.
// Get the current ImportantData.
string important1 = Global.ImportantData;
// 2.
// If we don't have the data yet, initialize it.
if (important1 == null)
{
// Example code.
important1 = DateTime.Now.ToString();
Global.ImportantData = important1;
}
// 3.
// Render the important data.
Important1.Text = important1;
}
And the commentary to it:
Performance: Static fields are efficient in normal situations. You
will only have one copy of the data and there is no locking required.
My question is - why there is no double-null-check and no locking required?
Am I missing something is ASP.NET multi-threading?
I believe the article is incorrect, at least when considering the generalized statement: No locking required, ever. I am not aware of any mechanism that would automatically serialize access to static members in the Global class, and I am also not aware of any ASP.Net mechanism that would serialize Page_Load calls. While that article only showed read access after a one-time initialization (in most cases, see end of post), it stands to reason that somebody will be mislead by it and use those static members to read and write at different times. Example:
public static class Global
{
public static int PageRequests;
}
...
protected void Page_Load()
{
// This construct without locking might lead to lost counts
Global.PageRequests++;
}
Locking is definitely required here, unless you don't care if your page request count is not entirely accurate.
In the specific case with the DateTime initialization and read-only access afterwards, you can live without a lock. Should two Page_Load threads successfully test for important1 == null at around the same time, they would both write the same or almost same DateTime value into that variable, and from a split second later on, this variable will never be changed again because important1 == null will then evaluate to false. But this is a very special case where it doesn't matter. Certainly not a generalization.
If I have to, I will pass an arg to a page when I navigate to it, but is it necessary - is there a way to know which page called Navigate() without doing this?
Keep a static property in the Application object called PreviousPage. In the OnNavigatedFrom event of every page (or in the base class) set the PreviousPage to "this" (current page). In the OnNavigatedTo event, you can check that Application.PreviousPage property and use it how you see fit. This is the most effective way of accessing the previous page as there is no Frame.BackStack property in the framework for Windows Store Applications.
You could use GetNavigationState but it has some undesirable side-effects (it navigates away from the current page and the string is internal with no guarantees about how it might/might not change). I think you're best off passing it.
Calling this method will call Page.OnNavigatedFrom for the current
page using NavigationMode.Forward. GetNavigationState is usually
called when the application is being suspended, so the current page is
navigated away from.
Note:
The serialization format used by these
methods is for internal use only. Your app should not form any
dependencies on it. Additionally, this format supports serialization
only for basic types like string, char, numeric and GUID types.
I ran into this exact problem and Caleb's answer was very helpful. I just wanted to post some code to clarify how I used his solution.
DISCLAIMER I'm a bit (very!) new to C#, I'm sure there's a better way to do this, but I wanted to provide it as a starting point in case somebody else was looking for a bit more detail.
I created a global variable in the application's namespace to hold the previous page state:
public static class global_vals{
static object _previousPage;
public static object previousPage
{
get
{
return _previousPage;
}
set
{
_previousPage = value;
}
}
}
The OnNavigatedFrom method on each of my pages looks something like this:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
global_vals.previousPage = this;
}
And the OnNavigatedTo method checks the global variable like this:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (global_vals.previousPage.GetType().ToString() == "AppName.SomePage")
{
//do something
}
}
Surely it must be a common problem but I can't find an easy way to do this.
Is there a way to share a global variable (say a class which is expensive to instantiate) through the life cycle of an asp.net webform, without having to pass the handle to every single component of the page?
If I just create a static variable, it will be accessible by all threads in the app domain (problem: my class is not thread safe), and it will be hard to ensure that every page works on a recent copy (I want to cache the class through every step of the life cycle of the page, but I want a new instance every time a new page is called).
The alternative is to pass a handle through each control in the page, but between the master page, the page itself, and all the user controls, it makes the code quite hairy.
I was wondering if there wasn't an elegant solution to store a class in some place which is accessible only to the thread executing this particular page (including all sub user controls)?
Any suggestion welcome!
Thanks
Charles
There is a simple answer: the Items container. It is available for the lifetime of a single request and then it is automatically destroyed. You can wrap it in a property to have what you want:
public class Foo {
const string SOMEKEY = "_somekey";
public static string SingleRequestVariable
{
get
{
return (string)HttpContext.Current.Items[SOMEKEY];
}
set
{
HttpContext.Current.Items.Add( SOMEKEY, value );
}
}
}
and then
Foo.SingleRequestVariable = "bar"; // somewhere
...
string val = Foo.SingleRequestVariable; // yet somewhere else
I have a multi-user set of ASP.NET web pages. The pages use AJAX update panels so I can avoid updating the screen on every postback.
The lifecycle of each page is as follows:
1. During Page_Load, get relevant data for user from a web service.
2. Store the data (quite large), and a service reference in a static dataset.
3. allow various edits to parts of the data via the screen controls (grids, text boxes)
4. validate data captured via form
5. send updated data back to service
I am doing this using static variables in the Page class itself as follows:
public partial class MyPage : System.Web.UI.Page
{
static xxxx.DataCaptureServiceClient m_Service; //reference to web service
static string m_PersonID = string.Empty; //current person_id page is viewing
static ServResponse m_ServiceResult = null; // reference to our data to edit ( ServResponse is a large data contract)
static string m_SortExpression = "Reference"; //default sort expression for grid
const int PERSONID_COLUMN = 0; //column index in grid for the personID column
const int STATUS_COLUMN = 4; //column index in grid for the application status
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (!Page.IsPostBack)
{
// Get new service instance.
m_Service = new xxxx.DataCaptureServiceClient();
ShowDataOnPage(); //get data in m_ServiceResult and bind to a grid on screen
}
}
catch (Exception ex)
{
Response.Redirect("ErrorPage.aspx", false);
}
}
protected void butNext_Click(object sender, EventArgs e)
{
try
{
Page.Validate();
if (Page.IsValid)
{
// Use m_ServiceResult and m_Service to send a packaged submission to the service.
SendDatatoService();
Response.Redirect("TheNextPage.aspx", false);
}
}
catch (Exception ex)
{
Response.Redirect("ErrorPage.aspx", false);
}
}
//Other methods which allow edits to m_ServiceResult
I am wondering if:
A) This is a good way to implement or are there better practices?
B) Should I clear down memory by setting all statics to NULL when I redirect to another page?
C) If I clear down the statics do I risk another user losing data?
UPDATE
I have rewritten removing the statics, keeping the const values and passing data I need around as parameters. Where I need to keep data for updates I have kept the minimal amount I need in session[] variables.
A) No - what happens if a 2nd user opens a page while another one is busy? The static dataset will be overwritten with the 2nd user's data, or is your static dataset somehow differentiating different users' data at the same time?
B) If you absolutely must use statics / server side data, then yes, you should clear them somehow. Guaranteeing that this happens is difficult however. (E.g. if one user just closes their browser)
C) Possibly, but if this is a concern then my question in A) is already going to cause greater problems for you.
As a general answer, storing large amounts of data in memory on the server is generally bad practice. It does not scale well, and opens you up for many different types of errors. Your back-end should be stateless, and there are a number of ways you could achieve that, for example storing the records in a separate table in the DB, which only get finalised (and thus moved into the "real" tables) at the end of the several screens you have.
In direct answer to your questions, without opening a can of worms
A) There is pretty much no worse way to implement data capture
B) Setting variables to null in .NET does not clear down memory.
C) Yes, yes you do. By definition every user is sharing the same static data.
I would keep your service delcarations local, and only use global variables for the constants. You're not saving much by declaring them globally. Also, I would use a const string instead of a static string.
I think the others have answered your questions. My suggestion to help fix your code would be to do some of the following:
m_PersonID should not be static - keep it an instance property/field of the page and have it as static. In fact, class-level variables in code behind can lend itself to fairly unreadable code very quickly. Does it really need to be class-level, or can it be defined in the method where you need it (and just, perhaps, passed as an argument to other methods)?
m_ServiceResult - same for this. Not sure why you have it, and it doesn't need to be static. Try to move it to a method-level variable.
m_SortExpression could just be a const
m_Service - again, I don't see the need for this to be a static. If it's just a proxy to a service, I don't think you need to keep it in memory to avoid unnecessary overhead. I think they problems keeping it static far outweigh the small overhead of having the service client be a method-level variable.
In regards to the static dataset, you may want to consider caching the large result set, if the data is fairly static for the user. Or, really take a look at what you're returning and evaluate if you really need all that data. If you're performing calculations in the code-behind, that's probably not the best place for it. Consider your service layer as the place for those calculations (or the db if you have a lot of your logic in stored procs - not saying that's the place for it, but just acknowledging that it's a possibility).
Also, a dataset is pretty large and bulky. Do you need this structure, or is a set of streamlined entity objects more appropriate? This item pretty much goes with #5, since you really should evaluate how you're retrieving data, storing it and acting upon it. A dataset may be the quick and dirty answer, but usually it's not the efficient answer.
In short, dump the statics and move to private, method-level variables where possible. Try to reduce the amount of data you have coming back from the database. Consider methods that take parameters instead of using class-level and/or global variables.
I hope this helps. Good luck!
A instance of a class is created in the partial class of an aspx page.Under page_load or button click method I'm trying to set the value to the class. but when each postback takes place new instance is created and I'm losing the previous value.
public partial class DatabaseSelection : System.Web.UI.Page
{
DBProperties dbpro;
Metadata obmeta;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dbpro = new DBProperties();
}
If you need this instance by application throw it in the Application or use a Singleton collection:
Application["Foo"] = new MyClass();
See other answer.
If you need this for a single request (which seems unlikely here):
HttpContext.Current.Items["Foo"] = new MyClass();
If you need this across requests the the following are all options depending on your scenario:
Serialize into Cookie (will be transfered on every request so if the pipeline is an issue don't use this). Good for per user data.
Store in Session: Session["Foo"] = new MyClass(); // I personally don't like this option because it tends to grow your memory pressure as your user base grows, but if this is small then this is a good option as it does not increase bandwidth consumption and performance. Good for per user storage. And persistence to DB or memory (and others) can be configured.
Store in Cache. Benefits are good control over lifetime and some flexibility on whether per user depending on what keys you use.
Store in ViewState. Good across a single request, scalable, but increases payload.
Store in hidden var (about the same as ViewState)
I am not really sure I would recommend the Singleton pattern. Technically singletons will stick around as long as your AppDomain effectively being similar to the Application variable.
Are you looking for Singleton pattern?
Just create a method that accesses the session state and tries to return it if it is already there. If not, it creates it, stores it in the session, and returns it.
If you want the object to persist between post backs you'll need to store it in either Session/View State or persist it to a database.