Asp.net membership provider using ninject - call Initialize method - c#

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.

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>();

MVC MEF Error: Make sure that the controller has a parameterless public constructor

For MVC app using MEF, sometimes I get the erorr ""
I have a .NET solution with
One MVC Web application project and,
Many class libraries projects which take care of authentication, getting configurations, making external API calls etc.
I have configured the MEF as shown below through the code and deployed this on Web Server using IIS. Observed the below error couple of times, after which I tried to load the page multiple times but it still throws the same error.
Once I refreshed the application pool, then only the error disappears. I had been trying hard to debug and understand the error, but with no success. Am I misconfiguring MEF anywhere?
Global.asax:
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
var pluginFolders = LoadMefComponents();
Bootstrapper.Compose(pluginFolders);
IControllerFactory mefControllerFactory = new MefControllerFactory(Bootstrapper.Container);
ControllerBuilder.Current.SetControllerFactory(mefControllerFactory);
}
protected List<string> LoadMefComponents()
{
var pluginFolders = new List<string>();
string ModulesPath = CommonUtility.GetApplicationDirectory();
var plugins = Directory.GetDirectories(ModulesPath).ToList();
plugins.ForEach(path =>
{
var directoryInfo = new DirectoryInfo(path);
pluginFolders.Add(directoryInfo.Name);
});
return pluginFolders;
}
}
MEFControllerFactory.cs: This file resides in App_Start
public class MefControllerFactory : DefaultControllerFactory
{
private readonly CompositionContainer _container;
private readonly Dictionary<IController, Lazy<object, object>> exports;
private readonly object syncRoot;
public MefControllerFactory(CompositionContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this._container = container;
this.exports = new Dictionary<IController, Lazy<object, object>>();
this.syncRoot = new object();
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
Lazy<object, object> export = _container.GetExports(controllerType, null, null).FirstOrDefault();
var controller = null == export ? base.GetControllerInstance(requestContext, controllerType)
: (IController)export.Value;
lock (this.syncRoot)
{
this.exports.Add(controller, export);
}
return controller;
}
public override void ReleaseController(IController controller)
{
lock (this.syncRoot)
{
var export = this.exports[controller];
this.exports.Remove(controller);
// this._container.ReleaseExport(export);
}
((IDisposable)controller).Dispose();
}
}
Bootstrapper.cs: This file resides in App_Start
public class Bootstrapper
{
private static CompositionContainer compositionContainer;
private static bool IsLoaded = false;
public static CompositionContainer Container
{
get { return compositionContainer; }
set { compositionContainer = value; }
}
public static void Compose(List<string> pluginFolders)
{
if (IsLoaded) return;
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog(CommonUtility.GetApplicationDirectory()));
compositionContainer = new CompositionContainer(catalog);
compositionContainer.ComposeParts();
IsLoaded = true;
}
public static T GetInstance<T>(string contractName = null)
{
var type = default(T);
if (compositionContainer == null) return type;
if (!string.IsNullOrWhiteSpace(contractName))
type = compositionContainer.GetExportedValue<T>(contractName);
else
type = compositionContainer.GetExportedValue<T>();
return type;
}
}
CommonUtility.cs: This file resides in App_Start
public class CommonUtility
{
public static string GetApplicationDirectory()
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
HomeController.cs:
[CommonExceptionFilter]
public class HomeController : Controller
{
private IConfigurationManager _configurationManager;
[ImportingConstructor]
public HomeController()
{
_configurationManager = Bootstrapper.GetInstance<IConfigurationManager>();
}
public async Task<ActionResult> Index()
{
//Business Logic
return View()
}
}
IConfigurationManager.cs
[InheritedExport]
public interface IConfigurationManager
{
string GetConfigurationValue(string keyName)
}
ConfigurationManager.cs
[PartCreationPolicy(CreationPolicy.Shared)]
public class ConfigurationManager: IConfigurationManager
{
[ImportingConstructor]
public ConfigurationManager()
{
}
public string GetConfigurationValue(string keyName)
{
return "";
}
}
IHttpHandlers Used In The MVC Project:
public class CommonServiceHandler : HttpTaskAsyncHandler, IRequiresSessionState
{
private ICommonServiceHandlerManager _commonServiceHandlerManager;
public CommonServiceHandler()
{
_commonServiceHandlerManager = Bootstrapper.GetInstance<ICommonServiceHandlerManager>();
}
public override bool IsReusable
{
get
{
return false;
}
}
}
This error indicates that at least one controller exists that its constructor parameters are not resolved.
Every controller needs a constructor to be resolved at run-time. By default every c# class has a default (parameter-less) constructor that can be called when an instance of the class is needed to be created.
However, after defining an explicit constructor, you lose the default constructor, therefore, you need to make sure that all your controllers have either a parameter-less constructor or if instead they have a parametric constructor the parameters need to be registered through dependency injection.

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>();
}
}
}

Error with dependency injections

I trying create generic repository for ms sql db. Earlier i worked with something like this but with mongo db. I can compile project. but when i trying sent request i see error: "An error occurred when trying to create a controller of type 'EmployeeController'. Make sure that the controller has a parameterless public constructor." can anybody help me?
namespace TestTask.Util
{public class NinjectDependencyResolver
{
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()
{
// repository
kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
}
}
controller
namespace TestTask.Controllers
{
[RoutePrefix("api/v1")]
public class EmployeeController : ApiController
{
private readonly IEmployeeRepository _employeeRepository;
public EmployeeController(IEmployeeRepository employeeRepository) : base()
{
_employeeRepository = employeeRepository;
}
[HttpGet]
[Route("getItems")]
public IHttpActionResult GetItems(int take = 8, int skip = 0)
{
if(take<1|| skip < 0)
{
ModelState.AddModelError("Error", "Invalid take or skip params.");
return BadRequest(ModelState);
}
var result = _employeeRepository.Find(x => x.Id >= 0, x=>x.Id, take, skip);
return Ok(result);
}
[HttpGet]
[Route("pageCount")]
public IHttpActionResult PageCount(int itemsInPage)
{
var TotalCount = _employeeRepository.Count(x => x.Id >= 0);
var result = Math.Ceiling((double)TotalCount / (double)itemsInPage);
return Ok(result);
}
}
}
generic repository
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Web;
namespace TestTask.Context
{
public abstract class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
private DefaultConnection context = new DefaultConnection();
public virtual List<TEntity> Find(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> order, int take =50, int skip=0) //where TEntity : class
{
return context.Set<TEntity>().Where(predicate).OrderBy(order).Skip(skip).Take(take).ToList();
}
public virtual int Count(Expression<Func<TEntity, bool>> predicate)
{
return context.Set<TEntity>().Where(predicate).Count();
}
}
}
I had to do this a while back and remember it not being as straight forward with Web API as it is with MVC.
I posted details and a demo project as part of a unit of work example here, and github project here.
Below are the pieces for configuring dependency injection with Ninject.
UnitOfWorkExample.WebApi/Controllers/ProductsController.cs
namespace UnitOfWorkExample.WebApi.Controllers
{
[RoutePrefix("products")]
public class ProductsController : ApiController
{
private IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[Route]
public IHttpActionResult GetProducts()
{
// ensure there are products for the example
if (!_productService.GetAll().Any())
{
_productService.Create(new Product { Name = "Product 1" });
_productService.Create(new Product { Name = "Product 2" });
_productService.Create(new Product { Name = "Product 3" });
}
return Ok(_productService.GetAll());
}
}
}
UnitOfWorkExample.WebApi/App_Start/NinjectWebCommon.cs
namespace UnitOfWorkExample.WebApi.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
using Ninject.Extensions.Conventions;
using System.Web.Http;
using UnitOfWorkExample.Domain.Helpers;
using UnitOfWorkExample.Data.Helpers;
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);
// Install our Ninject-based IDependencyResolver into the Web API config
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(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)
{
// unit of work per request
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
// default binding for everything except unit of work
kernel.Bind(x => x.FromAssembliesMatching("*").SelectAllClasses().Excluding<UnitOfWork>().BindDefaultInterface());
}
}
}
UnitOfWorkExample.WebApi/App_Start/NinjectDependencyResolver.cs
namespace UnitOfWorkExample.WebApi.App_Start
{
public class NinjectScope : IDependencyScope
{
protected IResolutionRoot resolutionRoot;
public NinjectScope(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return resolutionRoot.Resolve(request).SingleOrDefault();
}
public IEnumerable<object> GetServices(Type serviceType)
{
IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return resolutionRoot.Resolve(request).ToList();
}
public void Dispose()
{
IDisposable disposable = (IDisposable)resolutionRoot;
if (disposable != null) disposable.Dispose();
resolutionRoot = null;
}
}
public class NinjectResolver : NinjectScope, IDependencyResolver
{
private IKernel _kernel;
public NinjectResolver(IKernel kernel)
: base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectScope(_kernel.BeginBlock());
}
}
}
ASP.NET does not inject anything into controllers by default. Instead, it looks for a matching controller and just uses new. So it needs a controller that has a default constructor with no parameters-- unless you do something special to add DI.
To add dependency injection to your controllers, you need to register a custom controller factory. The factory should use Ninject to instantiate the controller, which will automatically populate its constructor arguments with the appropriate dependencies. Here is a bare bones example (you will need to modify it to allow other controller types, as well as implement the rest of the interface):
public class CustomControllerFactory : IControllerFactory
{
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return _kernel.Get<EmployeeController>();
}
}
You can register it in startup with code like this:
protected void Application_Start()
{
IControllerFactory factory = new CustomControllerFactory();
ControllerBuilder.Current.SetControllerFactory(factory);
}
More info can be found here. The blogger shows you exactly the error that you are seeing and then fixes it.

BasicAuthHttpModule custom property resolve at runtime

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);
}
}

Categories