How do I avoid calling my initialization method repeatedly in ASP.NET? - asp.net

protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) { // sadly, **never** in here }
MyInit() // Slow initialization method, that I only wan't to call one time.
}
So, if I can't tuck my MyInit() in the if, can I solve my performance/strucktur problem with use of OnNeedDataSource()?

Not really sure if this is what you mean, but to initialise something once from Page_Load, you could use a static class with a static bool to determine if it's been initialized. Given it's on Page_Load, you'll also need to guard against multiple threads - so use a double checked lock to make it threadsafe and guard against a race condition.
public static class InitMe
{
private static bool isInitialized = false;
private static object theLock = new Object();
public static void MyInit()
{
if(!isInitialized)
{
lock(theLock);
{
if(!isInitialized) // double checked lock for thread safety
{
// Perform initialization
isInitialized = true;
}
}
}
}
}
and in your Page_Load, call it via InitMe.MyInit()
Hope that helps.

Try this:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Page.IsPostBack) { MyInit(); }
}
I assume you are in a page or user control...
HTH.

Related

difference between Page_Init vs OnInit

I had an interview a week ago and one of the questions was what the difference between OnInit, Page_Init and PreRender. which one is preferable?
Page_Init is an event handler for the Page.Init event, you typically see it if you add a handler within the class itself.
OnInit is a method that raises the Init event.
The two can be seen as being equivalent if used within a subclass, but there is a difference: only Init is exposed to other types, the OnInit method is protected and is responsible for raising the event, so if you override OnInit and fail to call base.OnInit then the Init event won't be fired. Here's how it looks like:
public class Page {
public event EventHandler Init;
public event EventHandler Load;
protected virtual void OnInit(Object sender, EventArgs e) {
if( this.Init != null ) this.Init(sender, e);
}
protected virtual void OnLoad(Object sender, EventArgs e) {
if( this.Load != null ) this.Load(sender, e);
}
public void ExecutePageLifecylce() {
this.OnInit();
// do some houskeeping here
this.OnLoad();
// further housekeeping
this.Dispose();
}
}
public class MyPage : Page {
public MyPage() {
this.Init += new EventHandler( MyPage_Init );
}
private void MyPage_Init(Object sender, EventArgs e) {
// No additional calls are necessary here
}
protected override void OnLoad(Object sender, EventArgs e) {
// you must make this following call for any .Load event handlers to be called
base.OnLoad(sender, e);
}
}
Generally overriding the OnLoad / OnInit methods is faster (but this is a microptimisation, you're only saving a couple of extra instructions for delegate dispatch) and many "purists" will argue using events unnecessarily is just ugly :)
Another advantage of not using events is avoiding bugs caused by AutoEventWireUp which can cause events to be called twice for each page load, which obviously is not desirable if your event-handlers are not idempotent.

ASP.NET MVC3 : Connected clients count

In my ASP.NET MVC3 web application I'd like to add a small message displaying the number of users currently browsing the site.
I'm currently using Session_Start and Session_End application events to increment or decrement a static property inside Global.asax.
This works, but it isn't precise at all. Since my session timeout is configured to 20mn there's a huge delay between updates.
Is there a more elegant, precise way of doing this?
I've thought of calling an action via AJAX which simply does Session.Abandon() on the window.onbeforeunload javascript event, but this would be called each time the user changed pages. Is there a way to determine when the user closes his browser or leaves the domain?
Any hints, comments or code examples would be welcome!
Here is the relevant part of current code:
public class MvcApplication : System.Web.HttpApplication
{
public static int UsersConnected { get; set; }
protected void Session_Start(object sender, EventArgs e)
{
Application.Lock();
UsersConnected++;
Application.UnLock();
}
protected void Session_End(object sender, EventArgs e)
{
Application.Lock();
UsersConnected--;
Application.UnLock();
}
...
}
Okay this was a bit tricky but I've come with an 'okay' solution.
First, Ive created a static dictionary in Global.asax which will store the IP address of the clients and their last poll date.
public class MvcApplication : System.Web.HttpApplication
{
public static Dictionary<string, DateTime> ConnectedtUsers { get; set; }
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ConnectedtUsers = new Dictionary<string, DateTime>();
}
...
}
Then, in my CommonsController, I've created this action which will add new clients, remove clients which haven't been polled in the last 30 seconds and update the poll date of already registered clients :
public class CommonsController : Controller
{
...
public JsonResult UserConnected()
{
string ip = Request.UserHostAddress;
if (MvcApplication.ConnectedtUsers.ContainsKey(ip))
{
MvcApplication.ConnectedtUsers[ip] = DateTime.Now;
}
else
{
MvcApplication.ConnectedtUsers.Add(ip, DateTime.Now);
}
int connected = MvcApplication.ConnectedtUsers.Where(c => c.Value.AddSeconds(30d) > DateTime.Now).Count();
foreach (string key in MvcApplication.ConnectedtUsers.Where(c => c.Value.AddSeconds(30d) < DateTime.Now).Select(c => c.Key))
{
MvcApplication.ConnectedtUsers.Remove(key);
}
return Json(new { count = connected }, JsonRequestBehavior.AllowGet);
}
}
Finally, on my layout page, added this code which will call my action every 30 seconds and output the result in a span :
<span id="connectedUsers"></span>
<script type="text/javascript">
function PollUsers()
{
$(function() {
$.getJSON("/Commons/UserConnected", function(json){ $('#connectedUsers').text(json.count + " user(s) connected")});
});
}
setInterval(PollUsers, 30000);
</script>
May not be that precise, maybe not that elegant either, but it works. Of course, multiple users from the same IP would count for one user. But it's the best solution I've experimented so far.
you can call a webmethod from javascript on unload body event.
http://www.codeproject.com/Tips/89661/Execute-server-side-code-on-close-of-a-browser
Best regards

Can't i set Session in a class file?

Why session is null in this even if i set:
public class HelperClass
{
public AtuhenticatedUser f_IsAuthenticated(bool _bRedirect)
{
HttpContext.Current.Session["yk"] = DAO.context.GetById<AtuhenticatedUser>(1);
if (HttpContext.Current.Session["yk"] == null)
{
if (_bRedirect)
{
HttpContext.Current.Response.Redirect(ConfigurationManager.AppSettings["loginPage"] + "?msg=You have to login.");
}
return null;
}
return (AtuhenticatedUser)HttpContext.Current.Session["yk"];
}
}
Usually session is not available on application authenticate request.
Session will be available after OnAcquireRequestState call.
Here is application events call sequence
Also, note that session will be available, only if target HttpHandler implements IRequiresSessionState or IReadOnlySessionState, and AuhenticateRequest is usually called for resources like .js or .jpg.
Just throwing this out there. The proper way to reference it is:
System.Web.HttpContext.Current.Session
Or if you've referenced this assembly HttpContext.Current.Session should be good to go.
When i call the method with this code i'm getting error:
public partial class AddNews : System.Web.UI.Page
{
private AtuhenticatedUser yk = (new HelperClass()).f_IsAuthenticated(true);
protected void Page_Load(object sender, EventArgs e)
{
//
}
But when i call the method in Page_Load function it is working
public partial class AddNews : System.Web.UI.Page
{
private AtuhenticatedUser yk =new AtuhenticatedUser();
protected void Page_Load(object sender, EventArgs e)
{
yk = (new HelperClass()).f_IsAuthenticated(true);
}
I think Valera Kolupaev is right ;)

What's the best way to defer actions after a request in ASP.NET?

I'm writing an ASP.NET application. When a specific kind of request is being handled, I want to schedule a method to be called in a certain number of minutes after the request is handled. The postponed method does not need to communicate with the client that made the original request, it is just intended to do some "housekeeping" work. What is the best way to do this in an ASP.NET context? (It's ok to not fire the event if the application domain dies for some reason).
In Global.asax use this to check your incoming request:
protected void Application_BeginRequest(object sender, EventArgs e)
{
CheckRequest(HttpContext.Current.Request);
}
if your request is valid, register a cache entry:
private void CheckRequest(HttpRequest request)
{
if (request)
RegisterCacheEntry();
}
private void RegisterCacheEntry()
{
if (HttpRuntime.Cache[CacheItemKey] == null)
{
HttpRuntime.Cache.Add(CacheItemKey, "your key", null,
DateTime.Now.AddSeconds(60), //change to fire in whatever time frame you require
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback(CacheItemRemovedCallback));
}
}
then process your function in the callback:
private void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
// execute your function
}
You could start a timer (System.Timers.Timer) from one of the application event in global.asax.cs (e.g. in Application_BeginRequest) after checking that it is required for that request.
Then, in the handler of the timer's Elapsed event, make sure that you stop the timer.
E.g. put something like this into global.asax.cs:
System.Timers.Timer _timer = null;
void Application_BeginRequest(object sender, EventArgs e)
{
// check if cleanup must be initiated
bool mustInitCleanup = RequestRequiresCleanup();
if ((_timer == null) && mustInitCleanup)
{
_timer = new System.Timers.Timer(5000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer.Start();
}
}
void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Stop();
_timer = null;
// do cleanup task
}
Simply create a new thread to do the housekeeping work and at its beginning have it sleep for however long you want the server to wait before doing the action.
For example, somewhere in that specific request you want to call DoSomething:
aNewThread = new Thread(Foo);
aNewThread.Start();
public void Foo()
{
Thread.Sleep(5000);
DoSomething();
}

How to use Castle Windsor with ASP.Net web forms?

I am trying to wire up dependency injection with Windsor to standard asp.net web forms. I think I have achieved this using a HttpModule and a CustomAttribute (code shown below), although the solution seems a little clunky and was wondering if there is a better supported solution out of the box with Windsor?
There are several files all shown together here
// index.aspx.cs
public partial class IndexPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Logger.Write("page loading");
}
[Inject]
public ILogger Logger { get; set; }
}
// WindsorHttpModule.cs
public class WindsorHttpModule : IHttpModule
{
private HttpApplication _application;
private IoCProvider _iocProvider;
public void Init(HttpApplication context)
{
_application = context;
_iocProvider = context as IoCProvider;
if(_iocProvider == null)
{
throw new InvalidOperationException("Application must implement IoCProvider");
}
_application.PreRequestHandlerExecute += InitiateWindsor;
}
private void InitiateWindsor(object sender, System.EventArgs e)
{
Page currentPage = _application.Context.CurrentHandler as Page;
if(currentPage != null)
{
InjectPropertiesOn(currentPage);
currentPage.InitComplete += delegate { InjectUserControls(currentPage); };
}
}
private void InjectUserControls(Control parent)
{
if(parent.Controls != null)
{
foreach (Control control in parent.Controls)
{
if(control is UserControl)
{
InjectPropertiesOn(control);
}
InjectUserControls(control);
}
}
}
private void InjectPropertiesOn(object currentPage)
{
PropertyInfo[] properties = currentPage.GetType().GetProperties();
foreach(PropertyInfo property in properties)
{
object[] attributes = property.GetCustomAttributes(typeof (InjectAttribute), false);
if(attributes != null && attributes.Length > 0)
{
object valueToInject = _iocProvider.Container.Resolve(property.PropertyType);
property.SetValue(currentPage, valueToInject, null);
}
}
}
}
// Global.asax.cs
public class Global : System.Web.HttpApplication, IoCProvider
{
private IWindsorContainer _container;
public override void Init()
{
base.Init();
InitializeIoC();
}
private void InitializeIoC()
{
_container = new WindsorContainer();
_container.AddComponent<ILogger, Logger>();
}
public IWindsorContainer Container
{
get { return _container; }
}
}
public interface IoCProvider
{
IWindsorContainer Container { get; }
}
I think you're basically on the right track - If you have not already I would suggest taking a look at Rhino Igloo, an WebForms MVC framework, Here's a good blog post on this and the source is here - Ayende (the Author of Rhino Igloo) tackles the issue of using Windsor with webforms quite well in this project/library.
I would cache the reflection info if you're going to inject the entire nested set of controls, that could end up being a bit of a performance hog I suspect.
Last of all spring.net approaches this in a more configuration-oriented way, but it might be worth taking a look at their implementation - here's a good reference blog post on this.
Here's a modified version of the OP's code that (i) caches injected properties to avoid repeated reflection calls, (ii) releases all resolved components, (iii) encapsulates container access so as not to expose implementation.
// global.asax.cs
public class Global : HttpApplication
{
private static IWindsorContainer _container;
protected void Application_Start(object sender, EventArgs e)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
}
internal static object Resolve(Type type)
{
return _container.Resolve(type);
}
internal static void Release(object component)
{
_container.Release(component);
}
//...
}
// WindsorHttpModule.cs
public class WindsorHttpModule : IHttpModule
{
// cache the properties to inject for each page
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> InjectedProperties = new ConcurrentDictionary<Type, PropertyInfo[]>();
private HttpApplication _context;
public void Init(HttpApplication context)
{
_context = context;
_context.PreRequestHandlerExecute += InjectProperties;
_context.EndRequest += ReleaseComponents;
}
private void InjectProperties(object sender, EventArgs e)
{
var currentPage = _context.Context.CurrentHandler as Page;
if (currentPage != null)
{
InjectProperties(currentPage);
currentPage.InitComplete += delegate { InjectUserControls(currentPage); };
}
}
private void InjectUserControls(Control parent)
{
foreach (Control control in parent.Controls)
{
if (control is UserControl)
{
InjectProperties(control);
}
InjectUserControls(control);
}
}
private void InjectProperties(Control control)
{
ResolvedComponents = new List<object>();
var pageType = control.GetType();
PropertyInfo[] properties;
if (!InjectedProperties.TryGetValue(pageType, out properties))
{
properties = control.GetType().GetProperties()
.Where(p => p.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0)
.ToArray();
InjectedProperties.TryAdd(pageType, properties);
}
foreach (var property in properties)
{
var component = Global.Resolve(property.PropertyType);
property.SetValue(control, component, null);
ResolvedComponents.Add(component);
}
}
private void ReleaseComponents(object sender, EventArgs e)
{
var resolvedComponents = ResolvedComponents;
if (resolvedComponents != null)
{
foreach (var component in ResolvedComponents)
{
Global.Release(component);
}
}
}
private List<object> ResolvedComponents
{
get { return (List<object>)HttpContext.Current.Items["ResolvedComponents"]; }
set { HttpContext.Current.Items["ResolvedComponents"] = value; }
}
public void Dispose()
{ }
}
I've recently started at a company where there are a lot of legacy webform apps, so this looks to be a real interesting approach, and could offer a way forward if we wanted to add DI to existing web pages, thanks.
One point I noticed is that the Injection method uses the container.Resolve to explicitly resolve components, therefore I think we may need to do a container.Release on the components when the Page Unloads.
If we have transient components and don't do this then we may face memory leakages. Not sure how components with Per Web Request lifestyles would behave (i.e. would Windsor pick them up at the end of the web request, even though we explicitly resolved them) but here too may want to play safe.
Therefore the module may need to be extended to keep track of the components that it resolves and release them so that Windsor knows when to clean up.
One thing that was missing from the accepted answers was the fact that the http module needs to be registered in the web.config file (depending on the application) before the module will actually resolve the dependencies on the code-behind pages. What you need is :
<system.webServer>
<modules>
<add name="ClassNameForHttpModuleHere" type="NamespaceForClass"/>
</modules>
</system.webServer>
Other than that the accepted solutions worked like a charm.
Reference to the Microsoft website for adding http modules: https://msdn.microsoft.com/en-us/library/ms227673.aspx
Rather than doing it like this, you could also use a type resolver directly with something like:
ILogger Logger = ResolveType.Of<ILogger>();

Resources