Instantiate IUnitOfWork for each ViewModels in WPF/Prism app - wpf

I have lots of repositories like this:
public class PersonRepository : IPersonRepository
{
private readonly IUnitOfWork _unitOfWork;
public PersonRepository(IUnitOfWork instance)
{
_unitOfWork = instance;
}
//Remove, Get methods...
public void Add(Person p)
{
_unitOfWork.Context.People.Add(p);
}
}
and Unit of work class like this:
public class UnitOfWork : IUnitOfWork, IDisposable
{
public UnitOfWork(){ }
private readonly HezarehContext _context = new HezarehContext();
public HezarehContext Context
{
get
{
return _context;
}
}
public int Save()
{
return _context.SaveChanges();
}
public void Initialize()
{
Context.Database.Initialize(false);
}
#region IDisposable Members
public void Dispose()
{
_context.Dispose();
}
#endregion
}
Now i want each time my ViewModels gets resolved, a new IUnitOfWork instantiated. Most of my ViewModels are like this:
public class PeopleMainViewModel : NotificationObject
{
// both of repositories must have same instance of IUnitOfWork
private readonly IPersonRepository _personRepository = ServiceLocator.Current.GetService<IPersonRepository>();
private readonly ICategoryRepository _categoryRepository = ServiceLocator.Current.GetService<ICategoryRepository>();
public PeopleMainViewModel()
{
InitializeView();
}
// add, edit, remove commands ...
}
ViewModels always gets resolved using Unity Container like this:
Container.RegisterType<IPersonRepository, PersonRepository>();
// resolve in InjectionProperty...
Container.RegisterType<Object, PeopleMainView>("PeopleMainView", new InjectionProperty(PeopleMainView.DataContextProperty.Name, Container.Resolve<PeopleMainViewModel>();
And my question is, How and Where i Register my ViewModels and IUnitOfWork to have IUnitOfWork instance for each of them?

If I understand your question, just register your IUnitOfWork the same way (and same place) you register the repository in your above example. You don't need to register your ViewModels based on your current design since you aren't using an Interface.
Container.RegisterType<IUnitOfWork, UnitOfWork>();
And continue to have your repositories accept the IUnitOfWork in the constructor. This will allow Unity to use constructor injection to provide a new instance of IUnitOfWork each time it resolves a repository. By default, you'll get a new instance of the IUnitOfWork each time. If you'd like to have a singleton IUnitOfWork, you would have to say so when you register the IUnitOfWork like this:
Container.RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager());
If you want to read up on Lifetime Managers, you can do so here.
I would also recommend changing your ViewModels to take the repositories in as Constructor Parameters, like this if you are going to Resolve them (so Unity will do the work without you referencing the ServiceLocator directly)
public PeopleMainViewModel(IPersonRepository personRepo, ICategoryRepository categoryRepo)
{
...
}

Update:
There is another solution here in unity.codeplex discussions.
I finally found a solution.
There is a feature in Unity container that let you pass parameters while resolving a Type. by changing constructor of ViewModels to this:
public class PeopleMainViewModel : NotificationObject
{
private readonly IPersonRepository _personRepository = null;
private readonly ICategoryRepository _categoryRepository = null;
public PeopleMainViewModel(IUnityContainer container, IUnitOfWork unitOfWork)
{
// now both of repositories have same instance of IUnitOfWork
_personRepository = container.Resolve<IPersonRepository>(new ParameterOverride("unitOfWork", unitOfWork));
_categoryRepository = container.Resolve<ICategoryRepository>(new ParameterOverride("unitOfWork", unitOfWork));
InitializeView();
}
// add, edit, remove commands ...
}
problem solved. now _personReposiotry and _categoryRepository have reference to same instance of unitOfWork.

Related

In which window should I instantiate my object?

My WPF application has multiple windows:
MainWindow
ChildWindow1
Childwindow2
I instantiate the children from MainWindow by...
public partial class MainWindow : Window
{
ChildWindow1 CW1;
ChildWindow2 CW2;
public MainWindow()
{
InitializeComponent();
}
private void btnButton1_Click(object sender, RoutedEventArgs e)
{
CW1 = new ChildWindow1();
CW1.Show();
CW1.Owner = this;
CW2 = new ChildWindow2();
CW2.Show();
CW2.Owner = this;
}
}
Awesome!
But here's where I run into trouble: I have a non-UI class that I need to instantiate.
public class MyClass
{
public void DoSomething()
{
//Do something here
}
}
The object of that class should "live" inside ChildWindow1, because that's where it logically belongs. So, I instantiate it inside ChildWindow1 like so:
public partial class ChildWindow1 : Window
{
public MyClass MyObject = new MyClass();
public ChildWindow1()
{
InitializeComponent();
}
}
But eventually comes a point where ChildWindow2 needs to interact with the object in ChildWindow1:
Call the 'DoSomething()' method of the object in Childwindow1.
Subscribe to an event raised by the object in ChildWindow1
I can do all that from the main window, because it owns the children, but I want the children to be able to interact directly.
Am I violating a design principle by allowing CW1 <--> CW2 interaction?
How else would you get them to interact if not by calling methods or subscribing to events?
Thanks to all here, for providing this awesome learning resource! Much appreciated!
Make this the instance of MyClass both public and static, to access it from almost anywhere because that should be accessed based on a certain instance of window.
If you wanna access a variable from any window, either make it static or call it based on an instance of that window like:
public partial class ChildWindow1 : Window
{
public static MyClass MyObject = new MyClass();
public ChildWindow1()
{
InitializeComponent();
}
}
access like this CW1.MyObject

Open new window on click in WPF, Ninject and Caliburn.Micro

I'm trying to set up a WPF app to call the new window on a menu click with the data provider interface injected into the new viewmodel.
Followed many tutorials and created the Bootstrapper for Caliburn, a service locator and module for ninject. So far the main view doesn't need the IDataProvider but I'd like to open a new window on click event.
The Bootstrapper:
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainScreenViewModel>();
}
}
The Service Locator and Module:
public class ServiceLocator
{
private readonly IKernel _kernel;
public ServiceLocator()
{
_kernel = new StandardKernel(new ServiceModule());
}
public MainScreenViewModel MainScreenViewModel => _kernel.Get<MainScreenViewModel>();
public NewLayoutViewModel NewLayoutViewModel => _kernel.Get<NewLayoutViewModel>();
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ISqlite>().To<Sqlite>();
Bind<IDataProvider>().To<DataProvider>();
}
}
And this is where I got stuck:
public class MainScreenViewModel : Conductor<object>
{
private IWindowManager _windowManager;
public MainScreenViewModel()
{
_windowManager = new WindowManager();
}
public void NewLayout()
{
_windowManager.ShowWindow(new NewLayoutViewModel());
}
}
since the NewLayoutViewModel requires the IDataProvider.
Not sure, what am I missing, but in my understanding Ninject should take care of this di for NewLayoutViewModel.
Found a good solution from Tim Corey on YouTube.
Basically the answer is, if you not insist Ninjet, use Caliburn.Micro's build-in DI solution "SimpleContainer".

How the Unity container will resolve the registered type

Recently I am going through some old code and found the below code
public class ProfileModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
private IEventAggregator eventAggregator;
public ProfileModule(IUnityContainer c, IRegionManager r, IEventAggregator e)
{
container = c;
regionManager = r;
eventAggregator = e;
}
public void Initialize()
{
// Create and add profiles as new Tab items
container.RegisterType<IProfileViewModel, Profile1ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());// HomeRegion is of type TabControl
container.RegisterType<IProfileViewModel, Profile2ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());
container.RegisterType<IProfileViewModel, Profile3ViewModel>(new ContainerControlledLifetimeManager());
regionManager.Regions[RegionNames.HomeRegion].Add(container.Resolve<ProfileView>());
}
}
Below is the ProfileView.xaml.cs
public partial class ProfileView : INotifyPropertyChanged
{
[InjectionConstructor]
public ProfileView(IProfileViewModel vm)
{
DataContext = vm;
InitializeComponent();
}
}
Below are the viewModels
public abstract class ProfileViewModelBase : IProfileViewModel, IDataErrorInfo, INotifyPropertyChanged
{
public ProfileViewModelBase(IEventAggregator eventAggregator, IRegionManager regionManager)
{
}
}
public class Profile1ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile1ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
public class Profile2ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile2ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
public class Profile3ViewModel : ProfileViewModelBase
{
[InjectionConstructor]
public Profile3ViewModel(IEventAggregator eventAggregator, IRegionManager regionManager)
: base (eventAggregator, regionManager)
{
}
}
The part of the code that is not clear for me is the ProfileModule.Initialise().
Everytime when the region manager is adding a view a new new instance of ProfileView is getting created and the viewModel is the one that is registered last.
First time ProfileView is created with Profile1ViewModel as a Datacontext.
Second time ProfileView is created with Profile2ViewModel as a Datacontext.
Third time ProfileView is created with Profile3ViewModel as a Datacontext.
How the container knows exactly which viewmodel to create when creating the view.
Also I understand , container.Resolve will return the view if it already got one, first time view is created and returned, second time I except same view will be returned, but a new view is created. same with third.
Can anyone explain what is happening?
Here goes:
What you can see inside the Initialize method is that after registering the IProfileViewModel the code is then immediately calling Resolve<ProfileView> which on the first Resolve is providing Profile1ViewModel to the ProfileView constructor. Then the second Register replaces the first registration with Profile2ViewModel. Therefore subsequent calls to Resolve will never give you an instance (or the singleton instance) of Profile1ViewModel.
If for some reason you really want to resolve the same instance of ProfileView then you need to Register this with the Unity container as a singleton like the below.
container.RegisterType(new ContainerControlledLifetimeManager());
This is obviously assuming you have an interface defined called IProfileView

Unity Bootstrapper my configure container does not work when call a View Model [duplicate]

This question already has answers here:
Prism 6 with Unity - resolving view models for views without naming convention
(2 answers)
Closed 6 years ago.
I use prism 6.1. I set up the Unity configuration in the method ConfigureContainer of Bootstrapper class. But, when the Prism framework try to call the View Model, it can't create this, and throw the exception 'No parameterless constructor defined for this object.'.
Bootstrapper.cs
public class Bootstrapper: UnityBootstrapper
{
...
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.Container.RegisterType<IMyService, MyService>(new ContainerControlledLifetimeManager());
this.Container.RegisterType<MyFormViewModel>(new ContainerControlledLifetimeManager());
}
...
}
MyFormModule.cs
public class MyFormModule : IModule
{
private readonly IRegionViewRegistry regionViewRegistry;
public SkypeActionModule(IRegionViewRegistry registry)
{
this.regionViewRegistry = registry;
}
public void Initialize()
{
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.MyFormView));
}
}
MyFormViewModel.cs
public class MyFormViewModel : BindableBase
{
private readonly IMyService myService;
public SkypeActionViewModel(IMyService myService)
{
this.myService = myService;
}
...
}
In this line throw the exception:
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.MyFormView));
In the prism documentation explains how to configurate unity, but this is in the Module class (in my case is MyFormModule). I don't understand that because in this way, I need to configurate that in each module, and I haven't the object this.container.RegisterType in my module class.
In others links, I found some configuration similar to "MEF" configuration, where the "DI" configuration are in the ConfigureContainer method. But it doesn't work for my, or something missing in my configuration.
Edit
I include the container in my module class. But I have the same problem. I suppose that is normal, because the problem is when Prism create the view model class MyFormViewModel.
public class MyFormModule : IModule
{
private readonly IRegionViewRegistry regionViewRegistry;
private readonly IUnityContainer container;
public MyFormModule(IRegionViewRegistry registry, IUnityContainer container)
{
this.regionViewRegistry = registry;
this.container = container;
}
public void Initialize()
{
this.container.RegisterType<IMyService, MyService>(new ContainerControlledLifetimeManager());
this.container.RegisterType<MyFormViewModel>();
this.container.RegisterType<MyFormView>();
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(MyFormView));
}
}
Edit
Solution: I found the solution in this question on stackoverflow
Create the method BindViewModelToView on Bootstrap class:
public void BindViewModelToView<TViewModel, TView>()
{
ViewModelLocationProvider.Register(typeof(TView).ToString(), () => Container.Resolve<TViewModel>());
}
and then, in the method ConfigureViewModelLocator in Bootstrap class call all view models it to bind with views:
protected override void ConfigureViewModelLocator()
{
BindViewModelToView<ViewAVM, ViewA>();
BindViewModelToView<ViewAVM, ViewB>();
}
How do you tell prism to create the view model? Are you using ViewModelLocator.AutoWireViewModel="True"?
If so, you want to do something like
ViewModelLocationProvider.SetDefaultViewModelFactory( type =>
{
return Container.Resolve(type);
});
in the bootstrapper to make sure that your container is used for resolving the view model...

Prism v4, MEF service

I have a WPF windows application that uses the ms ribbon control for the menu. In my infrastructure project I want to have a shared service that will be referenced in all modules. Each module will then use that service to define what menu items should be displayed for the module.
I read this Prism+MEF: delayed a service export from prism-module but can't get my other modules to recognize the service.
The service
namespace Infrastructure
{
[ModuleExport("InfModule", typeof(InfModule), InitializationMode = InitializationMode.WhenAvailable)]
[PartCreationPolicy(CreationPolicy.Shared)]
public class InfModule : IModule
{
[Export(typeof(IMenuService))]
public IMenuService MenuService { get; private set; }
public void Initialize()
{
MenuService = new MenuService();
MenuService.AddItem("test");
}
}
}
The module
namespace Classic
{
[ModuleExport("Classic", typeof(Classic), InitializationMode = InitializationMode.WhenAvailable)]
[ModuleDependency("InfModule")]
public class Classic : IModule
{
private IRegionManager _regionManager;
[Import(typeof(IMenuService))]
private IMenuService menuService { get; set; }
[ImportingConstructor]
public Classic(IRegionManager regionManager)
{
this._regionManager = regionManager;
// This shows as true
Debug.WriteLine(menuService == null);
}
public void Initialize()
{
_regionManager.RegisterViewWithRegion("RibbonRegion", typeof(Views.RibbonTabMenu));
// This shows as true
Debug.WriteLine(menuService == null);
}
}
}
I would have expected one of the debug lines to output as false since its imported. Any idea's what I'm missing?
Property imports will never be set while running the constructor, since you can't set properties on an object until it's constructed.
The other problem is that in InfModule, you are setting the exported value too late. MEF only looks at the value for an export once, after that it caches the value and doesn't call the getter again. In this case it is getting the export before Initialize() is called. The logic to set the export needs to either run from the constructor or from code in the property getter.

Resources