Получение Ninject для работы
Очевидно, я что-то упустил. У меня есть приложение 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
класс всегда имеет нуль IUnitOfWorkFactory
когда я пытаюсь собрать нужные мне данные.
Что я делаю неправильно? Кажется, все так, как показывают все примеры, но я не получаю любви.
ОБНОВИТЬ
Используя совет Бассама Механни, я переписал свой 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>();
}
Есть что-то еще, что я пропускаю? Это очень расстраивает.
2 ответа
Статические классы, которые не зависят ни от каких статических классов, - это то, что вы не должны делать при использовании контейнера IoC. Вместо этого вы должны создать не статический класс с одиночным временем жизни.
- Сделайте ваш класс SiteSettings не статичным.
- Вставьте все зависимости, например, IUnitOfWorkFactory, в SiteSettings, используя конструктор
- Создайте привязку в одноэлементной области для SiteSettings
- Получите экземпляр SiteSettings, где бы вам ни понадобился доступ к инъекции unsing constructor.
Пример:
public class SiteSettings {
public SiteSettings(IUnitOfWorkFactory uowFactory) { .... }
....
}
public class INeedToAccessSiteSettings
{
public INeedToAccessSiteSettings(SiteSettings siteSettings) { .... }
}
kenrel.Bind<SiteSettings>().ToSelf().InSingletonScope();
Обычно ninject вводит ваш сервис в конструктор или что-то в этом роде, он волшебным образом не превращает все ваши интерфейсы в экземпляры объектов во время выполнения.
например:
public class MyController : Controller
{
private IServiceThatINeed _serviceThatINeed;
public MyController(IServiceThatINeed serviceThatINeed)
{
_serviceThatINeed = _serviceThatINeed;
}
}
в этом случае, поскольку вы зарегистрировали свой экземпляр ядра, mvc знает, как разрешить эту зависимость, и передаст экземпляр объекта, который реализует IServiceThatINeed
(Предполагая, что вы рассказали, как разрешить эту зависимость.
Теперь может быть случай, когда вам понадобится получить сервис без его внедрения в конструктор средой mvc, в этих случаях (например, тот, что у вас есть здесь) вам нужно будет использовать ServiceLocator
например:
var myService = ServiceLocator.Current.GetInstance<IServiceThatINeed>()
использовать ServiceLocator
необходимо добавить ссылку на Microsoft.Practices.ServiceLocation
Надеюсь, это поможет!