We can attach to this event from global.asax file creating method with name Application_End(). But I need to attach to it like this
HttpContext.ApplicationInstance.ApplicationEnd+=OnApplicationEnd;
Is there any way to do it?
Have solved the problem in such way.
public class MyHttpApplication:HttpApplication
{
public event Action ApplicationEnd;
protected void Application_End()
{
if (ApplicationEnd != null)
ApplicationEnd();
}
}
In global.asax defined
<%# Application Inherits="MyLib.MyHttpApplication" Language="C#" %>
Then in code
var app = HttpContext.ApplicationInstance as MyHttpApplication;
app.ApplicationEnd += () => { // do something };
Application_End is a special "event" that is called by Asp.net that doesn't belog to the HttpApplication class.
From MSDN
*The Application_Start and Application_End methods are special methods that do not represent HttpApplication events. ASP.NET calls them once for the lifetime of the application domain, not for each HttpApplication instance.*
I think you can have the same behaviour attaching and handler to the AppDomain.DomainUnload event
//your global.asax class contrauctor
public GlobalApplication()
{
AppDomain.CurrentDomain.DomainUnload += ApplicationEnd;
}
private void ApplicationEnd(object sender, EventArgs e)
{
}
I know the answer is already given but wanted to also include this way:
[assembly: PreApplicationStartMethod(typeof(Bootstraper), "Start")]
[assembly: ApplicationShutdownMethod(typeof(Bootstraper), "End")]
public static class Bootstraper
{
public static void End()
{
...
}
public static void Start()
{
...
}
}
Related
I'm creating an ASP.net Core application with onion architecture.I want to raise some events in Business Logic layer and subscribe to these events from infrastructure layer.(layers from inner to outer: Domain - Contracts - Business Logic - Infrastructure - API)
one of my BL classes and event implementation:
public class LiveStreamBusinessLogic : ILiveStreamBusinessLogic
{
public event ILiveStreamBusinessLogic.LiveStreamEventHandler LiveStreamEventOccured;
public async Task<IBusinessLogicResult<PagedList<LiveStreamForShowDto>>> GetAllLiveStreamAsync(LiveStreamParameters liveStreamParameters)
{
// some logic
OnDomainEventOccured();
return new BusinessLogicResult<PagedList<LiveStreamForShowDto>>
{Success = true, Result = livesListForTransferPaged};
}
protected virtual void OnDomainEventOccured()
{
LiveStreamEventOccured?.Invoke(this, EventArgs.Empty);
}
}
also i'm using DI for creating upper class with this interface :
public interface ILiveStreamBusinessLogic
{
public delegate void LiveStreamEventHandler(object sender, EventArgs args);
public event LiveStreamEventHandler LiveStreamEventOccured;
Task<IBusinessLogicResult<PagedList<LiveStreamForShowDto>>> GetAllLiveStreamAsync(LiveStreamParameters liveStreamParameters);
}
and this class will instantiated through StartUp class:
services.AddScoped<ILiveStreamBusinessLogic, LiveStreamBusinessLogic>();
and my subscriber is :
public class ElasticLogger
{
private readonly ILoggerManager _loggerManager;
private readonly ILiveStreamBusinessLogic _liveStreamBusinessLogic;
public ElasticLogger(ILoggerManager loggerManager, ILiveStreamBusinessLogic liveStreamBusinessLogic)
{
_loggerManager = loggerManager;
_liveStreamBusinessLogic = liveStreamBusinessLogic;
Subscribe();
}
private void Subscribe()
{
_liveStreamBusinessLogic.LiveStreamEventOccured += OnDomainEventOccured;
}
private void OnDomainEventOccured(object sender, EventArgs e)
{
_loggerManager.LogInfo(Serialize(e).ToString());
}
}
and StartUp :
services.AddScoped<ElasticLogger>();
the problem is event will raise correctly but the handler does not execute. I guess there is a problem with the procedure of instantiating my classes in startup but have no idea how to solve it? any solution or even better pattern for this problem?
By looking at your example codes here, I cannot see that you are creating an instance of the ElasticLogger. That means no ElasticLogger is created, therefore it couldn't Subscribe.
You can check if my theory is correct or not, by putting a breakpoint in the constructor of ElasticLogger. If you never hit the breakpoint, then I'm right.
I suggest you to refactor ElasticLogger, don't call Subscribe from the constructor. But do like this:
public class ElasticLogger
{
private readonly ILoggerManager _loggerManager;
private readonly ILiveStreamBusinessLogic _liveStreamBusinessLogic;
public ElasticLogger(ILoggerManager loggerManager, ILiveStreamBusinessLogic liveStreamBusinessLogic)
{
_loggerManager = loggerManager;
_liveStreamBusinessLogic = liveStreamBusinessLogic;
}
public void Subscribe()
{
_liveStreamBusinessLogic.LiveStreamEventOccured += OnDomainEventOccured;
}
private void OnDomainEventOccured(object sender, EventArgs e)
{
_loggerManager.LogInfo(Serialize(e).ToString());
}
}
But, make sure you call it externally, after your application starts, call it like: elasticLogger.Subscribe() externally. Then your event should be handled.
I created Global.asax file and put it into my web application, which works on SharePoint 2013.
In Global.asax.cs file, I inherited SPHttpApplication class and override
BeginRequest event. It does not work, the Global.asax file is ignored by SharePoint, or I miss something else to add.
The goal is to create redirection when session expires. What am I doing wrong?
This is my code in code behind:
namespace PhoenixHR.Global
{
public class PhenixHRHttpApplication : SPHttpApplication
{
public override void Init()
{
try
{
base.Init();
this.BeginRequest += new EventHandler(Moj_BeginRequest);
}
catch (Exception ex)
{
}
}
private void Moj_BeginRequest(object sender, EventArgs e)
{
try
{
if (HttpContext.Current.Session["TPL"] == null)
Response.Redirect("~/PhoenixHR/start.aspx");
}
catch (Exception ex)
{
}
}
}
}
Global.asax
<%# Assembly Name="Microsoft.SharePoint"%>
<%# Application Language="C#" Inherits="PhoenixHR.Global.PhenixHRHttpApplication,PhoenixHR.Global,Version=1.0.0.0,Culture=neutral,PublicKeyToken=a0bd8fabe3543dc0" %>
As you have some exception swallowing in your code can you prove the change is not working? or is it raising an exception every request and just ignoring it?
You will be better off with binding to a different event. The HttpApplication class has quite a few events you can bind and BeginRequest is very early (first) in the processing of the request pipeline. This will not have access to the Session State. I would suggest using AcquireRequestState or PostAcquireRequestState.
In order for this to work you will need to create an HttpModule.
namespace PhoenixHR.Global
{
public class SessionMissingRedirectHttpModule : IHttpModule
{
private const string LAYOUTS_SUBFOLDER = "/PhoenixHR.Global";
private const string START_PAGE = "/start.aspx";
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.PostAcquireRequestState += context_PostAcquireRequestState;
}
void context_PostAcquireRequestState(object sender, EventArgs e)
{
// get required contexts
SPHttpApplication application = (SPHttpApplication)sender;
HttpContext context = application.Context;
HttpSessionState session = context.Session;
if (session == null)
return;
if (session["TPL"] != null)
return;
// get SharePoint context and current SPWeb.
SPContext sharepointContext = SPContext.GetContext(context);
SPWeb currentWeb = sharepointContext.Web;
// build a url to the redirect page.
string layoutsFolder =
SPUtility.GetLayoutsFolder(currentWeb);
string url = string.Format("{0}/{1}{2}{3}", currentWeb.Url, layoutsFolder, LAYOUTS_SUBFOLDER, START_PAGE);
// prevent redirection loop
if (context.Request.Url.AbsoluteUri.Equals(url, StringComparison.InvariantCultureIgnoreCase))
return;
SPUtility.Redirect(url, SPRedirectFlags.Trusted, context);
}
}
}
To register the HttpModule you will need to edit the web.config of the Web Application that it will run in.
In the /Configuration/system.webServer/modules path add the following
<add name="SessionMissingRedirectHttpModule" type="PhoenixHR.Global.SessionMissingRedirectHttpModule, PhoenixHR.Global,Version=1.0.0.0,Culture=neutral,PublicKeyToken=a0bd8fabe3543dc0" />
There are some best guesses for paths to the required application pages so you may need to alter the variables to suit.
In my program I'm trying to use session variable in IHttpModule. Here is my code. This is working fine in VS 2010 development server. But when I try to debug in IIS7 it shows exception System.Web.HttpException: Session state is not available in this context
So why session not available in IIS 7 but in development server.
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
HttpApplication http_application = sender as HttpApplication;
http_application.Session["language"] = "test";
}
}
All you need is to implement IRequiresSessionState.
So your code should look like:
public class Globalizer : IHttpModule, IRequiresSessionState
if all you use is reading Sessions (and never update them) then you should use IReadOnlySessionState as this last do not lock the session state, and therefor, you will not have concurrent requests).
If possible, move your handler to a PostAcquireRequestState handler:
Occurs when the request state (for example, session state) that is associated with the current request has been obtained.
(Or an even later event)
The SessionStateModule itself loads session state in response to the AcquireRequestState event - So at the moment, you're depending on whether your module or the session state module's handlers fire first.
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regradless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
Found the reason.
If is because AcquireRequestState trigger for the all files such as CSS, JS, images. those files are not having session.
solution: Seems there is a way to avoid calling IHttpModule for all the request. ckeck this answer JS,Images and CSS getting intercepted by HTTPModule.
But it didn't work for me. So I uses HttpContext.Current.Session instead of HttpApplication.Session and every time it checks if Session is null before save to session.
If some one knows, How to avoid calling IHttpModule for files other than .aspx Please help here.
Here is the final code
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
if(HttpContext.Current.Session != null){
HttpContext.Current.Session["language"] = "test";
}
}
}
Edit: One other way is only use session if request comes to a .aspx file
HttpApplication http_application = sender as HttpApplication;
HttpContext context = http_application.Context;
if(Path.GetExtension(context.Request.PhysicalPath) == ".aspx")
{
HttpContext.Current.Session["language"] = "test";
http_application.Session["language2"] = "test2";
}
I am trying to execute some code in the application start of an HTML Module. Since the Init() gets fired multiple times, is there a reliable flag to tell me if the application started or not?
public class Module : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
//PROCESS ON APPLICATION START EVENT
this.OnApplicationStart(context);
}
#endregion
public void OnApplicationStart(HttpApplication context)
{
if (!application started??) //FRAMEWORK FLAG?
//DO SOMETHING
}
}
You could use a flag:
public class Module : IHttpModule
{
private static bool isStarted = false;
private static object syncRoot = new object();
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
if (!isStarted)
{
lock (syncRoot)
{
if (!isStarted)
{
//PROCESS ON APPLICATION START EVENT
this.OnApplicationStart(context);
isStarted = true;
}
}
}
}
public void OnApplicationStart(HttpApplication context)
{
//DO SOMETHING
}
}
As a better alternative to using HttpModules to perform this task if you are targetting .NET 4.0 or later I would recommend you using WebActivator which is a very handy package based on Microsoft.Web.Infrastructure allowing you to subscribe to events such as Application_Start in separate libraries.
For example, simply put the following code in a class library:
[assembly: WebActivator.PreApplicationStartMethod(typeof(WebAppInitializer), "Start")]
namespace FooBar
{
public static class WebAppInitializer
{
public static void Start()
{
// PROCESS ON APPLICATION START EVENT
}
}
}
and then referencing the class library in your ASP.NET application is all it takes.
You could also use this handy WebActivator to perform dependency injection into your HttpModules and self register them without the need to add them to web.config. Phil Haack wrote a nice blog post on this topic if you are interested.
All implementation of IHttpModule I've seen looks following:
class HttpCompressionModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.SomeEvent += OnSomeEvent;
}
private void OnSomeEvent(Object source, EventArgs e)
{
// ...
}
public void Dispose()
{
// nothing here !!!
}
}
I am wondering why is the Dispose method always empty? Shouldn't we unsubscribe the event which we subscribe in the Init method?
The lifecycle of an HttpModule is tightly integrated with the lifecycle of an HttpApplication. Instances of HttpModule are generated when the application is started and destroyed when the application is disposed of.
In this case there is no point in unsubscribing from the event because the publisher (HttpApplication) is being disposed of anyway. Of course, in a situation where the publisher wasn't being disposed of, unhooking the event handler would be the right thing to do.
The dispose method won't be empty if you need to instantiate IDisposable objects inside your module.
class HttpCompressionModule : IHttpModule
{
private IDisposalbe _myResource;
public void Init(HttpApplication application)
{
_myResource = new MyDisposableResource();
application.SomeEvent += OnSomeEvent;
}
private void OnSomeEvent(Object source, EventArgs e)
{
// ...
myResource.DoSomething();
}
public void Dispose()
{
_myResource.Dispose();
}
}