Search code examples
wpfunity-containerprism

How to inject new instance at run time in Prism?


I'm using Prism 7.1 for WPF application. I tried to use built-in IOC(Unity) but I don't know how I can resolve new instance at run time maybe from method or from containerProvider. But it seems IContainerProvider is not available to be injected in my view model. Do I have any option?

I want to do this to reload my database context. I was told that creating new context is the easiest among others.

    public class ProjectViewModel : BindableBase
    {
        private UnitOfWork unitOfWork;
        private IContainerProvider containerProvider;
        private IEventAggregator eventAggregator;
        #region Constructor
        public ProjectViewModel(UnitOfWork unitOfWork, IContainerProvider cp, IEventAggregator ea)
        {
            this.unitOfWork = unitOfWork;
            containerProvider = cp;
            eventAggregator = ea;
            eventAggregator.GetEvent<SendReloadDataEvents>().Subscribe(Reload);
            Reload();
        }
        #endregion

        private void Reload()
        {
            this.unitOfWork = containerProvider.Resolve<UnitOfWork>();
            Projects = new ObservableCollection<Projects>(unitOfWork.ProjectRepo.GetAll());
            Customers = new ObservableCollection<Customers>(unitOfWork.CustomerRepo.Find(x => x.Projects.Count > 0));
            SelectedProjectType = null;
        }
//Other logic continues
}

Solution

  • Injecting the container is an anti-pattern, so we'll try to avoid that and inject a factory instead. In fact, IContainerProvider and IContainerRegistry aren't registered themselves for this reason, to make it impossible to inject them.

    A factory is easy though:

    public class ProjectViewModel : BindableBase
    {
        private UnitOfWork unitOfWork;
        private Func<UnitOfWork> unitOfWorkFactory;
        private IEventAggregator eventAggregator;
        #region Constructor
        public ProjectViewModel(Func<UnitOfWork> unitOfWorkFactory, IEventAggregator ea) // no need to inject a unit of work
        {
            _unitOfWorkFactory = unitOfWorkFactory;
            eventAggregator = ea;
            ea.GetEvent<SendReloadDataEvents>().Subscribe(Reload); // prefer the parameter
            Reload();
        }
        #endregion
    
        private void Reload()
        {
            this.unitOfWork = _unitOfWorkFactory(); // create a new one here (the container does the work but is hidden)
            Projects = new ObservableCollection<Projects>(unitOfWork.ProjectRepo.GetAll()); // I'd rather update the existing collection
            Customers = new ObservableCollection<Customers>(unitOfWork.CustomerRepo.Find(x => x.Projects.Count > 0)); // same here, or probably, it could just be IEnumerable
            SelectedProjectType = null;
        }
    //Other logic continues
    }
    

    Note that this Func<> factory is ok only for the most simple cases. If you need parameters passed into the product constructor, you have to code it yourself as described in this answer. Also note how the container never shows up anywhere (except where it's configured), making your code dependent on the container is just unnecessary and will make tests more ugly.