To make a long story short, the application that I am currently writing ought to impersonate the current logged in user.
It's an application to manage information inquiries.
Because of NHibernate.ISessionFactory
not allowing more flexibility at the level of its connection string, I need to build the connection dynamically using the current user credentials. (By the way, I'm not complaining against NH, it's a wonderful that I use on each project.)
So, I need to force authentication at start up. Many dependencies may be bound on startup, but not those of the data access module, which require to be loaded up after the user has authenticated because of the connection string.
- Authentication if forced;
- The
AuthenticationPresenter
tells theMembershipService
to authenticate the user using the provided credentials; - On successful authentication, an instance of the
AuthetnicationUser
class is kept within theMembershipService
, which in turn is bound in singleton scope; - The
SessionFactoryProvider
depends on myMembershipService.CurrentUser
property to retrieve the credentials used for building the connection string, thus theCurrentUser
property cannot be null on this module's loading, hence I can't load this module on application startup without breaking something.
It is a Windows Form application, so I use the Program
class to instantiate the Ninject IKernel
and load the modules required for application startup.
The application later requires for new dependencies to be resolved such the data access, the inquiries management and the repositories which depends on a ISession
.
The solution should be to be able to either load the modules that require the CurrentUser
to be known later in the application lifecycle, or perhaps using contextual or conditional dependency injection binding.
DataModule
public class DataModule : NinjectModule {
public override void Load() {
Bind<ISessionFactory>().ToProvider<SessionFactoryProvider>().InSingletonScope();
Bind<ISession>().ToProvider<SessionProvider>();
Bind<IStatelessSession>().ToProvider<StatelessSessionProvider>();
}
}
ServiceModule
public class ServiceModule : NinjectModule {
public override void Load() {
Bind<IMembershipService>().To<MembershipService>().InSingletonScope();
}
}
AuthenticationModule
public class AuthenticationModule : NinjectModule {
public override void Load() {
Bind<AuthenticationPresenter>().ToSelf().InSingletonScope();
Bind<IAuthenticationPresenterFactory>().ToFactory();
Bind<IAuthenticationView>().To<AuthenticationForm>();
}
}
InquiriesManagementModule
public class InquiriesManagementModule : NinjectModule {
public override void Load() {
Bind<IInquiriesManagementPresenterFactory>().ToFactory();
Bind<InquiriesManagementPresenter>().ToSelf().InSingletonScope();
Bind<IInquiriesManagementView>().To<InquiriesMgmtForm>();
Bind<ICancelInquiryPresenterFactory>().ToFactory();
Bind<CancelInquiryPresenter>().ToSelf();
Bind<ICancelInquiryView>().To<CancelInquiryForm>();
Bind<IEditInquiryPresenterFactory>().ToFactory();
Bind<EditInquiryPresenter>().ToSelf();
Bind<IEditInquiryView>().To<EditInquiryForm>();
Bind<INewInquiryPresenterFactory>().ToFactory();
Bind<NewInquiryPresenter>().ToSelf();
Bind<INewInquiryView>().To<NewInquiryForm>();
Bind<IInquiriesRepository>().To<InquiriesRepository>();
}
}
Should you require further details, please have a look at this question:
Conditional dependency injection binding only when property not null
I have also thought of writing an ApplicationContext
class that could contain such essential information as the current user to pass it along the application's presenters and provider that require it, and still, the problem remains.
So I wonder whether it is a design flaw or a lack of knowledge of the dependency injection tool, or maybe both.
EDIT
Following @Simon Whitehead's comment,
Have you thought about using the
WithConstructorArgument
method to pass in user credentials?
I have not thought of using WithConstructorArgument
for a couple of reasons:
- I'm using Ninject for the second time in my life
- I do think that I have to provide values to these arguments, which I don't yet have upon application startup, because no user has been authenticated yet
- The dependencies are loaded upfront application startup, so it throws even before getting to the authentication window, which is the first and only thing the user can do when double-clicking the executable
Program (How it was before I decide to go for a static dependency injection factory)
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
IKernel dependencies = new StandardKernel(
new ApplicationModule(),
new AuthenticationModule(),
new ServiceModule(),
new InquiriesManagementModule(),
new DataModule());
ApplicationPresenter applicationPresenter =
dependencies.Get<ApplicationPresenter>();
Application.Run((Form)applicationPresenter.View);
}
Program (How it is now that I use the static dependency injection factory)
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
DependencyInjectionFactory.Register(
new ApplicationModule(),
new AuthenticationModule(),
new ServiceModule());
ApplicationPresenter applicationPresenter =
DependencyInjectionFactory.Resolve<ApplicationPresenter>();
Application.Run((Form)applicationPresenter.View);
}
Beside, I feel like it's a dirty solution to have a static dependency injector as it can be called from anywhere as by magic...
Am I pushing it too far so that I complicate things instead of simplifying them?
WithConstructorArgument
method to pass in user credentials? – EmpowerWithConstructorArgument
. I shall edit to reflect our exchange. – Uprise