使用ninject的Asp.net成员资格提供者 - 调用Initialize方法

时间:2013-05-07 17:42:32

标签: c# asp.net-mvc asp.net-membership ninject

我正在尝试使用ninject进行依赖注入,在asp.net mvc 4 Web应用程序中实现自定义成员资格提供程序。这是我到目前为止的代码。

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

这是我的ninject控制器工厂(我也在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);
    }
}
正如我在AccountMembershipProvider类中的注释中提到的那样,当AccountRepository.Initialize(name,config);调用我收到以下错误:对象引用未设置为对象的实例。调试应用程序并阅读有关ninject如何工作的文章后,我无法弄清楚问题是什么。拜托,你能解释一下吗?谢谢。

5 个答案:

答案 0 :(得分:2)

尝试在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
        ...
    }
}

答案 1 :(得分:2)

我使用自定义成员资格提供程序遇到了类似的问题。如果要在AccountRepository中调用initialize方法,可以执行以下操作:

使用以下内容(通过nuget提供)在App_Start中使用ninject配置DI:

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. 

    }

}

然后在您的自定义提供程序中:

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
}

希望这有帮助,

答案 2 :(得分:1)

不是您正在寻找的答案,但我不久前尝试过这样做,虽然我确实找到了一种方法使其发挥作用,但我发现解决方案对于生产使用而言过于复杂和脆弱。

由于Membership创建提供程序的方式,我只是放弃了对自己的自定义提供程序使用Injection的想法。在我看来,这不值得头痛。

我还认为使用Injection会破坏成员资格提供程序的整个概念,因为我们的想法是提供程序应该是可插入的,并且您可以在不对代码进行任何更改的情况下将其替换为另一个。如果您必须在应用程序中配置配置代码以配置数据库上下文,则根本无法做到这一点。

好的,你可能会争辩说你不会改变提供者..在这种情况下,为什么要麻烦提供者呢?为什么不实现自定义IIdentity和IPrincipal实现。

答案 3 :(得分:1)

如何将以下行添加到AddBindings()方法

kernel.Bind<AccountMembershipProvider>().ToMethod(ctx => Membership.Provider);

我在mvc3中使用了ninject和自定义成员资格提供程序 不确定它是否对您有所帮助,但您可以将您与我的进行比较。

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

        }  

    }
}

答案 4 :(得分:0)

您可以实施DefaultControllerFactory

,而不是实施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>();
    }
}

然后在 global.asax.cs 而不是设置ControllerFactory,您可以设置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.
    }
}

然后在MembershipProvider的实现中询问当前的DependencyResolver(Ninject),为您创建一个实例,在本例中为IAccountRepository

public class CustomMembershipProvider : MembershipProvider
{
    private readonly IAccountRepository _repository;

    public OpenTibiaMembershipProvider()
    {
        _repository = (IAccountRepository)DependencyResolver.Current.GetService(typeof(IAccountRepository));
    }

    // Rest of implementation.
}

希望这有帮助。