ASP.NET Application Lifecycle - how to check configuration properties exist? - asp.net

I've written a singleton class that exposes the web.config properties in a nice get property kind of way.
I want a Load method to parse the data in the config and set the public properties, and I want to throw exceptions (so they are logged in the EventLog) when a configuration key is missing or can't be parsed.
I tried placing the Load() code in Application_Start of the global.asax but then remembered this will only be run once, or until the application restarts.
Where is the best place to put code that you need to run 'everytime' your site is started/run by the user? I basically want the website to stop functioning if certain config properties cannot be loaded.
Thanks.

When you change your web.config file, the application pool is recycled. This means that the next hit will cause your Application_Start method to be called.
Altering the following files will also
trigger an immediate restart of the
application pool:
- web.config
- machine.config
- global.asax
- Anything in the bin directory or it's sub-directories
On that basis, as soon as your configuration is changed, it will be reloaded the next time a user hits the site, which should resolve the problem with the minimum number of configuration reloads, as opposed to reloading whenever a session starts for example. Therefore, you can do this (in your global.asax):
static bool configValid = false;
void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext context = base.Context;
HttpResponse response = context.Response;
HttpRequest request = context.Request;
// Redirect users to an alternate page if the current config is invalid
// I happen to pass the Url they were attempting to access in the query string
// that way you can give them a "try again" link
if ((!configValid) && (!request.Url.ToString().Contains("BadConfig.aspx")))
{
response.Redirect("BadConfig.aspx?originalUrl=" + context.Server.UrlEncode(request.Url.ToString()));
}
}
void Application_Start(object sender, EventArgs e)
{
// Load config and determine if it's valid, thus setting configValid to true/false
//
//
configValid = false;
}

Related

Context is null in the global.asax file

I have global.asax file, and I use this two functions to update cache value every 15 seconds by this code:
protected void Application_Start(object sender,EventArgs e)
{
Context.Cache.Insert("value","some value",null,DateTime.Now.AddSeconds(15),Cache.NoSlidingExpiration,CacheItemProirirt.Default,new CacheItemRemovedCallback(updating));
}
private void updating(string key,object value,CacheItemRemoveReason reason)
{
Context.Cache.Insert("value","updated value",null,DateTime.Now.AddSeconds(15),Cache.NoSlidingExpiration,CacheItemProirirt.Default,new CacheItemRemovedCallback(updating));
}
but it give me an NullReferenceException, and the context is null, please why I can't use context at the "updating" function?
Application_Start doesn't have any context.
The first event that does is Begin_Request.
Application_Start occurs when the particular website gets fired up for the first time, or after been recycled.
To keep the cache item renewed I suggest you do that in the Begin_Request, where you check if it's there, and if not, initiate it again.
This way it's only use memory while the site is being hit, otherwise not.

User ID initialization on Master Page?

I have a site with multiple pages, not necessarily heirarchical. I want to query the user's identity (using AD...) whenever the user first enters the site, and create session state variables for the convenience of other pages as needed. A user could possibly enter the site without going through the default.aspx page, so I thought I'd put the code in the Master Page's code-behind.
On the assumption this is a good idea, versus some sort of static class that maintains this information, I started setting it up, but found the Master Page code-behind doesn't always seem to get fired when I enter the site. Is this a debugging phenomenon, or am I right, and the Master Page is the wrong place to put this code...?
I would recommend using the Global.asax class. You'll need to add it to your web app if it's not already there. Once you have it, you can then use the various events (session start and end, app start and end and error) to implement business logic particular to what you need exactly.
I tend to monkey around with the logged in user in the Application_PreRequestHandlerExecute event of the global.asax. This will allow you to look at the User Principle (eg - User.Identity.Name) to see who is logged in (or if they're not logged in) and do what you need to (such as set Session information for the user, etc.).
Here's a tidbit of code I've got on one .NET web app that uses the Global.asax for storing user data in the Session.
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e) {
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState) {
SetUserItem();
}
}
private void SetUserItem() {
if (Session["UserItem"] == null)
Server.Execute("~/SetSessionUserObj.aspx", true);
}
... and then the SetSessionUserObj.aspx.cs
protected void Page_Load(object sender, EventArgs e) {
string ID = User.Identity.Name;
MyUser myUser = new MyUser();
UserItem userItem = myUser.GetUserItemByID(ID);
if (userItem != null) {
Session["UserItem"] = userItem;
}
}
This is just one manner that you can go about accessing a user's identity in the global.asax. You don't necessarily have to go about doing a Server.Execute to set user data (I just did it for other reasons that fall outside the scope of this question).
Good luck.

ASP.Net PreSendRequestHeaders unable to access session

The goal is to intercept when user code calls Response.Redirect and alter the URL the browser is being redirected to. To alter this URL, I need access to Session (stored in Session is information that tells me what I should put in this URL.) Mostly I'm just appending a query argument to the redirect location under a circumstance.
PreSendRequestHeaders does let me alter Response.RedirectLocation. That's fine. However, I'm unable to access Session state from here. It's apparently been released before this event is fired.
So, I need a way to get this information into PreSendRequestHeaders; or I need another way to accomplish this. Ultimately my goal is to just append an argument to the query string of wherever the browser is being redirected.
If you can modify the code that writes to Session then you can use Context.Items.
For example, before the Response.RedirectPermanent method existed, I used the following:
//in some library
public static void PermanentRedirect(this HttpContext context, string url)
{
context.Items["IsPermanentRedirect"] = true;
context.Response.Redirect(url);
}
//in global.asax
void Application_PreSendRequestHeaders(object sender, EventArgs e)
{
if (Response.IsRequestBeingRedirected && (bool) (Context.Items["IsPermanentRedirect"] ?? false))
{
Response.Status = "301 Moved Permanently";
}
}

The Application_PreRequestHandlerExecute event doesn't fire for PageMethods. What can I use instead?

This article explains that the PreRequestHandlerExecute event does not fire for PageMethod calls for whatever reason. However, I'm trying to use that event to populate the Principal object with the user's permissions so they can be checked within any web request (PageMethod call or not). I'm caching the permissions in the Session, so I need an event that fires whenever a PageMethod is called, and I need to have access to the Session. This way I can populate the Principal object with the security permissions cached in the session, and User.IsInRole() calls will work as expected. What event can I use?
You should implement an authorization module that will be run with every request that goes up to the server. This way you are able to authorize your principal for any request that come up to the server (page request, method, etc.)
public class AuthorizationModule : IHttpModule, IRequiresSessionState
{
//not going to implement it fully, might not compile
public void Init( HttpApplication context )
{
//you'll prolly want to hook up to the acquire request state event, but read up to make sure this is the one you want on the msdn
context.AcquireRequestState += AuthorizeRequest;
}
public void AuthorizeRequest( HttpContextBase httpContext )
{
// do you work in here
// you can redirect them wherever if they don't have permssion, log them out, etc
}
}
}
After you've crated the module, you'll need to hook it up in the web.config. Your type should include the namespace if it has one.
<httpModules>
<add name="AuthorizationModule" type="AuthorizationModule"/>
</httpModules>
I hope this helps.
You can use the Application_OnPostAuthenticateRequest as shown below (assuming you are using Forms Authentication. Else, pls replace the code with your Authentication mechanism):
public void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
IPrincipal usr = HttpContext.Current.User;
if (usr.Identity.IsAuthenticated && usr.Identity.AuthenticationType == "Forms")
{
var fIdent = (FormsIdentity)usr.Identity;
var ci = new CustomIdentity(fIdent.Ticket);
var p = new CustomPrincipal(ci);
HttpContext.Current.User = p;
Thread.CurrentPrincipal = p;
}
}
Page Methods are static, and bypass the normal Page lifecycle, its objects and its events. The best you can do is pass authentication information as parameters to the Page Method itself.
From my point of view, you can:
1.- Use a common method you can call from every page method server code that have access to Session variables. Please refer to:
http://mattberseth.com/blog/2007/06/aspnet_ajax_use_pagemethods_pr.html
2.- Try to capture a similar behaviour later using __doPostBack() function to run server code. See if this work for you to capture page method async posbacks:
http://www.dotnetcurry.com/ShowArticle.aspx?ID=256
Hope that helps,

How to manage COM access in ASP.NET application

I have built ASP.NET app. I need to utilize a third party COM object SDK for managing documents. The application's name is "Interwoven". According to the COM object documentation you should "Never create more than one IManDMS object per application".
So, I decided to create one instance of the IManDMS object and store it in an Application variable. Here is the function that I use to retrieve a IManDMS object:
public static IManDMS GetDMS()
{
IManDMS dms = HttpContext.Current.Application["DMS"] as IManage.IManDMS;
if (dms == null)
{
dms = new IManage.ManDMSClass();
HttpContext.Current.Application["DMS"] = dms;
}
return dms;
}
…
// Here is a code snippet showing its use
IManage.IManDMS dms = GetDMS();
string serverName = "myServer";
IManSession s = dms.Sessions.Add(serverName);
s.TrustedLogin();
// Do something with the session object, like retrieve documents.
s.Logout();
dms.Sessions.RemoveByObject(s);
…
The above code works fine when only one person is requesting the .ASPX page at a time. When 2 users concurrently request the page I get the following error:
[Sessions ][Add ]Item "SV-TRC-IWDEV"
already exists in the collection
Apparently you cannot add more than one session to the IManDMS object, with the same name, at the same time. This means I can only have one session open, for the entire app, at any given time.
Is there anyone who has experience with a similar issue and can offer a suggestion on how to get around this problem, assuming I can't create more than one IManDMS object per app?
Thanks.
You can use a lock to make sure only one session access the object at the same time. I am not sure what IManDSM does, but based on your code, it looks like the error is caused by
IManSession s = dms.Sessions.Add(serverName);
You are adding the same name to the Sessions collection. Can you try adding a different name like SesssionID to the Sessions collection?
In ASP.Net, you can safely think of each session as it's own application. Here is logic I have used with the IManage SDK kit for years to manage sessions in Global.asax file:
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
ManDMS clientDMS =
new ManDMSClass();
clientDMS.ApplicationName = "My App Name";
IManSession clientSession =
clientDMS.Sessions.Add(
ConfigurationManager.AppSettings["DMS Server"]);
clientSession.MaxRowsForSearch = 750;
clientSession.AllVersions = false;
// Save the configured session for use by the user
Session.Add("Client.Session", clientSession);
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
IManSession clientSession =
(IManSession)Session["Client.Session"];
try
{
if (clientSession.Connected)
clientSession.Logout();
}
catch (System.Runtime.InteropServices.COMException cex)
{
// Ignore COMException if user cannot logout
}
clientSession.DMS.Close();
}
The benefit to this is that the session setup / tear-down is linked to the ASP.Net session, which adequately manages the session, and provides some great speed benefits to the user.
Whenever you need to access the Session object, just use this code on your ASP.Net pages or postbacks:
IManSession clientSession =
(IManSession)Session["Client.Session"];

Resources