2

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

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

正如我在 AccountRepository.Initialize(name, config); 时在 AccountMembershipProvider 类的评论中提到的那样。调用我收到以下错误:对象引用未设置为对象的实例。 在调试应用程序并阅读有关 ninject 如何工作的文章后,我无法弄清楚问题所在。请问,你能解释一下吗?谢谢你。

4

5 回答 5

2

尝试在 Global.asax 中注册实例的初始化。

公共类 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
        ...
    }
}
于 2013-05-17T11:44:29.373 回答
2

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

使用以下命令在 App_Start 中使用 ninject 配置 DI(可通过 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. 

    }

}

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

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
}

希望这可以帮助,

于 2013-05-17T22:44:34.943 回答
1

将以下行添加到您的 AddBindings() 方法中怎么样

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

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

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

        }  

    }
}
于 2013-05-15T01:11:55.503 回答
1

不是您要寻找的答案,但我不久前自己尝试过这样做,虽然我确实设法找到了使其工作的方法,但我发现该解决方案对于生产使用来说过于复杂和脆弱。

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

我还认为使用注入打破了成员资格提供者的整个概念,因为这个想法是提供者应该是可插入的,并且您可以用另一个替换一个而不对您的代码进行任何更改。如果您必须在应用程序中使用配置代码来配置数据库上下文,那根本不可能做到这一点。

好的,您可能会争辩说您不会更改提供者.. 在这种情况下,为什么还要打扰提供者呢?为什么不只实现自定义 IIdentity 和 IPrincipal 实现。

于 2013-05-12T20:29:36.000 回答
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.
}

希望这可以帮助。

于 2014-07-02T19:44:55.240 回答