我弄错了什么,DI或设计,我应该怎么做呢?

时间:2014-04-02 02:13:37

标签: c# nhibernate architecture dependency-injection ninject

总而言之,我目前正在编写的应用程序应该模拟当前登录的用户。

这是一个管理信息查询的应用程序。

由于NHibernate.ISessionFactory在连接字符串级别上不允许更多的灵活性,我需要使用当前用户凭据动态构建连接。 (顺便说一句,我不是在反对NH,我在每个项目上使用都很棒。)

所以,我需要在启动时强制进行身份验证。许多依赖项可能在启动时绑定,但不能绑定数据访问模块,这些依赖项需要在用户通过连接字符串进行身份验证后加载。

  1. 强制验证;
  2. AuthenticationPresenter告诉MembershipService使用提供的凭据对用户进行身份验证;
  3. 成功验证后,AuthetnicationUser类的实例保留在MembershipService内,而SessionFactoryProvider又绑定在单一范围内;
  4. MembershipService.CurrentUser依赖于我的CurrentUser属性来检索用于构建连接字符串的凭据,因此Program属性在此模块的加载时不能为空,因此我不能在应用程序启动时加载此模块而不会破坏某些内容。
  5. 它是一个Windows窗体应用程序,因此我使用IKernel类来实例化Ninject ISession并加载应用程序启动所需的模块。

    该应用程序稍后需要解析新的依赖项,例如数据访问,查询管理和依赖于CurrentUser的存储库。

    解决方案应该是能够加载需要在应用程序生命周期中稍后知道public class DataModule : NinjectModule { public override void Load() { Bind<ISessionFactory>().ToProvider<SessionFactoryProvider>().InSingletonScope(); Bind<ISession>().ToProvider<SessionProvider>(); Bind<IStatelessSession>().ToProvider<StatelessSessionProvider>(); } } 的模块,或者使用上下文或条件依赖注入绑定。

    DataModule的

    public class ServiceModule : NinjectModule {
        public override void Load() {
            Bind<IMembershipService>().To<MembershipService>().InSingletonScope();
        }
    }
    

    ServiceModule

    public class AuthenticationModule : NinjectModule {
        public override void Load() {
            Bind<AuthenticationPresenter>().ToSelf().InSingletonScope();
            Bind<IAuthenticationPresenterFactory>().ToFactory();
            Bind<IAuthenticationView>().To<AuthenticationForm>();
        }
    }
    

    AuthenticationModule

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

    InquiriesManagementModule

    ApplicationContext

    如果您需要更多详细信息,请查看此问题:

    Conditional dependency injection binding only when property not null

    我还想过编写一个WithConstructorArgument类,它可以包含当前用户的基本信息,以便将它传递给需要它的应用程序的演示者和提供者,但问题仍然存在。

    所以我想知道这是一个设计缺陷还是缺乏依赖注入工具的知识,或者两者兼而有之。

      

    修改

    关注@Simon Whitehead的评论,

      

    您是否考虑过使用WithConstructorArgument方法传入用户凭据?

    由于以下几个原因,我没有想过使用[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); }

    1. 我生命中第二次使用Ninject
    2. 我认为我必须为这些参数提供值,这些参数在应用程序启动时尚未提供,因为尚未对用户进行身份验证
    3. 依赖项是在前端应用程序启动时加载的,因此它甚至在进入身份验证窗口之前就会抛出,这是用户在双击可执行文件时可以做的第一件也是唯一的事情
    4. 程序 (我决定去静态依赖注入工厂之前的情况)

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

      程序 (我现在如何使用静态依赖注入工厂)

      {{1}}

      除此之外,我觉得拥有一个静态依赖注入器是一个肮脏的解决方案,因为它可以通过魔术从任何地方调用......

      我是否将它推得太远以至于我使事情复杂化而不是简化它们?

1 个答案:

答案 0 :(得分:1)

您根本不需要条件或上下文绑定。 转而采用最简单的方法: 编写nhibernate配置,会话,......好像你没有运行时&#34;计时&#34;依赖。这意味着:将经过身份验证的用户注入NHibernate配置构建器。

然后,您只需要确保在对用户进行身份验证之前,不依赖于任何NHibernate的对象树部分进行实例化。这应该很容易。

使用任何类型的引导机制,首先创建绑定,然后显示登录窗口,登录,成功登录后,加载(显示)应用程序的其余部分。

结合:

IBindingRoot.Bind<Configuration>().ToProvider<ConfigurationProvider>();
IBIndingRoot.Bind<ISessionFactory>()
            .ToMethod(ctx => ctx.Kernel.Get<Configuration>().BuildSessionFactory())
            .InSingletonScope();

public class ConfigurationProvider : IProvider<Configuration> 
{
    private readonly IUserService userService;

    public ConfigurationProvider(IUserService userService)
    {
        this.userService = userService;
    }

    public object Create(IContext context)
    {
        if(this.userService.AuthenticatedUser == null)
            throw new InvalidOperationException("never ever try to use NHibernate before user is authenticated! this includes injection an ISessionFactory in any class! Postpone creationg of object tree until after authentication of user. This exception means you've produced buggy code!");

        return Fluently.Configure()
            .DataBase(MsSqlConfiguration.MsSql2008)
                .ConnectionString(connectionBuilder => connectionBuilder.Is(... create the string...)
            ....
            .BuildConfiguration();
    }
}