I am trying to inherite a base global.asax class to create my custom global.asax class. But my custome inherited global class does not work properly. Its Application_Start wont been called.
Anyone knows whey?
public class BaseGlobal : HttpApplication
{
protected void Application_Start(Object sender, EventArgs e)
{
log4net.Config.XmlConfigurator.Configure();
Logger.Warn("Portal Started"); //I can find it log file
}
......
}
public class MyGlobal : BaseGlobal
{
new protected void Application_Start(Object sender, EventArgs e)
{
base.Application_Start(sender,e);
Logger.Warn("Portal Started 2"); // Can not find it in log file
}
}
<%# Application Codebehind="Global.asax.cs" Inherits="Membership_ABC.MyGlobal" Language="C#" %>
In the log file, I could not find "Portal started 2" but just "Portal Started".
Any ideas?
On startup of an application, the runtime takes the HttpApplication descendant that is pointed out by the Global.asax file, and creates an instance of it. The runtime does not know or care how the class got to be descended from HttpApplication, it just cares that it is, in fact a descendant.
After that it start calling method on it, treating it as a regular HttpApplication object. Since the new modifier effectively breaks the inheritance chain (it's just a new method that happens to share the old methods name) it is not called, but instead the method of the parent class is called. Basically you have this situation (pseudocode):
HttpApplication httpApp = new MyGlobal();
httpApp.Application_Start(..)
// ^^^ calls BaseGlobal.Application_Start(..)
//because the is not an unbroken chain from HttpApplication to MyGlobal
This is an example and consequence of the Brittle Base Class problem, a topic about which Eric Lippert has written in depth.
The solution is to declare the function virtual in the base class and then override it in the child class.
But as you can't edit base class to declare the Application_Start method virtual, it won't work :
Is it possible to override a non-virtual method?
The accepted answer gives an example that matches your case.
Related
I want to create a reference for button click event which happens on another page in UWP project.
Page one (witch button):
private void Button_ItemClick(object sender, ItemClickEventArgs e)
{
// some code to reference to class
}
Class (in external file)
public static void DoSomething()
{
// Do something on Page 2 or run void on page 2
}
If the method is static, you can call it without an instance of the class. Suppose the method is defined in class OtherPage. You could then just do:
private void Button_ItemClick(object sender, ItemClickEventArgs e)
{
OtherPage.DoSomething();
}
In case the method is not static, you cannot easily do that, because there is only one Page instance in memory at a given time, when you have just one navigation Frame. It would make sense to put the method into a service class that would have a singleton instance and you could call it from anywhere. You can use MVVM framework like MvvmLight or MvvmCross to make this easier. These frameworks also maintain navigation stack of pages and their view models so you can potentially access view model instances in the navigation backstack.
I have this C# ASP.NET 4 website.
I would like to have a general method in a class which will include a Response.Redirect or Server.Transfer in it to a specific page.
Both names, Response and Server, does not exist in the context.
How to work this around?
as for the comment by #Maess, please consider something like this (edited):
ASPX:
<asp:Button onclick="MyClass.btnRedirect_Click" ID="btnMyButton" Text="MyButtonText" runat="server" />
Code behind:
public static void btnRedirect_Click(object sender, EventArgs e)
{
Response.Redirect("~/SomePage.aspx");
}
You'll find these as properties within HttpContext.Current
You can use System.Web.HttpContext.Current, but be careful. If there is no HTTP context at the time these methods are called you will get an exception.
It is probably safer to have these classes either require an HttpContext in the constructor or have the methods that require an HttpContext to have them in the method signature.
For instance, maybe you have a class which needs to redirect as you mentioned. Your method signature might look like this: public void CustomRedirect(HttpContext context) then within the body of your signature you would do something like this: context.Response.Redirect("..."). Basically, you make the class or method dependent upon having an HttpContext.
Don't directly reference anything involving the HTTP/IIS stack if you're ever going to be called outside of it.
Your best bet is probably to have your class or method accept either a delegate or an object that implements an interface provided by the calling code. When you're ready to redirect, call that delegate/interface method with the URI (or enough information to construct it) and then the calling code can respond with a server.transfer, a response.redirect or a passing unit test.
public static void ButtonRedirect()
{
MyClass foo = new MyClass(delegate(string s) { Server.Transfer(s); });
foo.DoThings();
}
public class MyClass
{
private Action<string> redirector;
public MyClass(Action<string> redirectAction)
{
redirector = redirectAction;
}
public void DoThings()
{
//Doing stuff
//Aha! this should redirect
redirector("/go/to/here");
}
}
In Global, as an alternative to the application AutoEventWireups, it seems that events are exposed for most of the underlying Application events (BeginRequest, AuthorizeRequest, Error, etc), as well as a set of asynch methods like AddOnBeginRequestAsync etc. However, I cannot find an equivalent event for ApplicationStart!
So my question is, is there anyway to subscribe to the 'same' event that the AutoEventWireup method Application_(On)Start is hooked into?
public class Global : HttpApplication
{
public Global()
{
// I can do this ...
base.BeginRequest += new EventHandler(Global_BeginRequest);
// Or even AddOnBeginRequestAsync();
// But how can I do this?
base.ApplicationStart += new EventHandler(GlobalApplication_Start);
}
protected void Global_BeginRequest(object sender, EventArgs e)
{
// ...
}
protected void Global_ApplicationStart(object sender, EventArgs e)
{
// ...
}
}
(Out of interest ... is there a way to switch off the AutoEventWireups in Global.asax?. Using the AutoEventWireup = "false" attribute only seems to work on aspx pages)
Edit - it seems that ApplicationStart and ApplicationEnd "are special methods that do not represent HttpApplication events". So I might be barking up the wrong tree entirely.
Edit
Re : Why would I need this? Unfortunately, a corporate customer has a framework in place whereby new apps need to inherit their custom HttpApplication class, and FWR, their HttpApplication had already implemented the autowireup Application_(On)Start, meaning that I needed to find another way to override the Framework wireup, so that I can Bootstrap my IoC container and Automapper maps. As per Lloyd's answer, I could also bootstrap in the ctor or Init() as well, although this isn't quite the same. Ultimately I was able to change the corporate framework to allow multiple subscriptions.
You could override Init:
public class MyApplication : HttpApplication
{
public override void Init()
{
base.Init();
}
}
However your constructor could also work just as well.
Be really careful with the Init method.
Using Init method for code that you want to be executed once in the Application lifecycle is a bad option as the Init method is called once for every instance of the HttpApplication...
As you said ApplicationStart and ApplicationEnd are special methods that do not represent HttpApplication events but they work in a similar fashion to the Page events when the AutoEventWireups is set to true..
After jumping to the .NET source code i found that the HttpApplicationFactory class looks for a method named "Application_OnStart" or "Application_Start" in the Global.asax file and then invokes it using reflection => ReflectOnMethodInfoIfItLooksLikeEventHandler().
Check out my question about a similar subject: HttpApplication.Start event does not exist
If I override the System.Web.UI.Page constructor, as shown, when does DoSomething() get called in terms of the page lifecycle? I can't seem to find this documented anywhere.
namespace NameSpace1
{
public partial class MyClass : System.Web.UI.Page
{
public MyClass()
{
DoSomething();
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
For reference, here is the ASP.NET Page Lifecycle Overview:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
Turns out the best answer was right in the MSDN article. I just had to look carefully at the diagram. Construct is the very first event in the Page life cycle (comes before PreInit, Init, Load, etc).
Diagram http://img156.imageshack.us/img156/9246/lifecyclen.jpg
DoSomething(); will be called before member methods. That's not about Page Lifecycle actually. It's about classes and instances. ASP.NET creates an instance of MyClass. (Contructor is executed). After that any other member methods can be called.
To answer your question, an instance is created at step 10:
http://msdn.microsoft.com/en-us/library/ms178473.aspx
Scroll down to "The request is processed by the HttpApplication pipeline."
How do I access a page class from another class. For example I have:
public partial class MyPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{ }
}
Why can I not access it from another class in App_Code folder?
public class MyClass
{
public MyClass() {}
public void DoSomething(object o)
{
// The following line won't compile.
MyPage page = o as MyPage;
}
}
I just figured it out (thanks to Fujiy) that for some reason this is the case with website project, but is not a problem with web application project in VS. If anyone has any clues as to why, please share your thoughts. Thank you :)
I see nothing wrong with your code as posted, most likely you've got a namespace problem.
edit: gah, just noticed that you mentioned this was a website project. It's been a while since I deigned to start one of those :) but I believe this stems from the fact that App_Code is run-time compiled. It would take a better man than me to explain why that creates the problem, but long story short I'd just avoid website projects in general.
You can do this, afaik:
System.Web.UI.Page page = System.Web.HttpContext.Current.Handler as System.Web.UI.Page;
Be careful where you call that though, because obviously at different points in your code the page won't be available. For example, Application_Start in global.asax.