I keep reading that property injection is bad, but I have one usecase where I can justify it to myself, private property injection no less. But I am wondering if it could be done differently.
So, my issue is that I'm creating (Prism) modules for an app, they need to implement the IModule interface like this
public class Configuration : NinjectModule
{
public override void Load()
{
...
//Binding here
...
}
}
public class MyModule : IModule
{
private readonly IKernel _kernel;
private readonly IRegionManager _regionManager;
public MyModule (IKernel kernel, IRegionManager regionManager)
{
_kernel = kernel;
_regionManager = regionManager;
}
public void Initialize()
{
_kernel.Load<Configuration>();
//register views and the like here.
}
}
It is however not optimal in my world. For instance I always have to inject the kernel and regionmanager. I can handler most of it in an abstract class like this
public abstract class AModule : IModule
{
protected IKernel Kernel { get; set; }
protected IRegionManager RegionManager { get; set; }
protected AModule(IKernel kernel, IRegionManager regionManager)
{
Kernel = kernel;
RegionManager = regionManager;
}
public virtual void Initialize()
{
Kernel.Load(CreateConfiguration());
}
protected abstract NinjectModule CreateConfiguration();
}
public class MyModule : IModule
{
public MyModule (IKernel kernel, IRegionManager regionManager)
: base(kernel, regionManager)
{
}
public void Initialize()
{
base.Initialize();
//register views and the like here.
}
protected override NinjectModule CreateConfiguration()
{
return new Configuration();
}
}
But I still have to worry about the kernel and regionmanager in the constructor definition. Ideally I want to avoid writing a constructor entirely like this
public abstract class AModule : IModule
{
[Inject]
protected IKernel Kernel { get; set; }
[Inject]
protected IRegionManager RegionManager { get; set; }
public virtual void Initialize()
{
Kernel.Load(CreateConfiguration());
}
protected abstract NinjectModule CreateConfiguration();
}
This allows me to write the module like this
public class MyModule : AModule
{
public override void Initialize()
{
base.Initialize();
//register views and the like here.
}
protected override NinjectModule CreateConfiguration()
{
return new Configuration();
}
}
Why is this a bad way to do it, and is it even possible to do this without property injection?
Related
This is going to be a bit lengthy, so hold on tight.
I have a project that uses Caliburn.Micro to implement MVVM pattern. For IoC, I am using MEF. All the ViewModels are marked with attribute [Export(typeof(IScreen))] . IScreen is defined in Caliburn.Micro.
In my BootStrapper file I have set up MEF like so:
private CompositionContainer container;
protected override void Configure()
{
//Set up MEF
container = new CompositionContainer(new AggregateCatalog``AssemblySource.Instance.Select``(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(container);
container.Compose(batch);
//Other stuff
}
Now, I show the first ViewModel using
protected override void OnStartup(object sender, StartupEventArgs e) => DisplayRootViewFor<ViewModels.Main.MainViewModel>();
All that is fine and dandy. Now, the problem starts when I want to switch from one ViewModel to another.
An example of a ViewModel is :
[PartCreationPolicy(CreationPolicy.NonShared), Export(typeof(IScreen))]
sealed class SampleViewModel: ScreenVMwithUoW<IAddItemUoW>
{
[ImportingConstructor]
public SampleViewModel(IAddItemUoW uow, IEventAggregator eventAggregator) : base(uow, eventAggregator)
{
}
//bla bla bla
}
Let's say I want to open SampleViewModel and close my MainViewModel. How do I do it using MEF?
Here is what I am doing now:
[Export(typeof(IScreenFactory))]
class ScreenFactory : IScreenFactory
{
private Dictionary<Type, ExportFactory<IScreen>> _screenDictionary { get; set; }
private IEnumerable<ExportFactory<IScreen>> _screenfactoryCollection { get; set; }
private ExportLifetimeContext<IScreen> _currentScreenLifeTimeCtx { get; set; }
[ImportingConstructor]
public ScreenFactory([ImportMany(AllowRecomposition = true)] IEnumerable<ExportFactory<IScreen>> screenList)
{
_screenfactoryCollection = screenList;
_reportfactoryCollection = reportsList;
}
public IScreen GetScreen(Type t)
{
if (_screenDictionary == null)
{
PopulateScreenDictionary(_screenfactoryCollection);
}
_currentScreenLifeTimeCtx = _screenDictionary[t].CreateExport();
return _currentScreenLifeTimeCtx.Value;
}
private void PopulateScreenDictionary(IEnumerable<ExportFactory<IScreen>> screenfactories)
{
_screenDictionary = screenfactories.ToDictionary(c => c.CreateExport().Value.GetType(), c => c);
}
public void DisposeCurrentScreenContext()
{
_currentScreenLifeTimeCtx?.Dispose();
}
public void Dispose()
{
DisposeCurrentScreenContext();
}
}
Whenever I need a new instance of a ViewModel, I just do:
DeactivateItem(ActiveItem, true);
_vmFactory.DisposeCurrentScreenContext();
ActivateItem(_vmFactory.GetScreen(NextViewModelName));
This CANNOT be the normal way to do so. Also, this also adds a huge hit in performance, especially the startup time of the application, when the number of ViewModels increase to about 25-30.
I would also like to avoid the Service Locator Pattern, if possible. I am also open to using a proper IoC Container instead of MEF, but MEF allows me to add ViewModels with very little effort. Just adding the annotation [Export(typeof(IScreen))] does the job.
I'm trying to use Autofac to register a service into Xamarin.Forms (netstandard2.0)
here's my code:
SqliteManager.cs
public interface ISqliteManager
{
void test();
}
public class SqliteManager : ISqliteManager
{
private readonly string _path;
public SqliteManager()
{
_path = "test";
}
public void test()
{
Console.Write(_path);
}
}
App.xaml.cs
public partial class App : Application
{
public App()
{
InitializeComponent();
ViewModelLocator.RegisterDependencies();
ViewModelLocator.Resolve<ISqliteManager>().test();
}
}
ViewModelLocator.cs
public static class ViewModelLocator
{
private static IContainer _container;
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterType<SqliteManager>().As<ISqliteManager>().SingleInstance();
_container?.Dispose();
_container = builder.Build();
}
public static T Resolve<T>()
{
return _container.Resolve<T>();
}
}
it's seems ok, but i get this error when i try to resolve the service in app.xaml.cs:
Registration: Activator = SqliteManager (ReflectionActivator),
Services = [Tracker.Managers.Interfaces.ISqliteManager], Lifetime =
Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership =
OwnedByLifetimeScope ---> No constructors on type
'Tracker.Managers.SqliteManager' can be found with the constructor
finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'.
but sqlitemanager has a constructor!
The ViewModel (set via AutoWireViewModel="True") of my Shell / MainWindow requests a dependency which gets loaded in a module at startup using the ConfigurationModuleCatalog.
Because the Shell is initialzed before the modules, the DI container obviously can't resolve it, so the application crashes.
public class MainWindowViewModel : BindableBase
{
// Cannot resolve IService
public MainWindowViewModel(IService service)
{
}
}
I already tried the two approaches of this post, but both didn't worked.
I tried it this way:
public interface IShellService
{
int NumberOfLoadedModules { get; }
void FlagModuleAsLoaded();
}
public class ShellService : IShellService
{
private readonly IModuleCatalog _moduleCatalog;
public ShellService(IModuleCatalog moduleCatalog)
{
_moduleCatalog = moduleCatalog;
}
public int NumberOfLoadedModules { get; private set; }
public void FlagModuleAsLoaded()
{
NumberOfLoadedModules++;
if (NumberOfLoadedModules != _moduleCatalog.Modules.Count())
return;
InitializeShell();
}
private static void InitializeShell()
{
Application.Current.MainWindow.Show();
}
}
internal class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return null;
}
protected override void InitializeShell()
{
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterInstance<IShellService>(new ShellService(ModuleCatalog), new ContainerControlledLifetimeManager());
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
Usage
public abstract class ModuleBase : IModule
{
private readonly IShellService _shellService;
protected ModuleBase(IShellService shellService)
{
_shellService = shellService;
}
public void Initialize()
{
InitializeInternal();
FlagAsLoaded();
}
public abstract void InitializeInternal();
public void FlagAsLoaded()
{
_shellService.FlagModuleAsLoaded();
}
}
public class FooModule : ModuleBase
{
IUnityContainer _container;
public MusicUIModule(IUnityContainer container, IShellService shellService) : base(shellService)
{
_container = container;
}
public override void InitializeInternal()
{
_container.RegisterType<IService, Service>();
}
}
It starts counting the modules and then the application crashes because of the same reason.
If the approach above isn't fitting for my purpose, how could that problem be solved?
Thanks!
I tried to implement Haukinger's suggestion and did it this way which works just fine:
// Factory --------------------------------------------------------
public interface IDependencyFactory
{
IService GetService();
}
public class DependencyFactory : IDependencyFactory
{
private readonly IUnityContainer _container;
public DependencyFactory(IUnityContainer container)
{
_container = container;
}
public IService GetService()
{
return _container.Resolve<IService>();
}
}
// PubSubEvent ------------------------------------------------------
public class AllModulesLoaded : PubSubEvent
{
}
// Bootstrapper -----------------------------------------------------
internal class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void InitializeModules()
{
base.InitializeModules();
// Publishing event to tell subscribers that the modules are loaded
var eventAggregator = Container.Resolve<IEventAggregator>();
eventAggregator?.GetEvent<AllModulesLoaded>().Publish();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
// ...
Container.RegisterType<IDependencyFactory, DependencyFactory>();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new ConfigurationModuleCatalog();
}
}
// ViewModel ---------------------------------------------------------
public class MainWindowViewModel : BindableBase
{
private IService _service;
private readonly IEventAggregator _eventAggregator;
private readonly IDependencyFactory _dependencyFactory;
public MainWindowViewModel(IEventAggregator eventAggregator, IDependencyFactory dependencyFactory)
{
_eventAggregator = eventAggregator;
_dependencyFactory = dependencyFactory;
_eventAggregator.GetEvent<AllModulesLoaded>().Subscribe(OnAllModulesLoaded);
}
private void OnAllModulesLoaded()
{
var service = _dependencyFactory.GetService();
if (service != null)
_service = service ;
_eventAggregator.GetEvent<AllModulesLoaded>().Unsubscribe(OnAllModulesLoaded);
}
}
I'd hide the dependency of the shell behind a factory/provider and then create/fetch it when the last module is loaded. Your shell's view model subscribes to a AllModulesLoaded event that's fired from your bootstrapper's InitializeModules when base.InitializeModules returns to get notified that the dependency is available. Or the factory/provider subscribes to the event and the shell polls it, depending on how you want to use the dependency.
My Setup:
Visual Studio 2013
Web Forms/MVC project
C#
Ninject 3.2.0.0
Entity Framework
I have a Web Forms/MVC hybrid project that uses Ninject for its IoC containter. I've no problems with Ninject until today. The problem I ran into is that I can't get Ninject to new up some objects whenever I use a class. Here is some code that works:
// Master1.master
namespace TestCode
{
public partial class Master1 : MasterPage
{
[Inject]
public FooController Foo { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
// Do some setup logic.
Foo.Bar();
}
}
}
Now here is some code that doesn't work using a class:
// Master1.master
namespace TestCode
{
public partial class Master1 : MasterPage
{
protected void Page_Load(object sender, EventArgs e)
{
new Wrapper().SomeMethod();
}
}
}
// Wrapper.cs
namespace TestCode
{
public class Wrapper
{
[Inject]
public FooController Foo { get; set; }
public void SomeMethod()
{
// Do some setup logic.
Foo.Bar();
}
}
}
My problem is that when I execute SomeMethod(), Foo is null. Why is this and what can I do to get Ninject to new up Foo?
Okay - I got it working now. Thanks everyone! I needed to add a binding my NinjectWebCommon class like so:
public static class NinjectWebCommon
{
private static readonly Bootstrapper Bootstrapper = new Bootstrapper();
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
Bootstrapper.Initialize(CreateKernel);
}
public static void Stop()
{
Bootstrapper.ShutDown();
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Needed to add this binding.
kernel.Bind<IWraper>().To<Wraper>().InRequestScope();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
}
I am trying to implement Dependency injection in my Global.asax file. Below is the code-
public class MvcApplication : System.Web.HttpApplication
{
private readonly IJobTimerBS _objTimer;
// want to replace this constructor injection code. How ????
//public MvcApplication(IJobTimerBS jobTimerBS)
//{
// _objTimer = jobTimerBS;
//}
protected void Application_Start()
{
UnityConfig.RegisterComponents();
processingTimer_Elapsed();
}
void processingTimer_Elapsed()
{
_objTimer.QuoteDeleteJobTimer();
}
}
Code in Business layer-
public class JobTimerBS : IJobTimerBS
{
private readonly IUnitofWork _unitOfWork;
private IJobTimerRepository _jobTimerRepository;
public JobTimerBS(IJobTimerRepository jobTimerRepository, IUnitofWork unitOfWork)
{
_unitOfWork = unitOfWork;
_jobTimerRepository = jobTimerRepository;
_jobTimerRepository.UnitOfWork = unitOfWork;
}
public void QuoteDeleteJobTimer()
{
_jobTimerRepository.JobTimer();
}
}
My question is how can I implement the dependency injection in this case when I can't use constructor injection. How to instantiate the JobTimerBS class in Global.asax present in Business layer.