I'm writing an ASP.NET C# web site that needs to access data from a database and show it to the user for viewing and editing. The specific data it accesses is based on the user who logs in, and I need for multiple users to be able to use the site simultaneously, viewing and editing different data as they do so. I stumbled upon the concept of Session States, and after a lot of reading and not as much understanding. I've come across a problem.
In my default page, I do this to create a Session variable:
Session.Add("UserData",userdata);
I have also tried this:
Session["UserData"] = userdata;
Then in a later page, I do this to try to call it:
object myobject = Session["UserData"];
This gives me an error, saying that Session["UserData"] is not set to an instance of an object. This is the method everyone seems to be using, is there something I'm missing?
My site is configured on IIS to have the Session State Mode set to "In Process", but most people seem to set this manually using their web.config file. However, when I try to do this in my web.config file I am always greeted with "unrecognized configuration section". My compiler doesn't know what this is:
<sessionstate mode="inproc"/>
EDIT, more detailed code:
MyClass userdata = new MyClass();
userdata.name = "myname";
userdata.number = 5;
Session["UserData"] = userdata;
later...
MyClass mydata = (MyClass)(Session["UserData"]);
This returns the error that Session["UserData"] is null.
The fact that you can't set the session mode in the web.config is a red flag to me of something weird and smelly going on. So ...
Check that the session mode is under the system.web element of the web.config otherwise it won't be valid.
Check that enableSessionState hasn't been set to false in either the web.config or the page directive
Try to rule out IIS. If possible convert your website to a web app and run through visual studio so it starts with it's own built in web server. What happens then? Is the Session state back?
It should n't make a difference but if you are not doing the test in Page_Load then just try it there - just in case you are doing these tests somewhere unusual.
Whatever the answer is to this when we know it will be headachingly obvious. I'm geninuely looking forward to finding out what it is. Good luck
Session variables are good to manage multiple users on your website, but to initialize them you should use the Global.asax file in your web application. This file has two methods specifically for Session variables, Session_Start and Session_End. To initialize your Session variable you would use code liked the following in Global.asax:
void Session_Start(object sender, EventArgs e)
{
// initialize session variable
Session["MySessionVar"] = 1;
}
Also you may have to cast the value of your session variable if you are doing operations on it like +, for example if you have a session variable holding an integer value, you may have to do like the following:
Session["MySessionVar"] = ((int) Session["MySessionVar]) + 1;
Also, if you try to use your session variable outside of a method like Page_Load or other method, like trying to use it as a property of the System.Web.UI.Page class in your C# code behind file, that may not work, you can only use your session variables within a method.
I would search for any calls to Session.Clear or Session.Abandon to see if your session is being purged in between those two actions.
You could also hook up to the Session_End event and see if that gets hit sometime in between the two calls.
Where you have
Session.Add("UserData",userdata);
you want to check the value you need to cast the object with (string) like this
string userdata= (string)(Session["UserData"]);
you could then run a check to see
if(string.IsNullOrEmpty(userdata))
but not sure how you are initializing and assigning a a value to userdata
Does it complain the your myobject is null or that Session is null? When you try to retrieve the value you are doing this from the method of what class?
Yet another question - by any chance are you trying to access it in a parallel thread?
Related
I've got an ASP.NET that relies on some code that uses static constructors. The code in these type initializers sometimes fails. Let's say, for sake of argument, that the code is:
public static readonly string Thing = SomeSpecialCallThatRarelyFails();
Perhaps that's vile, but it cannot be changed. And this kinda code is in every controller, so ASP.NET can't create the controller and just sits there broken until someone comes along to restart it.
I understand this is the way it should be, because the problem may very well be non-transient and auto-restarting would create a loop. Or perhaps only one controller fails, so the app is still sort of alive. So I get the default behaviour to just keep returning the error. But in this particular case, let's pretend the best thing is to notice this failure and restart.
How can I automate the detection of this scenario and trigger a restart or recycle of the IIS app pool/AppDomain?
I've noticed that if I cause an exception on Application_Start, then the app will auto-restart. So one way is for me to iterate over all my types and try accessing them. If they have .cctor failures, then I'll crash Application_Start and ASP.NET will restart. But that's pretty hacky, plus it won't help if the actual request code references another type that I don't know about which throws on .cctor.
Is there a better way? Should I write a Web API filter and look for TypeInitializerException or something?
Just a thought. Is the 'rare failure' deterministic? Could it be solved by adding retry logic?
public static readonly string Thing = RetrySpecialCall();
private static string RetrySpecialCall()
{
while (true)
{
try
{
return SomeSpecialCallThatRarelyFails();
}
catch (Exception) {}
}
}
So here's a way to handle it in Web API 1:
In Application_Start, iterate over your controller types, calling System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor to force all your known type constructors to run. If it Application_Start fails, ASP.NET seems to restart.
Add an exception filter that looks for TypeInitializationExceptions. Then call HttpRuntime.UnloadAppDomain().
The two parts are needed as a controller failing to construct will not hit the exception filters.
With Web API 2 it seems like you could do it in one go by implementing System.Web.Http.ExceptionHandling.IExceptionLogger and registering it as a global service. Same logic: check for TypeInitializationException and UnloadAppDomain if so.
I'm want to be able to set the caller ID on my
XrmServiceContext : Microsoft.Xrm.Client.CrmOrganizationServiceContext
Context that has been generated for crm using svcutil.exe.
As far as I can tell I cant do this on an existing connection and I need to first create an instance of OrganizationServiceProxy set the CallerID and then pass it as a paramater to a new XrmServiceContext which I can then use instead.
However I'm kind of stuck on how I go from a CrmOrganizationServiceContext to having a OrganizationServiceProxy
The program is a separate .Net4.5 application
Any helpful tips or links?
Edit: Found this page just after posting this: http://msdn.microsoft.com/en-us/library/gg695810.aspx
So it may be as simple as:
var connection = new CrmConnection("Xrm");
connection.CallerId = uide;
_serviceContext = new XrmServiceContext(connection);
Edit 2: It was not as simple as that.
Doing this resulted in no change of what data I received.
CrmConnection connection = new CrmConnection("Xrm");
connection.CallerId = Guid.NewGuid();//u.Id;
_serviceContext = new XrmServiceContext(connection);
It compiles and dosen't crash but I was suspicious when I used the id of a user with very low privledges but still got all data back, I then tried generating a new Guid for every instance of the XrmServiceContext but I am still getting everything back. So I am guessing it is not being used.. or I am missing something else.
Edit 3
Doing a WhoAmIRequest after the CallerID has been set still returns the same user that is set in the connection string.
Edit 4
Seems my problems are Cache related.
In my implementation I need to first make a call to the service context to figure out the Guid of the user I want to impersonate. This call is made without CallerID set. If I skip this initial query and just set a specific Guid from the beginning the CallerID works. I'm guessing this is because the service context has cached my original CallerId or something similar.
Now I just have to figure out how to clear the cache in CRM 2013 SDK.
Edit 5
By turning of the cache completly using this guide: http://msdn.microsoft.com/en-us/library/gg695805.aspx I have gotten it to work. I would however prefer if I could just clear it out at the one point I need to instead of disabling it completly.
If someone can show me how to empty the service context cache using code I will mark that as the correct solution
There is a method that can be used when dealing with your "_serviceContext"
You should be able to use: _serviceContext.ClearChanges(); This clears all tracking of a particular entity within the Cache. See the Public Methods Section
The problem is related to the default instanceMode that is defined in the web.config under the microsoft.xrm.client section.
By default, the setting is set to PerRequest
PerRequest – returns the same first instance in the context of a Web
request, for example. one instance for each HttpContext instance.
So, in this case, when you do the initial call to work out which user you want to set the CallerId to, the instance is being 'cached' (for lack of a better word) and on subsequest calls within the same request, this instance is being returned, even if you are creating a new XrmServiceContext
The solution is to change the instanceMode to PerInstance
PerInstance – returns a new instance on each call.
Modify your web.config so that the instanceMode attribute is specified correctly
<microsoft.xrm.client>
<contexts>
<add name="Xrm" type="Xrm.XrmServiceContext, Xrm" serviceName="Xrm" />
</contexts>
<services>
<add name="Xrm" type="Microsoft.Xrm.Client.Services.OrganizationService, Microsoft.Xrm.Client" instanceMode="PerInstance" />
</services>
</microsoft.xrm.client>
Found this information in the article posted by JensB in his 5th edit: http://msdn.microsoft.com/en-us/library/gg695805.aspx
I am making a project using WebServices in C#. I wanted to ask you, how can I give a value to a variable from Client to a Web Service?
For example:
In the web service I have a variable and two Methods, getVariable() and setVariable(bool a);
bool var = false;
[WebMethod]
public void setVariable(bool a)
{
var = a;
}
[WebMethod]
public bool getVariable()
{
return var;
}
This is how my web service looks (it's simple because I am learning).
My client:
//In the client I added the web service as a Service Reference and added this code:
ServiceReference.ServiceSoapClient obj = new ServiceReference.ServiceSoapClient();
private void Form_Load(object sender, EventArgs e)
{
obj.setVariable(true);
label1.Text = obj.getVariable().ToString();
}
And when I load my form, the label1.Text isn't equal with "True" but with "False"!! Which means that it didn't execute this code: obj.setVariable(true);
My professor said in the class something that WebService are now "full...." (but I couldn't hear it well), he said you have to find a way to make WebServices "ful..."
Can someone help me ?
Web services are stateless by default, which means that they don't retain state between calls. So a value set in one call won't be available for usage by the next call.
If you need a stateful service, there are several ways to go. this is probably the simplest.
It sounds like you are doing this for learning purposes, in which case I suggest reading up on why it is not a good practice to develop using stateful services. Try this one for starters.
Since web services are stateless you can't do what you are trying to do.
Although you have only one client instance, for each call a server-side instance gets created. That means that in the first call you set the variable on an object, which then goes out of scope (since it's tied to that request). When you do make the second call, a new instance of your service gets created and this new instance obviously has no knowledge of the previous one.
If you want to do something like that you need the save the state. This can be done in various ways:
Save the value in the Application object (HttpContext.Current.Application("myvar") = a)
Save it in session state (you need to add an attribute to the service class to enable session state, then you do the same as with the Application but accessing HttpContext.Current.Session
Save it in a database
Save it in a file
...
#I4V - Thanks for your comment on my post.
I just added static before the bool variables and it worked.
Is there room for issue in the following code in terms of multiple users of the same web application? I mean, I know that a purely static string will be shared across all sessions for a single ASP.NET application, but given that this explicitly refers to the Current.Session, even though it is static it seems like it would always refer to the session instance of the "current user."
But an error is happening that could be explained by everyone sharing the current value of Mode and thus the most recent change overwriting everyone else's mode value.
(As a background: This string is in a Helpers class that is used throughout the application. I do not want to make hard-coded references to Session["Mode"] throughout the application and do not want to have to pass Session["Mode"] in every method call from an aspx.cs page.)
public static string Mode
{
get
{
var value = HttpContext.Current.Session["Mode"];
return (value ?? string.Empty).ToString();
}
set
{
HttpContext.Current.Session["Mode"] = value;
}
}
HttpContext.Current always returns the context of the current request (if there is a current request).
Since each user will be executing a different request, each context will be different.
Your property is static. This is actually the cause of sharing the property between users.
See Scope of static Variable in multi-user ASP.NET web application for more details.
OK so to set and read variables from the current session
String Myvar =(string) System.Web.HttpContext.Current.Session[“MyVariable”]
To set
System.Web.HttpContext.Current.Session[“MyVariable”] = “NewValue”
I can do neither, I get a System.NullReferenceException: Object reference not set to an instance of an object. from System.Web.HttpContext.Current.Session.
In my web.config I have
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" cookieless="false" timeout="20">
</sessionState>
I have read a dozen articles on the the necessity of IHttpHandler and an IRequiresSessionState interface. I think the issue may be caused because I am requesting this information in Page_PreInit. I found a solution in a stack overflow article but I don't seem be using it properly to actually make this go.
I am not sure what I am missing. Thanks in advance.
As the comment mentioned, is there some reason you need this in the PreInit event?
PreInit happens very early in the page lifecycle. It happens, in fact, before even the master page (if any) is applied, before all the controls are fully initialized, etc.
A much better choice for the majority of applications is in the Load event. If you're still getting a NullReferenceException there, then there's a bigger issue.
You can access the session by implementing IRequiresSessionState interface in your class.
This is a flag interface, so you dont need to implement any extra code.
When you implement this asp.net will know that you want to access the session.
public partial class YOUR_ASPX: System.Web.UI.Page , IRequiresSessionState
{
... your code
}
To access the session state pre-init you can do something like this. I use it so that I can have a different admin master than the regular user one. Each page has a method at the top.
PageTools tools = new PageTools();
protected void Page_PreInit(object sender, EventArgs e)
{
tools.setMasterPage(Page, Context);
}
PageTools is my class that holds the method that chooses the appropriate mater page and has the http handler.
public void setMasterPage(Page page, HttpContext context)
/***********************************************************************
* Author Daniel Tweddell
* Date 9/18/09
*
* Several of the pages are for non-admin use, however these pages will
* also be used by the admin users and will need to have the admin menu
* and such. So based on the login, we either show the page with the
* standard master or if the user is admin, use the admin master.
***********************************************************************/
{
if (context.Handler is IReadOnlySessionState || context.Handler is IRequiresSessionState)
{
context.Handler = Handler();
}
String sMasterPage="~/content/page.master";
if (userinfo.IsUserAdmin) sMasterPage="~/content/administrator/admin.master";//make sure the user is admin
page.MasterPageFile = sMasterPage;
}
Here is a step by step to setting up the httphandler. (which is the other thing you'll need.