我正在尝试使用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如何工作的文章后,我无法弄清楚问题是什么。拜托,你能解释一下吗?谢谢。
答案 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.
}
希望这有帮助。