This is probably going to sound rather naive, but I'm developing a web application that spans multiple pages. All of these pages instantiate the same class object w/ methods that accesses a CMS using their API. Currently, when a user starts creating content, I'm storing variables like a folder ID where the content is located in a Session variable.
My question is this: Can I instantiate a single instance of a class that can be used across all pages without having to do it on every page? If so, would each person accessing that page be given their own version of the class? I assume that using static variables and methods isn't the way to go since they are shared in memory. And also, where/how is something declared if it is going to be used globally in a Web Application in a .net C# application?
I recommend making a base class which inherits from System.Page. Then have your page code behind inherit from that.
Then, inside the base class, create a property that is a reference to your object. Like this, for example:
public class BasePage : System.Web.UI.Page
{
public Foo CurrentFoo
{
get
{
return (Foo)Session["FooSessionObject"];
}
set
{
if(Session["FooSessionObject"] == null)
{
// instantiate a new one
Session["FooSessionObject"] = new Foo();
}
Session["FooSessionObject"] = value;
}
}
}
Then, from anywhere in any page, just do a CurrentFoo. and you will have access to any of the properties.
This makes for nice and clean code behind.
You should use Session state if you want to store information that is specific only to the current user.
For example in one page of the application you could store some value into the session:
SomeType someValue = ...
Session["someKey"] = someValue;
and then later on other page retrieve it from the session:
SomeType someValue = (SomeType)Session["someKey"];
The pattern you are looking for is quite easy. There are a couple of ways to accomplish it.
Create an object and store it in session
Create a multi-part form and leave the items in viewstate
Each has its benefits.
Bad thing about doing this and using global over sessions for website is the simple fact that global variables could be accessed by any user accessing the regardless of session. For example take the code below.
public class globals
{
static string _test = "MyTest";
public static string test
{
get
{
return _test;
}
set
{
_test = value;
}
}
After I created the global class I added a label to my default from and assigned the global variable text to the label.
protected void Page_Load(object sender, EventArgs e)
{
globals.test = "Some Data";
}
now you will see this on every form of you page however the down side to this is anyone else that accesses your page will also be able to see this data. If its data that needs to be shared across multiple sessions you could do this. If its data that just needs to be shared by the current user over multiple classes you need to use session variables.
Related
I have seen many "wrapper" classes for the ASP.NET Session state and some do something like:
Strongly Typed Layer (Pseudo Code #1)
public class MySession
{
public int MyID
{
get
{
return Convert.ToInt32(HttpContext.Current.Session["MyID"]);
}
set
{
HttpContext.Current.Session["MyID"] = value;
}
}
public string MyName
{
get
{
return (HttpContext.Current.Session["MyName"]).ToString();
}
set
{
HttpContext.Current.Session["MyName"] = value;
}
}
...
public MySession()
{
// Could be static or instantiated depending on needs...
}
...
}
///// USAGE IN OTHER CLASS /////
MySession currSession = new MySession();
currSession.MyID = 5;
currSession.MyName = "John Doe";
Console.WriteLine($"{currSession.MyName}'s ID = {currSession.MyID}");
Then I have seen others do something like:
Generic List Variant (Pseudo Code #2)
public class SessionVariables
{
public int MyID
{
get;
set
{
MyID = value;
MySession.SaveVariables();
}
}
public string MyName
{
get;
set
{
MyName = value;
MySession.SaveVariables();
}
}
...
}
public class MySession
{
public static List<SessionVariables> Variables;
// Might be private in real application environment
public MySession() // Could be static or instantiated depending on needs...
{
if (HttpContext.Current.Session["MyVariables"] == null)
{
HttpContext.Current.Session["MyVariables"] = new List<SessionVariables>();
}
// Obviously more appropriate checking to do here, but for simplicity's sake...
Variables = (List<SessionVariables>)HttpContext.Current.Session["MyVariables"]
}
public static void SaveVariables()
{
HttpContext.Current.Session["MyVariables"] = Variables;
}
...
}
///// USAGE /////
public class MyPage
{
public void MyMethod()
{
MySession currSession = new MySession(); // Create variables
MySession.Variables.MyID = 5;
MySession.Variables.MyName = "John Doe";
Console.WriteLine($"{MySession.Variables.MyName}'s ID = {MySession.Variables.MyID}");
...
}
}
Thoughts
Obviously, these examples are both pseudo code style (so please ignore general errors), but they illustrate some of the approaches to building a data access layer for the Session state.
I do something similar to the first variant, albeit, with a more comprehensive data type mapping/conversion plan. I use a an "normal" class to wrap Session in, but it could easily be static since the properties will pull from the Session state when their "get" is called and thus never be out of sync since the class actually doesn't hold any data itself.
The second seems more "overkill" to me from first impressions since yes, you are only storing one variable in the Session state, but it clutters up the rest of the code by forcing code to be making references to the list:
myObject.TheList.VariableIWant
VS
myObject.VariableIWant
of which I prefer the later (just looks cleaner), though this could easily be hidden in a super class or just making a local variable directly reference the list:
new MySession(); // Create the variables
List<SessionVariables> mySession = MySession.Variables;
... though that looks kind of dirty to me at first glance. However, I don't know how much of a benefit using a list for storage actually gives to code/performance since storing an object that represents a list should take as much memory as doing each variable separately, at least that is my thinking.
Question
Which is better practice / low maintenance in the long-term? And/or Which gives better performance to the website?
Option #1 is the most common pattern that I see, and I use it. You can improve it by using constants instead of magic strings. Sessions have their issues, but so does making a completely stateless app. I also recommend using HttpCache instead of Session -- it will not consume AppPool resources. But only Sessions can be used on a web farm, as long as you use a Session provider like SQL Server. Distributed caching is another matter.
With option 1 it's really easy to tell what it's doing. You're trying to standardize how your classes save/retrieve session data rather than scattering it all over the place.
Option 2 is a lot more confusing. In fact I've looked it over a few times and I can't figure what's going on the list. Why does option 2 require a list when option 1 doesn't?
For what you're trying to do, option 1 works just fine. The constants aren't a bad idea, but in this one case I might skip it. The meaning of the string is pretty obvious, and other classes won't need to duplicate it because they're going through this one to access Session.
Option #1 > Option #2
Two reasons:
You should be deleting session variables as soon as you are done with them, using Session.Remove. Otherwise your session state will keep getting bigger and bigger and your web server won't be able to support as many simultaneous users. But if all your variables are held in one big session variable, this is a bit harder to accomplish.
I would avoid using reference types (e.g. a List of any kind) in session. It creates an ambiguity: if your session is stored in-proc, the session is only storing a pointer, and you can change session variables by changing the objects that they reference just like normal reference types. But if your session is out of proc (e.g. using state server or SQL state) then your objects will be serialized and frozen, and if you change the objects that are referenced, those changes will not get reflected in your session variables. This could create all sorts of bugs that only appear on your upper environments (if your dev systems lack a state server) and drive you mad trying to troubleshoot.
You could possibly make an exception for immutable reference types, but you'd have to be careful; just because an object is immutable doesn't mean the objects that it references are immutable too.
How can I declare global variables in MVC application without using static ?
I am currently following the static approach and it is causing issues when the application is logged in from different users at the same time.
If you want to make them global per user, I would suggest to use the Session context to save the variables to. This is not really on a user level, since a user can log in multiple times, but usually this suits better in my experience.
I usually have something like this:
public string Prop
{
get
{
return (string)Session["Prop"];
}
set
{
Session["Prop"] = value;
}
}
This will make accessing the property very easy and consistent.
Similar to session you can also use application object. Also you can store variables in cache.
i have a small class with just a couple properties in it.
here is an example:
public class clsRepLogs
public string those;
public string these;
public void setThoseandThese
{
//call a stored procedure
//get results
this.those = something;
this.these = somethingElse;
}}
from my first.aspx.cs
i call the set function:
clsRepLogs cLog - new clsRepLogs()
cLog.setThoseandThese()
so now the properties have been assigned values.
i now want to use them in another aspx.cs file to populate a form... but can't figure out how to get to them...
i tried
clsRepLogs cLog;
lblThese.text = cLog.these;
but it's giving me an error: "Use of unassigned local variable 'cLog'
basically, how do i tell it to use the values i've already assigned to that class instance from before?
i hope i'm explaining this right, but i might be way of on what i'm doing. any help appreciated.
It sounds like you want to access the same instance of the class from multiple ASPX pages. There are multiple ways to do this; a per-user solution is to use session state.
// page 1
var c = new clsRepLogs();
c.setThoseAndThese();
Session["mykey"] = c;
// page 2
var c = (clsRepLogs)Session["mykey"];
"mykey" can be any string. You may also want to check if Session contains the value before accessing it, e.g. if( Session["mykey"] != null ){ ... }
Keep in mind:
Session isn't durable; restarting the web worker process will reset all in-process sessions.
It's usually not a good idea to store many large objects in Session.
If the data is confidential, be aware of Session fixation attacks.
Session can be load balanced across servers, so this solution will scale.
For reference (alternative approaches):
You could use the Cache object when you don't care about per-user isolation and want control over data expiration.
You could use the Application object; similar to Cache but no control over expiration.
You could store the object in the database and load it on each page. This is useful when you to don't want a heavy session, and/or wish to save the data more permanently (such as a shopping cart which persists across multiple sessions).
You need to initialize or set cLog to the instance of clsReport that you want.
You aren't instantiating your class. It should be
clsRepLogs cLog = new clsRepLogs();
Or, if I misunderstood your code,
clsReport cLog = new clsReport();
EDIT:
If you need to preserve these, it should be a static class so that it cannot be instantiated.
public static class clsReport
{
public static string those;
public static string these;
public static void setThoseandThese
{
//call a stored procedure
//get results
this.those = something;
this.these = somethingElse;
}
}
A lot of this depends on how you need this to function. In addition, I'm no expert on perpetuating objects/classes in memory in Webforms applications. If you wanted to re-access the class to get those values, I'm not sure they would still be in memory.
You will need to initialize your cLog class :
clsRepLogs cLog = new clsReport();
lblThese.text = cLog.these;
I have a asp.net project with c# code behind. I have a static class called GlobalVariable where I store some information, like the currently selected product for example.
However, I saw that when there are two users using the website, if one changes the selected product, if changes it for everybody. The static variables seem to be commun to everybody.
I would want to create (from the c# code) some kind of session variable used only from the c# code, but not only from the page, but from any class.
Yes static variables are shared by the whole application, they are in no way private to the user/session.
To access the Session object from a non-page class, you should use HttpContext.Current.Session.
GlobalVariable is a misleading name. Whatever it's called, it shouldn't be static if it's per session. You can do something like this instead:
// store the selected product
this.Session["CurrentProductId"] = productId;
You shouldn't try to make the Session collection globally accessible either. Rather, pass only the data you need and get / set using Session where appropriate.
Here's an overview on working with session storage in ASP .NET on MSDN.
You sort of answered your own question. An answer is in session variables. In your GlobalVariable class, you can place properties which are backed by session variables.
Example:
public string SelectedProductName
{
get { return (string)Session["SelectedProductName"]; }
set { Session["SelectedProductName"] = value; }
}
I have an OnClick event for my Pushpins as below.
void BusStop1061_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Buses from this station: City Link");
}
I would like to pass the bustop number to another page and to the int "PassedVariable"
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
string site = "http://www.sourceDomain?stop=" + PassedVariable;
webBrowser1.Navigate(new Uri(site, UriKind.Absolute));
}
I was thinking of creating a constant int on the Page one, then passing it to page 2 using urimapping, but it doesn't seem to work and I figured someone might have a better option
I have had a look at similar posts, but they dont quite answer my question.
There are multiple ways to achieve this.
One way, is to create a BustopNumber property in the App.xaml.cs class, a set it in one page, and access it else where.
This is my preferred approach, although you have to guard against the case where the value isn't set or invalid.
Another approach is to pass it in as a query string parameter, similar to what you are doing in your code snippet above. In the navigated to page, you can access the query string parameter via the NavigationContext object and look for by name.
Edit to add:
public partial class App : Application
{
public static int BusStopNumber { get; set;}
}
//And in your application you can access it like so:
App.BusStopNumber = 10;
Obviously there are issues with encapsulation, as this is essentially a hack for global, but if used carefully, can offer a quick and easy way to share information across multiple pages.
One of the approaches is to add a class with common data that can be shared between the views. This is one of the way and may not be the best.
You can have a static class which can create singleton of Session and provide it to XAML as part of User control binding. Session class can be enhanced in future with multiple properties if you want.
View1 View2
^ ^
| |
| |
v v
Session Service (stores user selection)
View1 and View2 should have reference to Session Service.
public class Session
{
public int BusStop {get; set;}
}
You should start looking at MVVM pattern to modularize your code and avoid any such issues in future.
you can use PhoneApplicationService. include the shell namespace. using Microsoft.Phone.Shell;
PhoneApplicationService appSer = PhoneApplicationService.Current;
appSer["busStopNumber"]=number;
and when you want to use it already in another page, do this (initialize the appSer also in the page)
if(appSer["busStopNumber"]!=null)
{
int number = (int)appSer["busStopNumber"];
}