BasicAuthHttpModule custom property resolve at runtime - c#

I have a problem with ASP .NET Web API. I have followed the tutorial http://www.asp.net/web-api/overview/security/basic-authentication and I have succesfully used it with sample code. However I would like to bind it with my AuthenticationService that I have made:
public class AuthenticationService : IAuthenticationService
{
private readonly GenericRepository<User> _userRepository;
public AuthenticationService(GluEntities entites)
{
_userRepository = new GenericRepository<User>(entites);
}
public bool Authenticate(string userName, string password)
{
var user = _userRepository.Get(x => x.UserName == userName).FirstOrDefault();
if (user == null || user.Password != password)
{
return false;
}
return true;
}
}
i tried to tie it up with my sample code as follows:
public class BasicAuthHttpModule : IHttpModule
{
private const string Realm = "GluService";
private readonly IAuthenticationService _authenticationService;
public BasicAuthHttpModule(IAuthenticationService authenticationService)
{
_authenticationService = authenticationService;
}
/... some irrelevant methods that are explained in the link
private bool CheckPassword(string username, string password)
{
return _authenticationService.Authenticate(username, password);
}
}
I am trying to resolve _authenticationService with ninject however the application throws Conustructor not found error at runtime. Any idea how can I resole _authenticationService at runtime so I can achieve maintainability and testability?

Http modules are unlikely to be resolved by an ioc container, however, if you refactor your code to use an authentication filter rather than http module, you should be able to set up the mvc internal service locator to resolve your filter.
Take a look at this tutorial by Rick Strahl
http://www.west-wind.com/weblog/posts/2013/Apr/18/A-WebAPI-Basic-Authentication-Authorization-Filter
and the IFilterProvider example
Property Injection into Web API's `System.Web.Http.Filters.ActionFilterAttribute`

Thanks to Wiktor I was able to glue together a code that actually works. Here is relevant code for future help. Other classes are in Wiktors' links.
public class NinjectFilterProvider : ActionDescriptorFilterProvider, IFilterProvider
{
private readonly IKernel _kernel;
public NinjectFilterProvider(IKernel kernel)
{
_kernel = kernel;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(configuration, actionDescriptor);
foreach (var filter in filters)
{
_kernel.Inject(filter.Instance);
}
return filters;
}
}
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
//Support WebApi
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
//kernel.Bind<TCountingRepository>().To<CountingRepository>();
//kernel.Bind<CountingContext>().To<CountinContext>();
kernel.Bind<IAuthenticationService>().To<AlwaysAcceptAuthenticationService>();
kernel.Bind<IFilterProvider>().To<NinjectFilterProvider>();
}
}
Here is filter
public class GluAuthenticationFilter : BasicAuthenticationFilter
{
[Inject]
public IAuthenticationService AuthenticationService { get; set; }
public GluAuthenticationFilter()
{ }
public GluAuthenticationFilter(bool active)
: base(active)
{ }
protected override bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
{
return AuthenticationService.Authenticate(username, password);
}
}

Related

Why might similar .NET Core services behave differently?

I have two .net core 2.2 services, InitialisationHandler and WorkingTicketHandler, one holds initial information and the other is intended to hold a working ticket.
The InitialisationHandler always has its information ready when asked, but the WorkingTicketHandler always returns a null record.
I need to figure out how to have WorkingTicketHandler hold on to its data just like InitialisationHandler does.
Both are added to as transient in ConfigureServices, and created as identically as possible.
There is one key difference, the InitialisationHandler has its data created during startup by its constructor method and is only ever read from thereafter, whereas the WorkingTicketHandler is written to by a controller and re read in the same controller (Get vs Post).
using VTModels.Other;
namespace VT_Online22.Infrastructure.Services
{
/// <summary>
/// Interface for the WorkingTicket Service
/// </summary>
public interface IWorkingTicketHandler
{
WorkingTicket GetWorkingTicket();
void SaveWorkingTicket(WorkingTicket argWorkingTicket);
void ClearWorkingTicket();
}
/// <summary>
/// WorkingTicket service class definition
/// </summary>
public class WorkingTicketHandler : IWorkingTicketHandler
{
public WorkingTicketHandler()
{
this.WorkingTicket = new WorkingTicket();
}
public WorkingTicket GetWorkingTicket()
{
return this.WorkingTicket;
}
public void SaveWorkingTicket(WorkingTicket arg_WorkingTicket)
{
this.WorkingTicket = arg_WorkingTicket;
}
public void ClearWorkingTicket()
{
this.WorkingTicket = new WorkingTicket();
}
private WorkingTicket WorkingTicket;
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(typeof(WorkingTicketHandler));
}
}
using Microsoft.Extensions.Configuration;
using SharedModels.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
using VT_OnlineCore.Infrastructure.Other;
using VTModels.Models;
namespace VT_Online22.Infrastructure.Services
{
/// <summary>
/// Interface for the Initialization Service
/// </summary>
public interface IInitializationHandler
{
void Initialize();
CConfigMasterBase GetConfig();
IEnumerable<CScopeMasterBase> GetScopes(string clientId);
}
/// <summary>
/// Initialization service class definition
/// </summary>
public class InitializationHandler : IInitializationHandler
{
public InitializationHandler(IConfiguration configTech)
{
ConfigurationTech = configTech;
Initialize();
}
public void Initialize()
{
this.ldh = new LocalDataHandler(logger, this.ConfigurationTech);
this.config = new CConfigMasterBase();
LoadConfig();
}
public IEnumerable<CScopeMasterBase> GetScopes(string clientId)
{
IEnumerable<CScopeMasterBase> scopes = new List<CScopeMasterBase>();
scopes = this.ldh.GetScopes(clientId);
return scopes;
}
public CConfigMasterBase GetConfig()
{
return this.config;
}
private async Task LoadConfig()
{
this.config = this.ldh.GetConfig();
}
private LocalDataHandler ldh;
private CConfigMasterBase config;
private IEnumerable<CScopeMasterBase> scopes;
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(typeof(InitializationHandler));
public IConfiguration ConfigurationTech { get; private set; }
}
}
services.AddTransient<IInitializationHandler,InitializationHandler>();
services.AddTransient<IWorkingTicketHandler, WorkingTicketHandler>();

An entity object cannot be referenced by multiple instances of IEntityChangeTracker when saving to the database

I'm trying to save my objects to my local database. We are using Microsoft Entity Framework and Ninject. Currently when I try to save my object I get the 'An entity object cannot be referenced by multiple instances of IEntityChangeTracker.' error. We are able to get data from the database currently, it is saving data that is causing problems.
The error is given the moment the savetickets function is called, Visual studio displays the error in the repository at context.Tickets.add(ticket). I have tried passing a single object instead of a list and saving that, this has however caused the same error.
We apply the same principle of creating repositories in other controllers as well for getting data from the database before we get to this part of the code where I'm trying to save tickets.
We have added a Ninject binding
kernel.Bind<ITicketRepository>().To<TicketRepository>();
Our EFDBContext looks like this
public class EFDbContext : DbContext
{
public DbSet<Ticket> Tickets { get; set; }
// Other Entities
public EFDbContext() : base("EFDbContext"){
}
}
The interface for the ticketrepository looks like this
public interface ITicketRepository
{
// Other functions
void SaveTickets(List<Ticket> tickets);
}
The repository implementing this interface looks like this
public class TicketRepository : ITicketRepository
{
private EFDbContext context = new EFDbContext();
// Other functions
public void SaveTickets(List<Ticket> tickets)
{
foreach (var item in tickets)
{
context.Tickets.Add(item);
}
context.SaveChanges();
}
}
The moment all of this is called is in my printtickets function. This is the only time the ticketrepository is created and called for in the code.
public class ReservationController : Controller
{
// Other repositories
private ITicketRepository ticketRepository;
public ReservationController(IMovieOverviewRepository movieRepository, IShowRepository showRepository, ITicketRepository ticketRepository)
{
// Other repositories
this.ticketRepository = ticketRepository;
}
// Other code
[HttpGet]
public ActionResult PrintTickets()
{
List<Ticket> tickets = (List<Ticket>)TempData["TicketList"];
ticketRepository.SaveTickets(tickets);
// Irrelevant stuff
}
}
EDIT: Our full ninject configuration. NinjectWebCommon.cs
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
System.Web.Mvc.DependencyResolver.SetResolver(new Sprint123.WebUI.Infrastructure.NinjectDependencyResolver(kernel));
}
}
And NinjectDependencyResolver.cs
namespace Sprint123.WebUI.Infrastructure
{
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernelParam)
{
kernel = kernelParam;
AddBindings();
}
public object GetService(Type serviceType)
{
return kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return kernel.GetAll(serviceType);
}
private void AddBindings()
{
kernel.Bind<IShowRepository>().To<ShowRepository>();
kernel.Bind<ITicketRepository>().To<TicketRepository>();
kernel.Bind<IMovieOverviewRepository>().To<MovieOverviewRepository>();
}
}
}

Asp.net membership provider using ninject - call Initialize method

I am trying to implement a custom membership provider in an asp.net mvc 4 web application using ninject for dependency injection. Here is the code I have until now.
public interface IAccountRepository
{
void Initialize(string name, NameValueCollection config);
string ApplicationName { get; set; }
bool ChangePassword(string username, string oldPassword, string newPassword);
bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion,
string newPasswordAnswer);
MembershipUser CreateUser(string username, string password, string email, string passwordQuestion,
string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status);
bool DeleteUser(string username, bool deleteAllRelatedData);
bool EnablePasswordReset { get; }
bool EnablePasswordRetrieval { get; }
MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize,
out int totalRecords);
/* I have deleted here all other methods and properties of membership for brevity */
}
.
public class AccountRepository : IAccountRepository
{
private string applicationName;
private bool enablePasswordReset;
private bool enablePasswordRetrieval;
private int maxInvalidPasswordAttempts;
private int minRequiredNonAlphanumericCharacters;
private int passwordAttemptWindow;
private MembershipPasswordFormat passwordFormat;
private string passwordStrengthRegularExpression;
private bool requiresQuestionAndAnswer;
private bool requiresUniqueEmail;
private int minRequiredPasswordLength;
public void Initialize(string name, NameValueCollection config)
{
applicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
maxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
passwordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
minRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "6"));
enablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
passwordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
}
public string ApplicationName
{
get { return applicationName; }
set { applicationName = value; }
}
public bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
}
public bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion,
string newPasswordAnswer)
{
throw new NotImplementedException();
}
public MembershipUser CreateUser(string username, string password, string email, string passwordQuestion,
string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
throw new NotImplementedException();
}
public bool DeleteUser(string username, bool deleteAllRelatedData)
{
using (var database = new KinematDbContext())
{
// Query to get the user with the specified username
User user = database.Users.SingleOrDefault(u => u.Username == username);
if (user != null)
{
if (deleteAllRelatedData)
{
database.Users.Remove(user);
}
else
{
user.IsDeleted = true;
}
database.SaveChanges();
return true;
}
return false;
}
}
public bool EnablePasswordReset
{
get { return enablePasswordReset; }
}
public bool EnablePasswordRetrieval
{
get { return enablePasswordRetrieval; }
}
public MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize,
out int totalRecords)
{
throw new NotImplementedException();
}
public MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize,
out int totalRecords)
{
throw new NotImplementedException();
}
/* I have deleted here all other methods and properties of membership for brevity */
}
.
public class AccountMembershipProvider : MembershipProvider
{
[Inject]
public IAccountRepository AccountRepository { get; set; }
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
AccountRepository.Initialize(name, config);
/* Here comes the error: Object reference not set to an instance of an object. */
}
public override string ApplicationName
{
get { return AccountRepository.ApplicationName; }
set { AccountRepository.ApplicationName = value; }
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
return AccountRepository.ChangePassword(username, oldPassword, newPassword);
}
}
and this is my ninject controller factory(I also have set the controller factory in Application_Start())
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<IAccountRepository>().To<AccountRepository>();
ninjectKernel.Bind<IRoleRepository>().To<RoleRepository>();
ninjectKernel.Inject(Membership.Provider);
ninjectKernel.Inject(Roles.Provider);
}
}
as I mention in a comment in the AccountMembershipProvider class when the AccountRepository.Initialize(name, config); is called I receive the following error: Object reference not set to an instance of an object. After debugging the application and read articles about how ninject works I cannot figure out what the problem is about. Please, can you give any explanation? Thank you.
I've run into a similar problem using a Custom Membership Provider. If you want to call the initialize method in the AccountRepository you could do the following:
Configure your DI with ninject in App_Start using the following (available via nuget):
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(() => CreateKernel());
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
//add your bindings here
kernel.Bind<IAccountRepository>().To<AccountRepository>();
kernel.Bind<MembershipProvider>().To<AccountMembershipProvider>().InRequestScope();
kernel.Bind<RoleProvider>().To<AccountRoleProvider>().InRequestScope(); //In case you have a custom Role Provider.
}
}
Then in your custom provider:
public class AccountMembershipProvider : MembershipProvider
{
private readonly IAccountRepository _repository;
public AccountMembershipProvider()
{
_repository = ServiceLocator.Current.GetInstance<IAccountRepository>();
_repository.Initialize();
}
public override bool ValidateUser(string username, string password)
{
return _repository.IsValidLogin(username, password);
}
...//Other methods
}
Hope this helps,
Try to do the initialization for registering instances in Global.asax.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// TODO register your binding here
...
}
}
Not the answer you're looking for, but I tried to do this myself a while ago and while I did manage to find a way to make it work, I found the solution to be too complex and brittle for production use.
I simply gave up on the idea of using Injection with my custom provider, because of the way Membership creates the provider. It's just not worth the headaches in my opinion.
I also think using Injection breaks the whole concept of the Membership provider, since the idea is that providers are supposed to be pluggable, and you can replace one with another without making any changes to your code. That's simply not possible to do if you have to have configuration code in your app to configure the database context..
Ok, you might argue that you won't be changing the provider.. in which case, Why bother with the provider at all then? Why not just implement a custom IIdentity and IPrincipal implementations.
How about adding the following line into your AddBindings() method
kernel.Bind<AccountMembershipProvider>().ToMethod(ctx => Membership.Provider);
I used ninject and custom membership provider with mvc3
Not sure if it helps you but you can compare yours with mine.
[assembly: WebActivator.PreApplicationStartMethod(typeof(VBooks.Web.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(VBooks.Web.App_Start.NinjectWebCommon), "Stop")]
namespace VBooks.Web.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public class ProviderInitializationHttpModule : IHttpModule
{
public ProviderInitializationHttpModule(MembershipProvider membershipProvider) { }
public void Init(HttpApplication context) { }
void IHttpModule.Dispose()
{
}
}
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
//
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IRegisterService>().To<RegisterService>();
kernel.Bind<IEmailService>().To<EmailService>();
kernel.Bind<IAccountService>().To<AccountService>();
kernel.Bind<ICoverService>().To<CoverService>();
kernel.Bind<IAdminLogService>().To<AdminLogService>();
kernel.Bind<MembershipProvider>().ToMethod(ctx => Membership.Provider);
kernel.Bind<IHttpModule>().To<ProviderInitializationHttpModule>();
// Add data and infrastructure modules
var modules = new List<INinjectModule>
{
new RepositoryModule()
};
kernel.Load(modules);
}
}
}
Instead of implementing DefaultControllerFactory you can implement IDependencyResolver:
public class NinjectDependencyResolver : IDependencyResolver
{
readonly IKernel _kernel;
public NinjectDependencyResolver()
{
_kernel = new StandardKernel();
AddBindings();
}
public object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType);
}
void AddBindings()
{
// Remember to add bindings here.
_kernel.Bind<IAccountRepository>().To<EFAccountRepository>();
}
}
Then in global.asax.cs instead of setting ControllerFactory you can set DependencyResolver:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
DependencyResolver.SetResolver(new NinjectDependencyResolver()); // Here.
}
}
Then in your implementation of MembershipProvider ask current DependencyResolver(Ninject) to create you an instance of, in this case, IAccountRepository:
public class CustomMembershipProvider : MembershipProvider
{
private readonly IAccountRepository _repository;
public OpenTibiaMembershipProvider()
{
_repository = (IAccountRepository)DependencyResolver.Current.GetService(typeof(IAccountRepository));
}
// Rest of implementation.
}
Hope this helps.

Singleton Scope binding not working as intended

I am using the ninject mvc3 plugin with my web api application. I have a binding that looks like:
kernel.Bind<IFoo>().To<Foo>().InSingletonScope();
It is my interpretation that the kernal will create exactly one instance of Foo and reuse it appropriately. By putting a breakpoint in Foo's constructor, I can clearly see that it is getting called once per request, and I cannot explain why.
My only guess is that somehow a new kernel is getting created per request, but that doesn't appear to be the case, as the CreateKernel method which sets the global dependency resolver is only getting run once in the application lifetime.
I am using some code taken from this post to make ninject play nice with mvc 4. Because of framework changes, I had to make an additional wrapper that I assign to GlobalConfiguration.Configuration.DependencyResolver:
public class NinjectResolver : NinjectScope, IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectResolver(IKernel kernel)
: base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectScope(_kernel.BeginBlock());
}
}
What am I doing wrong?
I never could get it to work properly, and I am not sure why. My guess is it has something to do with MVC4 integration being a bit immature at the moment.
As an alternative I am using:
kernel.Bind<IFoo>().ToConstant(new Foo());
This seems to work, but I am not too happy with it.
Like previously mentioned it does look like a bug.
One option is to simply implement a singleton extension method yourself:
public static class NinjectSingletonExtension
{
public static CustomSingletonKernelModel<T> SingletonBind<T>(this IKernel i_KernelInstance)
{
return new CustomSingletonKernelModel<T>(i_KernelInstance);
}
}
public class CustomSingletonKernelModel<T>
{
private const string k_ConstantInjectionName = "Implementation";
private readonly IKernel _kernel;
private static object padlock = new Object();
private T _concreteInstance;
public CustomSingletonKernelModel(IKernel i_KernelInstance)
{
this._kernel = i_KernelInstance;
}
public IBindingInNamedWithOrOnSyntax<T> To<TImplement>(TImplement i_Constant = null) where TImplement : class, T
{
_kernel.Bind<T>().To<TImplement>().Named(k_ConstantInjectionName);
var toReturn =
_kernel.Bind<T>().ToMethod(x =>
{
if (i_Constant != null)
{
return i_Constant;
}
if (_concreteInstance == null)
{
lock (padlock)
{
if (_concreteInstance == null)
{
_concreteInstance = _kernel.Get<T>(k_ConstantInjectionName);
}
}
}
return _concreteInstance;
}).When(x => true);
return toReturn;
}
}
And then simply use:
i_Kernel.SingletonBind<T>().To<TImplement>();
Rather then
i_Kernel.Bind<T>().To<TImplement>().InSingletonScope();
Certainly arriving late to this thread but it just happened to me with a Windows Service hosting OWIN for Web API controllers and resolving dependencies with Ninject, InSingletonScope() wasn't working until I did the following:
var kernel = new StandardKernel();
...
kernel.Bind<Foo>().ToSelf().InSingletonScope();
kernel.Bind<IFoo>().ToMethod(context => context.Kernel.Get<Foo>());
...
// Controllers ask for the dependency as usual...
public class SomeController : ApiController
{
readonly IFoo _foo;
public SomeController(IFoo foo)
{
_foo = foo;
}
...
Hope this helps
Note: I used nuget to install ninject & ninject.web.mvc (which I'm sure you did as well).
I'm not able to see the rest of your code, but here's what I had in my "NinjectDependencyScope" class. (I think yours is just called NinjectScope, might be some other naming inconsistencies with your code)
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot _resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
Contract.Assert(resolver != null);
_resolver = resolver;
}
#region IDependencyScope Members
public void Dispose()
{
var disposable = _resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
_resolver = null;
}
public object GetService(Type serviceType)
{
if (_resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return _resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (_resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return _resolver.GetAll(serviceType);
}
#endregion
}
And here is my NinjectWebCommon class (located in App_Start folder):
using System;
using System.Web;
using System.Web.Http;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Common;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ABCD.Project.Web.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(ABCD.Project.Web.App_Start.NinjectWebCommon), "Stop")]
namespace ABCD.Project.Web.App_Start
{
public static class NinjectWebCommon
{
private static readonly Bootstrapper Bootstrap = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
Bootstrap.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
Bootstrap.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
// Set Web API Resolver
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
//var modules = new INinjectModule[] { new NinjectBindingModule(), };
//kernel.Load(modules);
Here's where you would load your modules or define your bindings manually...
}
}
}

ASP.Net MVC Execution of the command requires an open and available connection. The connection's current state is broken

Several of our internal apps are MVC3 and all use Ninject.MVC.
We're running into an issue that affects all the tools at one point. First we get the error:
Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
Followed by:
Execution of the command requires an open and available connection. The connection's current state is broken.
This can happen at any time when the tool is used. We've updated our ninjects to the latest versions and it helped a LOT, errors have been much more seldom but still happen when the tools are heavily used. I've been simply setting up Ninject.MVC3 via the nuget package manager and registering the proper services to the NinjectWebCommon.cs.
Here's our our controllers, NinjectWebCommon and global.asax are setup, is there something we're doing wrong here?
One we we know for sure will fix this is to simply get rid of Ninject altogether and instantiate our repositories on each controller. But that's not a good solution I'm guessing.
Global.asax (removed some routes):
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Index", // Route name
"", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
NinjectWebCommon.cs:
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IToolRepository>().To<ToolRepository>();
}
}
HomeController:
public class HomeController : Controller
{
private IBattleStationRepository _repository;
private LoginEntities _loginEntities;
private SpecialLib _speclib;
private AccessManager _accessManager;
public HomeController(IBattleStationRepository repository)
{
_repository = repository;
_speclib = new SpecialLib();
_loginEntities = new LoginEntities();
_accessManager = new AccessManager();
}
public virtual ActionResult Index()
{
var byPassHomePage = false;
var urlReferrer = HttpContext.Request.UrlReferrer;
var user = _accessManager.GetUserByUserName(User.Identity.Name);
if (urlReferrer == null)
{
byPassHomePage = true;
}
if (user.TeamId != null && byPassHomePage == true)
{
return RedirectToAction("Release", "Release", new { shortTeamName = user.Team.ShortName });
}
return View();
}
}

Categories