Ninject OnePerRequestBehaviour doesn't seem to work correctly? - c#

I've been using Ninject as my IOC in my web app. It's great and I think it works really well, however I have been trying to register some interfaces / classes as OnePerRequestBehaviour but it doesn't seem to actually use the behaviour. The code runs correctly but in one of my classes it lazy loads page information from the database then once it has been loaded it doesn't need to hit the database.
My problem is that the lazily loaded property will load in my first request, when I then request the next page the same instance of the class is used. The reason I know this is because the class is not instantiated again and the lazily loaded property is already set.
This code is within my module class :
public class NinjectModule : StandardModule
{
public override void Load()
{
Bind<IUnitOfWorkDataStore>().To<HttpContextDataStore>().Using<OnePerRequestBehavior>();
Bind<CmsService>().ToSelf().Using<OnePerRequestBehavior>();
Bind<CmsRepository>().ToSelf().Using<OnePerRequestBehavior>();
}
}
Then inside my Global.asax which inherits from NinjectHttpApplication I have the following:
protected override IKernel CreateKernel()
{
OnePerRequestModule module = new OnePerRequestModule();
module.Init(this);
KernelOptions options = new KernelOptions();
options.InjectNonPublicMembers = true;
IKernel kernel = new StandardKernel(options, new NinjectModule());
return kernel;
}
The first call made to CmsService is within the global.asax as well on authenticate_request:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.Url.AbsoluteUri.Contains(".aspx") &&
!HttpContext.Current.Request.Url.AbsoluteUri.Contains(".aspx/"))
{
CmsService facCMS = HttpKernelFactory.Get<CmsService>();
ContentPage page = facCMS.GetCurrentPage();
// DO Logic based on the page being brought back
}
}
The above GetCurrentPage() code:
public ContentPage GetCurrentPage()
{
if (_currentPage != null)
return _currentPage;
return GetCurrentPage(_isAdmin);
}
So as you can see the _currentPage variable is only loaded if it hasn't been set before, which should be once per request, however Ninject doesn't seem to be creating the CmsService per request it seems to create it for an abritrary amount of time.
Deos anyone have any idea of why this isn't working for me or any example code of where it definately does work?
Thanks

The OnePerRequestModule is an HttpModule, and needs to be loaded into your ASP.NET pipeline in order to work. If you add it to your web.config, it should work:
IIS7:
<system.webServer>
<modules>
<add name="OnePerRequestModule" type="Ninject.Core.Behavior.OnePerRequestModule, Ninject.Core"/>
</modules>
</system.webServer>
IIS6:
<system.web>
<httpModules>
<add name="OnePerRequestModule" type="Ninject.Core.Behavior.OnePerRequestModule, Ninject.Core"/>
</httpModules>
</system.web>
The OnePerRequest behavior is greatly improved in Ninject2 (which is yet to be released).

Related

How to create a rule in a web.config file for a C# application, to block based on parameters in the URL

I want to create filter rule in the web.config file for a C# application, to block http://website.com/folder/Default.aspx,
but allow http://website.com/folder/Default.aspx?db=Database. Basically they don't have "Default.aspx?db=Database" in the URL, then it will either redirect them to a website another website (or Deny access).
You could implement an HttpModule that performs your desired logic. You can register it in the web.config without any code changes to the project. Just compile it into its own dll and drop that into the bin folder of your website.
The module
class RequestInterceptor : IHttpModule
{
public void Dispose()
{
}
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source,
EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
//Inspect the URL and decide if this is a request you are interested in
//context.Request.Url
//context.Request.QueryString
//Redirect, or whatever...
//context.Response.Redirect(...)
}
}
The web.config:
<system.webServer>
<modules>
<add name="MyInterceptor" type="YourNamespace.RequestInterceptor, RequestInterceptor"/>
</modules>
...

ClaimsAuthenticationManager.Authenticate never gets called

I want to add some additional claims to a Principal during authentication. I am trying to implement a custom ClaimsAuthenticationManager in my MVC 4.5 project which uses Windows Authentication:
namespace Project.Infrastructure
{
public class ClaimsTransformer : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
{
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "Admin"));
}
return incomingPrincipal;
}
}
}
I have the web.config set up to use my custom class:
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</configSections>
and
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="Project.Infrastructure.ClaimsTransformer, [AssemblyName]" />
</identityConfiguration>
</system.identityModel>
But the Authenticate method never gets called. Am I missing something?
The missing step is that you need to add an HTTP Module to kick all of this off.
So, you need a class that looks like this:
public class MyClaimsAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += Context_PostAuthenticateRequest;
}
public void Dispose()
{
// Nothing to dispose, method required by IHttpModule
}
void Context_PostAuthenticateRequest(object sender, EventArgs e)
{
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
if (transformer != null)
{
var context = ((HttpApplication)sender).Context;
var principal = context.User as ClaimsPrincipal;
var transformedPrincipal = transformer.Authenticate(context.Request.RawUrl, principal);
context.User = transformedPrincipal;
Thread.CurrentPrincipal = transformedPrincipal;
}
}
}
This will pick up the transformer you specified in the web.config and call authenticate on it, then attach the returned principal to the HttpContext and the current thread.
You will also need something like the following in your web.config:
<system.webServer>
<modules>
<add name="MyClaimsAuthenticationModule" type="MyApplication.MyHttpModels.MyClaimsAuthenticationModule, MyApplicationAssembly" />
</modules>
</system.webServer>
Update
You can, of course, put the code from the method Context_PostAuthenticationRequest in the PostAuthenticateRequest handler in your Global.asax.cs class file. However, I prefer to keep the responsibilities of the classes small so I go for an implementation of IHttpModule so that the module does its thing and it is obvious what that is, and it is separate from other things that may be happening at various stages of the pipeline.
If your Global.asax.cs file is small then there is no problem with putting the code there. It should still work. However, you are mixing responsibilities in the class and it could get unwieldy in the future.
Are you calling the Authenticate method with something like this, and the Authentication is not taking place?
ClaimsTransformer manager = new ClaimsTransformer();
manager.Authenticate("resource", incomingPrincipal )
You may want to replace the "return incomingPrincipal" from inside the ClaimsTransformer class with a call to:
return base.Authenticate(resourceName, incomingPrincipal);
Also why do you need Windows Authentication?

asp.net HTTPHandler prevents page from loading

I have an ASP:NET web project with a simple HTTPHandler which filters out requests from outside IPs.
The code itself works, but the HTTPHandler prevents my page from loading. No error. No infinite load. There's just a blank page.
If I remove the reference in the config, it loads perfectly fine. It's definitely caused by the HTTPHandler. I've also stepped through the handler and the code is definitely reached, it's just that when the handler is done, the page doesn't load like it should.
Here is the code.
public class SecurityHttpHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
string ipAddress = context.Request.UserHostAddress;
if (!IsValidIpAddress(ipAddress))
{
context.Response.StatusCode = 403;
}
}
private bool IsValidIpAddress(string ipAddress)
{
return true; //for the time being, this will always return true
}
public void Dispose() { } //clean
}
The handler exists in another project (i have a reference to the assembly) and the httphandler is registered in my webprojects web.config as such:
<handlers>
<add name="SecurityHttpHandler" verb="*"
path="*Default.aspx"
type="MyProjects.CommonResources.Web.SecurityHttpHandler"
resourceType="Unspecified" />
</handlers>
I'm running IIS 7.5 in integrated mode. .net framework 4.0.
Let me know if I should add more code. I excluded the code from my web-project as the handler itself seems to be the cause early in the asp.net pipeline.

How to inject dependencies in an HttpModule with a NinjectHttpApplication (no nuget)?

I have a .Net MVC 3.0 application and I'm using Ninject 3.0. I didn't install any nuget. I'm referencing Ninject.dll, Ninject.Web.Common.dll and Ninject.Web.Mvc.dll (and 2 others). I want to have dependencies injected in a custom HttpModule and I can't figure out how to make it work with a NinjectHttpApplication.
I have this error:
Error activating IntPtr
No matching bindings are available, and the type is not self-bindable.
Activation path:
3) Injection of dependency IntPtr into parameter method of constructor of type Func{IKernel}
2) Injection of dependency Func{IKernel} into parameter lazyKernel of constructor of type HttpApplicationInitializationHttpModule
1) Request for IHttpModule
Here is the code:
Global.asax
public class MvcApplication: NinjectHttpApplication
{
...
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new ServiceModule()
};
IKernel kernel = new StandardKernel(modules);
return kernel;
}
}
Web.Config
<httpModules>
<add name="NinjectHttpModule" type="Ninject.Web.Common.NinjectHttpModule"/>
</httpModules>
CustomHttpModule.cs
public class CustomHttpModule : IHttpModule
{
private ITesttService service;
public CustomHttpModule(ITesttService service)
{
this.service = service;
}
...
}
ServiceModule.cs
public class ServiceModule : NinjectModule
{
public override void Load()
{
...
Kernel.Bind<ITestService>().To<TestService>();
Kernel.Bind<IHttpModule>().To<CustomHttpModule>().InSingletonScope();
}
}
This binding solves my problem:
kernel.Bind<Func<IKernel>>().ToMethod(c => () => this.Kernel);
But according to this post on github, I'm not supposed to do it.
Can you someone tell me what I'm doing wrong or missing?
Currently there is no good way to use the NinjectHttpModule when deriving from NinjectHttpApplication. The bootstrapper registers the HttpApplicationInitializationHttpModule for both ways and as soon as the NinjectHttpModule is loaded this module is loaded as well.
Unfortunately there is no good point where you can unload it.
I suggest you use the WebActivator instead on deriving from NinjectHttpApplication. It's the only proper way to get it running. You don't necessarily have to use nuget to setup your application that way. You can also add the same files manually and manually reference all required assemblies.

Ninject + ASP.NET Web Forms Not Working

I've successfully implemented Ninject in an MVC3 application, but am running into some trouble doing the same thing with ASP.NET Web Forms. I'm getting null references every time I try to access an injected property in my business layer. After setting breakpoints within the CreateKernel method, as well as several places within the ServiceLocator class, it looks like none of them are ever getting hit, so it's not even loading.
I'm sure I'm just approaching this wrong, but there is very little documentation or info out there for wiring up Ninject in a Web Forms application.
Basically here's what I have so far:
code behind
public class ReviewManager
{
[Inject] private IReviewRepository _reviewRepository { get; set; }
public ReviewManager() { }
public ReviewManager(IReviewRepository reviewRepository)
{
_reviewRepository = reviewRepository;
}
public Review GetById(int id)
{
if (id <= 0) throw new ArgumentException("ID must be greater than zero");
**I get a null reference exception on the next line. _reviewRepository is null**
return _reviewRepository.GetById(id);
}
}
global.asax.cs
public class Global : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
return ServiceLocator.Kernel;
}
// deleted for brevity
}
ServiceLocator.cs (edited for brevity, the relevant parts are here)
public static class ServiceLocator
{
public static IKernel Kernel { get; set; }
public static ILogger Logger { get; set; }
static ServiceLocator()
{
Kernel = new StandardKernel(new INinjectModule[] {
new LoggerBindings(),
new DataBindings()
});
if (Logger == null)
Logger = Kernel.Get<ILogger>();
}
}
public class LoggerBindings : NinjectModule
{
public override void Load()
{
Bind<ILogger>().To<NLogLogger>();
}
}
public class DataBindings : NinjectModule
{
public override void Load()
{
Bind<IReviewRepository>().To<ReviewRepository>();
}
}
ASP.Net via WebForms does not allow you to manage the lifecycle of all object instances (like MVC does). For example, the framework instantiates page objects. This means you probably can't implement DI in quite the same way as you would in MVC/WPF/Silverlight (the same problem is present in WinForms IIRC). You will likely have to initiate the dependency graph directly in each of your code behinds.
Translation: you will want to call ServiceLocator.Kernel.Get<IReviewRepository> when your page loads (or as lazy-init on the property).
The cool thing about MVC ist that it can run side a side of ASP.NET WebForm pages in the same application. In my opinion the best way to extend ASP.NET WebForms websites is to create new pages using MVC3 and to refactor every page that needs major changes to MVC3.
If this is no option go and use the Ninject.Web extension. It contains a IHttpModule that property injects all web pages and controlls after they are initialized. That way you can property inject the services als have them created by Ninject.
A potential workaround, by changing your DataBindings class as follows:
public class DataBindings : NinjectModule
{
public override void Load()
{
Bind<IReviewRepository>().To<ReviewRepository>();
Bind<ReviewManager>().ToSelf();
}
}
And within your caller, instead of
var rm = new ReviewManager();
Try using
var rm = ServiceLocator.Kernel.Get<ReviewManager>();
I havent tested this code, but i think it'll solve your null reference problem.
I use property injection for pages, masterpages and usercontrols. All my pages, for example, inherit from a base class that overrides RequestActivation method with the following code:
''' <summary>
''' Asks the kernel to inject this instance.
''' </summary>
Protected Overridable Sub RequestActivation()
ServiceLocator.Kernel.Inject(Me)
End Sub
And in each page I declare injectable properties:
<Inject()>
Property repo As IMyRepository

Categories