I have an ASP.Net application and I've noticed through using profilers that there is a sizable amount of processing that happens before my page even runs. In my application, we don't used viewstate, asp.Net session, and we probably don't require most of the overhead that comes of a consequence of using the asp.net page lifecycle. Is there some other class I can easily inherit from that will cut out all the Asp.Net stuff, and just let my handle writing to the page by myself?
I've heard that ASP.Net MVC can cut down page loads considerably, simply because it doesn't use the old asp.net lifecycle, and handles pages differently. Is there an easy way, maybe by simply having my web pages inherit some other class to take advantage of something like this. I would like a solution that works in ASP.Net 2.0 if at all possible.
Most articles I found where talking about using the Page as a base class and implementing functionality on top of that, it looks like you need to make your own MyPage class that implements IHttpHandler
From the MSDN article
using System.Web;
namespace HandlerExample
{
public class MyHttpHandler : IHttpHandler
{
// Override the ProcessRequest method.
public void ProcessRequest(HttpContext context)
{
context.Response.Write("This is an HttpHandler Test.");
context.Response.Write("Your Browser:");
context.Response.Write("Type: " + context.Request.Browser.Type + "");
context.Response.Write("Version: " + context.Request.Browser.Version);
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
}
}
Again, from the article: To use this handler, include the following lines in a Web.config file.
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="handler.aspx" type="HandlerExample.MyHttpHandler,HandlerTest"/>
</httpHandlers>
</system.web>
</configuration>
I'd take a look at the source code for System.web.ui.page and take a look at what it does to guide you. My guess is that it mostly just calls the different methods in the asp.net page lifecycle in the right order. You'd do something similar by calling your own page_load from the ProcessRequest method. That would route to your individual implementation of your classes that implement your MyPage.
I've never thought of doing something like this before and it sounds pretty good since I really don't use any of the bloated webforms functionality. MVC may make this whole exercise futile, but it does seem pretty neat.
My Quick Example
new base:
using System.Web;
namespace HandlerExample
{
// Replacement System.Web.UI.Page class
public abstract class MyHttpHandler : IHttpHandler
{
// Override the ProcessRequest method.
public void ProcessRequest(HttpContext context)
{
// Call any lifecycle methods that you feel like
this.MyPageStart(context);
this.MyPageEnd(context);
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
// define any lifecycle methods that you feel like
public abstract void MyPageStart(HttpContext context);
public abstract void MyPageEnd(HttpContext context);
}
Implementation for the page:
// Individual implementation, akin to Form1 / Page1
public class MyIndividualAspPage : MyHttpHandler
{
public override void MyPageStart(HttpContext context)
{
// stuff here, gets called auto-magically
}
public override void MyPageEnd(HttpContext context)
{
// stuff here, gets called auto-magically
}
}
}
If you don't need all this "asp.net stuff", you may wish to implement a custom IHttpHandler. Afaik, there are no other standard IHttpHandlers to reuse except the Page class.
To do this you should start by looking at the System.Web.UI.PageHandlerFactory class and the corresponding System.Web.IHttpHandlerFactory interface.
From there you will probably look at the System.Web.IHttpHandler inferface and the System.Web.UI.Page class.
Basically you will write your own IHttpHandlerFactory that generates IHttpHandlers that handles the page requests.
Related
I need to do routing in an existing asp.net app - not an asp.net mvc (yeah I know I should convert but let's say it's not possible right now so don't tell me :) ) - how can I do routing to a normal class instead of an aspx page as all sample code I see is always with aspx page like here:
http://msdn.microsoft.com/en-us/magazine/dd347546.aspx
To precise, I want to do a bit like in MVC Controller routing : the controller for example product is a pure class you access through http://domain.com/product
ASP.NET MVC and ASP.NET Web Forms share the same routing infrastructure in that both frameworks ultimately need to come up with an IHttpHandler to handle the HTTP request:
The IHttpHandler interface has been a part of ASP.NET since the
beginning, and a Web Form (a System.Web.UI.Page) is an IHttpHandler.
(From the MSDN article linked in the question)
In ASP.NET MVC the System.Web.Mvc.MvcHandler class is used, which then delegates to a controller for further handling of the request. In ASP.NET Web Forms usually the System.Web.UI.Page class that represents an .aspx file is used, but a pure IHttpHandler associated with .ashx file can also be used.
So you can route to an .ashx handler as an alternative to an .aspx Web Forms page. Both implement IHttpHandler (as does MvcHandler), but with the former that's all it does. And that's as close as you can get to a 'pure class' handling a (routed) request. And since the handler part is just an interface, you are free to inherit from your own class.
<%# WebHandler Language="C#" Class="LightweightHandler" %>
using System.Web;
public class LightweightHandler : YourBaseClass, IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Hello world!");
}
public bool IsReusable { get { return false; } }
}
Notice that an IRouteHandler just needs to return an instance of IHttpHandler:
public IHttpHandler GetHttpHandler(RequestContext requestContext);
You may need to jump through some hoops to instantiate your handler using the BuildManager* if you use .ashx files. If not, you can just new up an instance of your class and return it:
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// In case of an .ashx file, otherwise just new up an instance of a class here
IHttpHandler handler =
BuildManager.CreateInstanceFromVirtualPath(path, typeof(IHttpHandler)) as IHttpHandler;
// Cast to your base class in order to make it work for you
YourBaseClass instance = handler as YourBaseClass;
instance.Setting = 42;
instance.DoWork();
// But return it as an IHttpHandler still, as it needs to do ProcessRequest
return handler;
}
See the answers to this question for much more in-depth analysis of routing pure IHttpHandlers: Can ASP.NET Routing be used to create “clean” URLs for .ashx (IHttpHander) handlers?
**I'm not entirely sure about the BuildManager example, someone please correct me if I got that part wrong*
If you can't switch to ASP.NET MVC and routing .ashx handlers doesn't meet your requirements, you may want to look into Nancy, a 'lightweight web framework'.
Here's an example from the introduction (see link in previous paragraph):
public class Module : NancyModule
{
public Module() : base("/foo")
{
Get["/"] = parameters => {
return "This is the site route";
};
Delete["/product/{id}"] = parameters => {
return string.Concat("You requested that the following product should be deleted: ", parameters.id);
};
}
}
This class will handle requests to /foo and /foo/product/42. You can also use views with this framework to render a more complex (HTML) response.
If you can update from 3.5 to 4.0, WebForms supports routing better. In Global.asax, you only need to do things like this:
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapPageRoute("default", string.Empty, "~/default.aspx");
}
I don't really understand the "pure class" part, but hopefully if updating to 4.0 is an option this can get you going.
I'm trying to get Session enabled in the GettHttpHandler method of my IRouteHandler classes but session is always null. Could someone tell me what I'm doing wrong?
In global.asax I have
RouteTable.Routes.Add("All", new Route("{*page}", new MyRouteHandler()));
The MyRouteHandler class where Session is null looks like this:
public class MyRouteHandler : IRouteHandler, IRequiresSessionState
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string test = HttpContext.Current.Session["test"].ToString();
return BuildManager.CreateInstanceFromVirtualPath("~/Page.aspx", typeof(Page)) as Page;
}
}
I made a small test app that shows the problem.
Could someone tell me what I'm doing wrong?
Edited to add:
Yes, I really need session data in the route handler. There are many reasons but one easily explainable is when the user can switch to browse the site in preview mode.
The site consists of a hierarchy of dynamic pages (/page1/page2...) in the database that can be published normally or to preview. A content producer browsing the site can choose to view just normal pages or also those published to preview. The browsing mode is stored in the user's session so therefor the route handler needs to know the browsing mode to be able to solve the requested page.
So I really need the session already at that stage.
I have explained reason behind this problem in this answer. And now I have found a solution to the problem!
You create a custom HttpHandler class:
class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
public MyRequestHandler RequestHandler;
public RequestContext Context;
public MyHttpHandler(MyRequestHandler routeHandler, RequestContext context)
{
RequestHandler = routeHandler;
Context = context;
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
public bool IsReusable
{
get { throw new NotImplementedException(); }
}
}
It is important to add IRequiresSessionState interface, otherwise IIS does not load session for this request. We do not need to implement logic of ProcessRequest and IsReusable, but class must implement the IHttpHandler interface.
You change your RouteHandler implementation:
public class MyRequestHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MyHttpHandler(this, requestContext);
}
public IHttpHandler DelayedGetHttpHandler(RequestContext requestContext)
{
// your custom routing logic comes here...
}
}
You simply move your original, Session dependent logic to DelayedGetHttpHandler function and in the GetHttphandler function you return an instance of the helping MyHttpHandler class.
Then, you hook your handling logic to the HttpApplication.PostAcquireRequestState event, e.g. in the Global.asax:
public class Global : HttpApplication
{
public override void Init()
{
base.Init();
PostAcquireRequestState += Global_PostAcquireRequestState;
}
}
For more reference, check this page: https://msdn.microsoft.com/en-us/library/bb470252(v=vs.140).aspx. It explains the request lifecycle and why I use the PostAcquireRequestState event.
In the event handler, you invoke your custom RouteHandling function:
void Global_PostAcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Handler is MyHttpHandler) {
var handler = HttpContext.Current.Handler as MyHttpHandler;
HttpContext.Current.Handler = handler.RouteHandler.DelayedGetHttpHandler(handler.Context);
}
}
And that's it. Works for me.
I am not sure that you can do this (although I may be wrong). My recollection is that IRequiresSessionState indicates that an HttpHandler needs session state, rather than the route handler (which is responsible for giving you a handler appropriate to the route).
Do you really need the session in the route handler itself and not the handler it gives out?
Well I know this is old thread but just putting up the answer here if anyone like me falls in the same scenario, I found an answer here
What you do is just add a runAllManagedModulesForAllRequests="true" attribute to your modules tag in web.config like below
<system.webServer>
.....
<modules runAllManagedModulesForAllRequests="true">
........
</modules>
......
</system.webServer>
However this is not a good solution as it calls managed module everytime, i am using
<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
add it in modules section of web.config, this a better solution than the previous one.
I recently read Phil Haack's post where he gives an example of implementing Model View Presenter for ASP.NET. One of the code snippets shows how the code for the view class.
public partial class _Default : System.Web.UI.Page, IPostEditView
{
PostEditController controller;
public _Default()
{
this.controller = new PostEditController(this, new BlogDataService());
}
}
However, here the view constructs the instance of the BlogDataService and passes it along to the presenter. Ideally the view should not know about BlogDataService or any of the presenter's lower layer dependencies. But i also prefer to keep the BlogDataService as a constructor injected dependency of the presenter as it makes the dependencies of the presenter explicit.
This same question has been asked on stackoverflow here.
One of the answers suggests using a service locator to get the instance of the BlogDataService and passing it along to the presenter's constructor.This solution however does not solve the problem of the view knowing about the BlogDataService and needing to explicitly get a reference to it.
Is there a way to automatically construct the presenter object using an IoC or DI container tool such that the view does not have to deal with explicitly creating the BlogDataService object and also injecting the view and service instances into the presenter's constructor. I prefer to use the constructor injection pattern as far as possible.
Or is there a better design available to solve the problem?. Can there be a better way to implement this If i am building a WinForms application instead of a ASP.NET WebForms application?
Thanks for any feedback.
Yes there is. For example using StructureMap in a webform constructor:
public partial class AttributeDetails : EntityDetailView<AttributeDetailPresenter>, IAttributeDetailView
{
public AttributeDetails()
{
_presenter = ObjectFactory.With<IAttributeDetailView>(this).GetInstance<AttributeDetailPresenter>();
}
....
}
and as you can see here presenter needs view and service injected
public AttributeDetailPresenter(IAttributeDetailView view, IAttributeService attributeService)
{
MyForm = view;
AppService = attributeService;
}
You can also use StructureMap BuildUp feature for webforms so that you can avoid using ObjectFactory directly in your view.
I did exactly this. The solution is based on Autofac, but can be implemented on top of any container.
First, define an interface representing the authority for presenting views in a request to the MVP system:
public interface IMvpRequest
{
void Present(object view);
}
Next, create a base page which has a property of that type:
public abstract class PageView : Page
{
public IMvpRequest MvpRequest { get; set; }
}
At this point, set up dependency injection for pages. Most containers have ASP.NET integration, usually in the form of HTTP modules. Because we don't create the page instance, we can't use constructor injection, and have to use property injection here only.
After that is set up, create event arguments representing a view which is ready to be presented:
public class PresentableEventArgs : EventArgs
{}
Now, catch the events in PageView and pass them to the request (present the page as well):
protected override bool OnBubbleEvent(object source, EventArgs args)
{
var cancel = false;
if(args is PresentableEventArgs)
{
cancel = true;
Present(source);
}
else
{
cancel = base.OnBubbleEvent(source, args);
}
return cancel;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Present(this);
}
private void Present(object view)
{
if(MvpRequest != null && view != null)
{
MvpRequest.Present(view);
}
}
Finally, create base classes for each type of control you'd like to serve as a view (master pages, composite controls, etc.):
public abstract class UserControlView : UserControl
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
RaiseBubbleEvent(this, new PresentableEventArgs());
}
}
This connects the control tree to the MVP system via IMvpRequest, which you'll now have to implement and register in the application-level container. The ASP.NET integration should take care of injecting the implementation into the page. This decouples the page entirely from presenter creation, relying on IMvpRequest to do the mapping.
The implementation of IMvpRequest will be container-specific. Presenters will be registered in the container like other types, meaning their constructors will automatically be resolved.
You will have some sort of map from view types to presenter types:
public interface IPresenterMap
{
Type GetPresenterType(Type viewType);
}
These are the types you will resolve from the container.
(The one gotcha here is that the view already exists, meaning the container doesn't create the instance or ever know about it. You will have to pass it in as a resolution parameter, another concept supported by most containers.)
A decent default mapping might look like this:
[Presenter(typeof(LogOnPresenter))]
public class LogOnPage : PageView, ILogOnView
{
// ...
}
I am trying to work out a way to use dependency injection with ASP.NET Web Forms controls.
I have got lots of controls that create repositories directly, and use those to access and bind to data etc.
I am looking for a pattern where I can pass repositories to the controls externally (IoC), so my controls remain unaware of how repositories are constructed and where they come from etc.
I would prefer not to have a dependency on the IoC container from my controls, therefore I just want to be able to construct the controls with constructor or property injection.
(And just to complicate things, these controls are being constructed and placed on the page by a CMS at runtime!)
Any thoughts?
UPDATE 2019:
With the introduction of Web Forms 4.7.2, there is now better support for DI. This invalidates the below. See: Wiring up Simple Injector in WebForms in .NET 4.7.2
You can use automatic constructor injection by replacing the default PageHandlerFactory with a custom one. This way you can use an overloaded constructor to load the dependencies. Your page might look like this:
public partial class HomePage : System.Web.UI.Page
{
private readonly IDependency dependency;
public HomePage(IDependency dependency)
{
this.dependency = dependency;
}
// Do note this protected ctor. You need it for this to work.
protected HomePage () { }
}
Configuring that custom PageHandlerFactory can be done in the web.config as follows:
<?xml version="1.0"?>
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.aspx"
type="YourApp.CustomPageHandlerFactory, YourApp"/>
</httpHandlers>
</system.web>
</configuration>
Your CustomPageHandlerFactory can look like this:
public class CustomPageHandlerFactory : PageHandlerFactory
{
private static object GetInstance(Type type)
{
// TODO: Get instance using your favorite DI library.
// for instance using the Common Service Locator:
return Microsoft.Practices.ServiceLocation
.ServiceLocator.Current.GetInstance(type);
}
public override IHttpHandler GetHandler(HttpContext cxt,
string type, string vPath, string path)
{
var page = base.GetHandler(cxt, type, vPath, path);
if (page != null)
{
// Magic happens here ;-)
InjectDependencies(page);
}
return page;
}
private static void InjectDependencies(object page)
{
Type pageType = page.GetType().BaseType;
var ctor = GetInjectableCtor(pageType);
if (ctor != null)
{
object[] arguments = (
from parameter in ctor.GetParameters()
select GetInstance(parameter.ParameterType)
.ToArray();
ctor.Invoke(page, arguments);
}
}
private static ConstructorInfo GetInjectableCtor(
Type type)
{
var overloadedPublicConstructors = (
from constructor in type.GetConstructors()
where constructor.GetParameters().Length > 0
select constructor).ToArray();
if (overloadedPublicConstructors.Length == 0)
{
return null;
}
if (overloadedPublicConstructors.Length == 1)
{
return overloadedPublicConstructors[0];
}
throw new Exception(string.Format(
"The type {0} has multiple public " +
"ctors and can't be initialized.", type));
}
}
Downside is that this only works when running your side in Full Trust. You can read more about it here. But do note that developing ASP.NET applications in partial trust seems a lost cause.
Starting from .NET 4.7.2 (what's new), it is now easy for developers to use Dependency Injection in WebForms applications. With the UnityAdapter, you can add it to your existing WebForms application in 4 simple steps. See this blog.
Autofac supports fairly unobtrusive dependency injection in ASP.NET WebForms. My understanding is it just hooks into the ASP.NET page lifecycle using an http module and does property injection. The only catch is that for controls I don't think this happens until after the Init event.
The best way is to have a base class for the controls like:
public class PartialView : UserControl
{
protected override void OnInit(System.EventArgs e)
{
ObjectFactory.BuildUp(this);
base.OnInit(e);
}
}
That will inject any control that inherits from that base class (uses structuremap). Combining that with a property based config, you will be able to have controls like:
public partial class AdminHeader : PartialView
{
IMyRepository Repository{get;set;}
}
Update 1: If you can't have the controls inherit, perhaps the CMS has a hook right after creating the controls, in there you can call the BuildUp. Also if the CMS allows you to hook something to fetch the instance you could use constructor based injection, but I prefer BuildUp on this specific scenario as asp.net doesn't have a hook for this.
You could also create some singleton instances in the Application_Start global.asax event and have them available as public static readonly properties.
This is a solution I recently used to avoid hooking into the pipeline (I find that confuses everyone that looks at my code in the future, but yes, I see its benefits as well):
public static class TemplateControlExtensions
{
static readonly PerRequestObjectManager perRequestObjectManager = new PerRequestObjectManager();
private static WIIIPDataContext GetDataContext(this TemplateControl templateControl)
{
var dataContext = (WIIIPDataContext) perRequestObjectManager.GetValue("DataContext");
if (dataContext == null)
{
dataContext = new WIIIPDataContext();
perRequestObjectManager.SetValue("DataContext", dataContext);
}
return dataContext;
}
public static IMailer GetMailer(this TemplateControl templateControl)
{
return (IMailer)IoC.Container.Resolve(typeof(IMailer));
}
public static T Query<T>(this TemplateControl templateControl, Query<T> query)
{
query.DataContext = GetDataContext(templateControl);
return query.GetQuery();
}
public static void ExecuteCommand(this TemplateControl templateControl, Command command)
{
command.DataContext = GetDataContext(templateControl);
command.Execute();
}
private class PerRequestObjectManager
{
public object GetValue(string key)
{
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(key))
return HttpContext.Current.Items[key];
else
return null;
}
public void SetValue(string key, object newValue)
{
if (HttpContext.Current != null)
HttpContext.Current.Items[key] = newValue;
}
}
}
This shows how you can create your own life time manager pretty easily as well as hook into an IoC container if you so desire. Oh, and I am also using a query/command structure which is sort of unrelated, but more on the reasoning behind that can be found here:
Limit your abstractions: Refactoring toward reduced abstractions
During practice of customizing VirtualPathProvider, I found that it the custom VirtualPathProvider can be registered in Global.asax or in AppInitialize method according to MSDN http://msdn.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx. However, MSDN doesn't clearly describe the method AppInitialize.
Does any static AppInitialize method in App_code folder will be automatically invoked by ASP.NET runtime at start up?
While there is precious little documentation about the AppInitialize() method, you are correct in your assumption that any class in your App_Code folder that contains a method signature like this:
public static void AppInitialize()
will be invoked when the Asp.Net application starts up. Remember that App_Code is a special folder to Asp.Net and everything inside there is treated a little differently. Good luck finding documentation on all the little quirks (like the aforementioned) of the App_Code folder.
Another thing to remember however is that only one class can contain a signature for the AppInitialize() method or else you will get a compiler error at runtime similar to this:
The AppInitialize method is defined
both in 'App_Code.SomeClassOne' and in
'App_Code.SomeClassTwo'.
So while this is perfectly valid:
public class SomeClassOne
{
public static void AppInitialize()
{
HostingEnvironment.Cache["InitializationTimeOne"] = DateTime.Now;
}
}
This will generate the compiler error I mentioned above:
public class SomeClassOne
{
public static void AppInitialize()
{
HostingEnvironment.Cache["InitializationTimeOne"] = DateTime.Now;
}
}
public class SomeClassTwo
{
public static void AppInitialize()
{
HostingEnvironment.Cache["InitializationTimeTwo"] = DateTime.Now;
}
}
I hope this clears things up a bit for you :)