I am working on a .net project that relies on pulling some information from a database upon login and persisting the information while the person is logged in.
The site is complex, but I feel that overuse of the Session variable may be occurring. I will right now say that I'm using session to store about 5 int values and 3 or 4 string values. I don't have any complex objects stored in it. Every page of my site utilizes these values multiple times, and I feel that posting them each time would be ridiculous. I don't think that viewstate is any better than session at this point either, but i'm open to suggestions.
Example current usage of Session variable
this.Session["vendorCategoryId"]
this.Session["ftp_directory_name"]
this.Session["VendorCodeOptionRefId"]
I thought about implementing a struct in a class and having a set method there that I use from Global.asax.cs in the Session_Start() event might be one solution...
(EDIT: This solution is designed to avoid having to hammer the db every time any piece of code wants to access the variables).
In VendorSessionData.cs class
public struct VendorData
{
public int VendorCategoryId;
public int NKIAccountingCode;
public int OptionCodeRefId;
public string FtpDirName;
}
/// <summary>
/// The set vendor session data.
/// </summary>
public class VendorSessionData
{
#region Public Methods
public static VendorData GetVendorData(Guid vendorGuid)
{
VendorData retVal = new VendorData();
using (NKIDBDataContext db = new NKIDBDataContext())
{
vendorInfo vendorRefs = (from vendorInfo in db.vendorInfos where vendorInfo.guid == vendorGuid.ToString() select vendorInfo).SingleOrDefault();
if (vendorRefs != null)
{
retVal.VendorCategoryId = vendorRefs.category_id;
retVal.NKIAccountingCode = vendorRefs.nki_vendor_id;
retVal.OptionCodeRefId = vendorRefs.option_ref_id;
retVal.FtpDirName = vendorRefs.ftp_directory_name;
}
}
return retVal;
} ......
And in global.asax.cs
public class Global : HttpApplication
{
public static VendorData CurrentVendorData;
public void Session_Start(object sender, EventArgs e)
{
/////////////////FYI tmpVendorGuid is set in code above this point
Guid tmpVendorGuid;
if (Guid.TryParse(vendorGuid, out tmpVendorGuid))
{
CurrentVendorData = VendorSessionData.GetVendorData(tmpVendorGuid);
}
Would it be better to try to attack this using hidden fields on the master page? Every search I've done says "don't use session, don't use globals.." basically any idea I have, there is some page stating its the worst idea ever :)
Any suggestions would be appreciated.
The amount of data isn't particularly surprising, but the choice is.
Normally a session would store user information (typically just their ID - you fetch the rest from the DB). "ftp_directory_name" sounds like a config variable or a per-user setting.
I'm surprised you don't store VendorData in the database with a unique ID and then just fetch the details (you'd need to store the Vendor ID in the session - if that's not the same as the User).
This solution is designed to avoid having to hammer the db every time any piece of code wants to access the variables
This seems like it might be a solution to a problem that doesn't exist - are you seeing problems with access time and are you sure this is the right solution? You're going to have to hit the DB for most pages anyway. One extra query per page load is unlikely to be a problem.
I'd say you're probably already doing it right... Session was designed for storing information for the user's "session"... I don't think you're taxing it too much as if you were storing an entire DataSet or something... A few int variables and a string take up less than 1K of memory... If you had 1,000 users logged in at the same time, their collective "Session" footprint would still be about 1MB... pitons on today's servers.
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.
I'm creating asp.net-mvc application where user is uploading multiple files.
The data will be compared with db data, processed and exported later. Also paging.
When displaying these data, sorting and filtering is importing.
When data is uploaded, some of them will be stored in db, some will be displayed as not found in db, some will be modified and stored ... etc
My question is, what is the best way to store the uploaded data in order to be available to be process or viewed?
Load in memory
Create temp tables for every session? (even don't know if possible)
Different storage which can be queryable (access data using linq) (JSON??)
Another option.
The source files are (csv or excel)
One of the files example
Name Age Street City Country Code VIP
---------------------------------------------------------
Mike 42 AntwSt Leuven Belgium T5Df No
Peter 32 Ut123 Utricht Netherland T666 Yes
Example of class
public class User
{
public string Name { get; set; }
public Address Address { get; set; } // street, city,country
public Info Info { get; set; } // Age, and Cres
}
public class Info
{
public int Age { get; set; }
public Cres Cres { get; set; }
}
public class Cres
{
public string Code { get; set; }
public bool VIP { get; set; }
}
There are a variety of strategies for handling this (I actually just wrote an entire dissertation over the subject), and there are many different considerations you'll need to take under consideration to achieve this.
Depending on the amount of data present, and what you're doing with it, it may be simple enough to simply store information in Session Storage. Now how you actually implement the session store is up to you, and there are pros and cons to how you decide to do that.
I would personally recommend a server side session store to handle everything and there are a variety of different options for how to do that. For example: SSDB and Redis.
Then from there, you'll need a way of communicating to clients what has actually happened with their data. If multiple clients need to access the same data set and a single user uploads a change, how will you alert every user of this change? Again, there are a lot of options, you can use a Pub/Sub Framework to alert all listening clients. You could also tap into Microsoft's SignalR framework to attempt to handle this.
There's a lot of different If's, But's, Maybe's, etc to the question, and unfortunately I don't believe there is any one perfect solution to you problem without knowing exactly what you're trying to achieve.
If the data size is small and you just need them to exist temporarily, feel free to go with storing them in memory and thus cut all the overhead your would have with other solutions.
You just need to be sure to consider that the data in memory will be gone if the server or the app is switched off for whatever reason.
It might also be a good idea to consider, what happens if the same user performs the operation for the second time, while the operation on the first data is not completed yet. If this can happen to you (it usually does), make sure to use good synchronization mechanisms to prevent race conditions.
I know this might be a silly question for the MVC pros but it become a headache for me for the last couple of hours so please accept my excuses!
I have a class called DeliveryDetails as below:
Public Class DeliveryDetails
{
Prop1;
Prop2;
Prop3;
PropX;
}
The concept is that i want the logged in users to edit the DeliveryDetails class data as a process of checkout even if there is no data in the backend database table. Is that possible? Or should i first save the DeilveryDetails and on the second time of purchasing i bring up DeliveryDetails in editing mode?
Of the top of my head, based on your input:
You could create DeliveryDetails and store its reference into Session on Session_Start event (you could also load it from database or some other source, if neccessary).
protected void Session_Start(object sender, EventArgs e)
{
Session["DeliveryDetails"] = new DeliveryDetails();
}
Then when you need your DeliveryDetails instance, you just extract it from session like this:
var deliveryDetails = (DeliveryDetails)Session["DeliveryDetails"];
// deliveryDetails.Prop1 = ...
// deliveryDetails.Prop2 = ...
// etc.
But in this way, you must be careful to init/reset DeliveryDetails object inside Session, when it is appropriate.
This way you can decide when to write data into database (if required), but the user will have its DeliveryDetails always available through Session. Of course, if you do not use Sessions, you may not be able to do this.
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 problem with static variable. Part of the organization of my controllers is as follows:
namespace MyApp.Controllers
{
public class DevicesController : Controller
{
static int some_var = 0;
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SetValue(int temp){
some_var = temp;
return RedirectToAction("DisplayValue");
}
[Authorize]
public ActionResult DisplayValue(){
....
return View(some_object);
}
}
}
The problem arises when multiple users simultaneously using this view. The same static variable is used by all users and change its value. How solve this?
Your entire ASP.NET MVC application runs within an AppDomain, that is the application plus all requests being served for all users, everything!!
When you create a static variable, a single instance is declared and made available to the entire AppDomain, every request from every user will see the same value.
An instance variable (simply remove the 'static' word) is specific to an instance of the particular object it's in. In this case the object is an instance of your Controller, so your variable as an instance variable will be individual/specific to that controller object only. The ASP.NET runtime will create an instance of your Controller for each request it serves, then discard that controller object once its processed the request. So if you remove the static word, the variable will only stay around for the duration of that request, then disappear.
What you need, as other posters have said, is Session state. Session State lasts the duration of a session, as in someone browsing your site, and is specific to each user. So if you store that variable in Session State, it will be different for each user.
Problem is Session state disappears when the user leaves your website, so if you need it to the stay around longer, you then should use something like a database.
From your comments and original post, it sounds (and I'll try to put this as politely as possible) that you haven't quite grasped some Object-Oriented Programming idioms and concepts. While Session state isn't a OOP concept per se, the difference between static and instance variables is. I would recommend brushing up on these, as an understanding in these concepts (fundamental to OO programming) would mean, IMHO, you wouldn't even be asking this very question. I hope this answer has helped in your understanding somewhat.
You can use,
HttpContext.Current.Session["some_var"]
instead of some_var, this will help. This will preserve for the user that is logged, one session, and you can access it statically with HttpContext.Current
namespace MyApp.Controllers
{
public class DevicesController : Controller
{
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SetValue(int temp){
HttpContext.Current.Session["some_var"] = temp;
return RedirectToAction("DisplayValue");
}
[Authorize]
public ActionResult DisplayValue(){
....
return View((int)HttpContext.Current.Session["some_var"]);
}
}
}
Make it a private instance variable, not static.
If you need to maintain this count per user (but only for the given session), then you can do the following:
if (Session["Count"] == null)
Session["Count"] = 0;
Session["Count"] = (int)Session["Count"] + MyNewValue;
If you want the count to persist across session, then you can persist it to a database.
Static variables (and properties) will be shared amongst all instances of that type -- in this case, your controller. The value will be lost when the application pool is restarted.
If you need the value to persist across requests, for a specific user, then you may need to look to move it to the session, or similar.
If you only need the value to remain for the duration of the current request, then a private variable would suffice.
There is an important difference between class variables and object variables. Static variables are class variables, this means all objects that are instantiated from a class share the same variable, so a change of that static variable in an object changes other objects of the same class too.
But a nonstatic variable (object variable) is created for each instantiated object, so change doesnt effect others.
But the problem is not to choose between this two because your need is different. You need a variable that will be kept for each user. So as others stated, you must use a session variable.
But i think you should read about object variables and class variables.
Statics are neither good nor bad. They simply have a different usage structure.
Consider that you are processing international telephone numbers.
You will need a country_code, country and a unique id at a minimum in order to do this. You may also need to IDD country escape prefix.
So you create an object to hold all of this and then gather them into a list from say a database or some other method.
You want a drop down to list the country that the user picks and then you want the country_code to go with whatever it is that you are gathering.
If you create the list of country objects as static then when you make the round trips to and from the server you don't have to re-load the list from it's backing store everytime, you just use it to populate the drop down list. Since this data never changes it's ok to make it static. Everyone on the site will be using the same list of countries so, having one copy does not matter. However, you will have to make the Id used to index the list non static so that when you restore the selection on the list everyone has their selection and not a global static one that shows what the last person selected.
Set your models non static county_code_id to used the id selected in the list and then when you are ready to update the data use the models country_code_id to lookup the country_code in the list of country objects to return the country_code for your update.
Like I said, statics are neither good or bad, they just have a different usage.