【问题标题】:Getting Ninject to work让 Ninject 工作
【发布时间】:2013-01-17 23:25:35
【问题描述】:

显然,我遗漏了一些东西。我有一个 MVC 应用程序并安装了 Ninject 3 和 MVC3 扩展(尽管我正在运行 MVC4)。我有一个在整个项目中引用的SiteSettings 类,如下所示:

public class SiteSettings
{
    private static readonly Common.Logging.ILog Logger = Common.Logging.LogManager.GetCurrentClassLogger();
    private static ObservableDictionary<string, string> settings;
    private static bool Initialized = false;
    private static DataPersister persister;

    public static void Initialize()
    {
        if (Initialized) throw new InvalidOperationException("The SiteSettings object has already been initialized.");
        persister = new DataPersister();
        using (var u = persister.UnitOfWorkFactory.GetUnitOfWork())
        {
            var settingsList = u.SiteSettings.GetAll();
            settings = new ObservableDictionary<string, string>(settingsList.ToDictionary(key => key.SiteSettingName, value => value.SiteSettingValue));
            settings.OnChange += new kvpChangeEvent<string, string>(settings_OnChange);
        }
        Initialized = true;
    }

    static void settings_OnChange(object sender, odKVPChangeEventArgs<string, string> e)
    {
        using (var u = persister.UnitOfWorkFactory.GetUnitOfWork())
        {
            var setting = u.SiteSettings.GetByName(e.Key);
            setting.SiteSettingValue = e.Value;
            u.SiteSettings.Update(setting);
            u.Save();
            Logger.Info(i => i("Changed the '{0}' site setting from '{1}' to '{2}'.", e.Key, e.OldValue, e.Value));
        }
    }

    private static int _ItemsPerPage;
    public static int ItemsPerPage
    {
        get
        {
            return _ItemsPerPage;
        }
        set
        {
            _ItemsPerPage = value;
            settings["itemsPerPage"] = value.ToString();
        }
    }

    private static int _SessionLifeInMinutes;
    public static int SessionLifeInMinutes
    {
        get
        {
            return _SessionLifeInMinutes;
        }
        set
        {
            _SessionLifeInMinutes = value;
            settings["sessionLifeInMinutes"] = value.ToString();
        }
    }

    private static string _DateFormat;
    public static string DateFormat
    {
        get
        {
            return _DateFormat;
        }
        set
        {
            _DateFormat = value;
            settings["defaultDateFormat"] = value;
        }
    }
}

我像这样构建了一个数据持久性对象:

public class DataPersister
{
    public IUnitOfWorkFactory UnitOfWorkFactory { get; set; }
}

...我的NinjectWebCommon.cs 看起来像这样:

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<IUnitOfWork>().To<NHUnitOfWork>();
        kernel.Bind<IUnitOfWorkFactory>().To<NHUnitOfWorkFactory>();
    }
}

在我看来,我已经满足了依赖注入的所有要求。我的Global.asax.cs Application_Start() 看起来像这样:

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new MonoRazorViewEngine());

    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    ControllerBuilder.Current.DefaultNamespaces.Add("MyApplication.Application.Controllers");
    Initialize.Security();
    SiteSettings.Initialize();
}

...然而,当我尝试收集我需要的数据时,我的 SiteSettings 类总是有一个 null IUnitOfWorkFactory

我做错了什么?一切似乎都如所有示例所暗示的那样,但我没有得到爱。

更新

根据 Bassam Mehanni 的建议,我重写了我的 DataPersister 类,如下所示:

public class DataPersister
{
    private IUnitOfWorkFactory UnitOfWorkFactory;
    public DataPersister(IUnitOfWorkFactory unitOfWorkFactory)
    {
        UnitOfWorkFactory = unitOfWorkFactory;
    }
    public IUnitOfWork GetUnitOfWork()
    {
        return UnitOfWorkFactory.GetUnitOfWork();
    }
}

...当然,现在我的SiteSettings 类抱怨我的无参数构造函数。我该怎么办?

更新 2

好的,继续,我像这样重写了我的DataPersister 类:

public class DataPersister
{
    private static readonly Common.Logging.ILog Logger = Common.Logging.LogManager.GetCurrentClassLogger();

    private IUnitOfWorkFactory UnitOfWorkFactory { get; set; }
    public IUnitOfWork GetUnitOfWork()
    {
        return UnitOfWorkFactory.GetUnitOfWork();
    }
    [Inject]
    public DataPersister(IUnitOfWorkFactory factory)
    {
        Logger.Info("Injected constructor called");
        UnitOfWorkFactory = factory;
    }
    public DataPersister()
    {
        Logger.Info("Parameterless constructor called");
    }
}

然后我像这样重写了我的SiteSettings 类:

public class SiteSettings
{
    private static readonly Common.Logging.ILog Logger = Common.Logging.LogManager.GetCurrentClassLogger();
    private ObservableDictionary<string, string> settings;
    private DataPersister persister;
    private SiteSettings()
    {
        persister = new DataPersister();
        using (var u = persister.GetUnitOfWork())
        {
            var settingsList = u.SiteSettings.GetAll();
            settings = new ObservableDictionary<string, string>(settingsList.ToDictionary(key => key.SiteSettingName, value => value.SiteSettingValue));
            settings.OnChange += new kvpChangeEvent<string, string>(settings_OnChange);
        }
    }
    private static SiteSettings instance;

    public static SiteSettings Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new SiteSettings();
            }
            return instance;
        }
    }

    private void settings_OnChange(object sender, odKVPChangeEventArgs<string, string> e)
    {
        using (var u = persister.GetUnitOfWork())
        {
            var setting = u.SiteSettings.GetByName(e.Key);
            setting.SiteSettingValue = e.Value;
            u.SiteSettings.Update(setting);
            u.Save();
            Logger.Info(i => i("Changed the '{0}' site setting from '{1}' to '{2}'.", e.Key, e.OldValue, e.Value));
        }
    }

    private int _ItemsPerPage;
    public int ItemsPerPage
    {
        get
        {
            return _ItemsPerPage;
        }
        set
        {
            _ItemsPerPage = value;
            settings["itemsPerPage"] = value.ToString();
        }
    }

    private int _SessionLifeInMinutes;
    public int SessionLifeInMinutes
    {
        get
        {
            return _SessionLifeInMinutes;
        }
        set
        {
            _SessionLifeInMinutes = value;
            settings["sessionLifeInMinutes"] = value.ToString();
        }
    }

    private string _DateFormat;
    public string DateFormat
    {
        get
        {
            return _DateFormat;
        }
        set
        {
            _DateFormat = value;
            settings["defaultDateFormat"] = value;
        }
    }
}

这不应该吗?因为它没有。 DataPersister 类总是使用无参数构造函数调用。我的内核绑定如下所示:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IUnitOfWork>().To<NHUnitOfWork>();
    kernel.Bind<IUnitOfWorkFactory>().To<NHUnitOfWorkFactory>();
}

我还有什么遗漏的吗?这变得非常令人沮丧。

【问题讨论】:

  • 使用 MVC,您可以从 NinjectHttpApplication.. 继承。
  • 我是注入库的新手,对ninject 的经验甚至更少,所以我把它作为评论而不是答案......但是你的注入点在哪里? Kernel.Get 的电话在哪里? - ninject.codeplex.com/…MVC ninject 库为 MVC 自己的工厂注册了 ninject 容器,但是您的 IUnitOfWorkFactory 创建不是通过该工厂?
  • @Snixtor 它的设置有点神奇。我在注册上设置了一个断点,它确实达到了……不知何故。
  • @SimonWhitehead,我试过了,但如果我也能让它工作,那就见鬼了。
  • 注册可能正在运行,但检索没有。

标签: c# asp.net-mvc asp.net-mvc-4 ninject ninject-extensions


【解决方案1】:

在使用 IoC 容器时不应该使用不依赖于静态类的静态类。相反,您应该创建一个具有单例生命周期的非静态类。

  1. 使您的 SiteSettings 类不是静态的。
  2. 注入所有依赖项,例如使用构造函数注入将 IUnitOfWorkFactory 放入 SiteSettings
  3. 在单例范围内为 SiteSettings 创建绑定
  4. 在需要访问 unsing 构造函数注入的任何地方获取 SiteSettings 实例。

例子:

public class SiteSettings {
    public SiteSettings(IUnitOfWorkFactory uowFactory) { .... }

    ....
}


public class INeedToAccessSiteSettings
{
    public INeedToAccessSiteSettings(SiteSettings siteSettings) { .... }
}

kenrel.Bind<SiteSettings>().ToSelf().InSingletonScope();

【讨论】:

  • 请看我的更新。我将SiteSettings 类更改为单例模型,但我仍然没有得到任何爱。
  • 你还是不做依赖注入。您使用单例模式,新建无法工作的持久器 (persister = new DataPersister();)!在构造函数中注入持久化器并移除单例模式
  • 对不起,我很抱歉,但是我将如何实例化您的 INeedToAccessSiteSettings 类?
  • 我不知道您需要在哪里访问 SiteSettings 类。例如。这可能是一个控制器。
  • 我在静态类中的助手和诸如此类的东西中使用了很多站点设置。看来我需要某种包装器,但我仍然无法思考如何正确实例化它。
【解决方案2】:

通常 ninject 会将您的服务注入构造函数或其他东西中,它不会在运行时神奇地将所有接口转换为对象实例

例如:

public class MyController : Controller
{
   private IServiceThatINeed _serviceThatINeed;
   public MyController(IServiceThatINeed serviceThatINeed)
   { 
       _serviceThatINeed = _serviceThatINeed;
   }
}

在这种情况下,由于您注册了内核实例,mvc 知道如何解决这种依赖关系,并将传递一个实现 IServiceThatINeed 的对象的实例(假设您告诉 ninject 如何解决这种依赖关系。

现在可能有一个实例,您需要在不通过 mvc 框架将其注入到构造函数中的情况下获取服务,在这些实例中(就像您在此处的实例一样),您将需要使用 ServiceLocator

例如:

var myService = ServiceLocator.Current.GetInstance<IServiceThatINeed>()

要使用ServiceLocator,您需要添加对Microsoft.Practices.ServiceLocation的引用

希望有帮助!

【讨论】:

  • 感谢您的回答。我尝试按照您建议的方式编写DataPersister 类(请参阅更新),但参数的东西仍然让我感到困惑。你能建议吗?
  • @Jeremy Holovacs - Bassam 的这个答案是我想要得到的。
  • 当你向 mvc 注册 ninject 时,它会在 mvc 框架新建一个类时使用它,但是当你是一个新建类时,你必须自己解决依赖关系,在 SiteSettings 中使用 serviceLoator,然后将其传递给 DataPersister 的构造函数
  • 没错。有些是自动的,有些不是。 @JeremyHolovacs - 这可能是一些快速教程让你误入歧途的地方,没有讨论 MVC 框架何时会和不会“为你”处理注入。
  • 应该注意的是,存在反对服务定位器模式的论据,您应该仔细阅读并决定它是否会影响您。另一种方法是让 Ninject 注入工厂,您可以使用它们来创建对象。
猜你喜欢
  • 2013-12-27
  • 2015-03-27
  • 1970-01-01
  • 2023-03-06
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
相关资源
最近更新 更多