I am currently learning Catel+Orchestra using MahApps Metro.
I am doing the Authentication example from the Catel.Examples project using the MetroUI.
My problem is when i create a new MainWindow in my MahAppsService
public FrameworkElement GetMainView()
{
return new MainWindow();
}
The constructor of the MainWindowViewModel is never called
public MainWindowViewModel(UIVisualizerService uiVisualizarService, IAuthenticationProvider authenticationProvider)
{
_uiVisualizerService = uiVisualizarService;
_authenticationProvider = authenticationProvider;
RoleCollection = new ObservableCollection<string>(new[] { "Read-Only", "Administrator" });
ShowView = new Command(OnShowViewExecute, OnShowViewCanExecute, "ShowView");
}
I have narrowed it down to the 2 dependencies of the constructor. If i remove the UIVisualizerService and IAuthenticacionProvider dependencies the constructor is properly called but the ModelView needs those two services later on.
I am lost at what can i do to get this working.
You must register the IAuthenticationProvider in the ServiceLocator:
var serviceLocator = ServiceLocator.Default;
serviceLocator.RegisterType<IAuthenticationProvider, MyAuthenticationProvider>();
Note that all services inside Catel are automatically registered for you, but you must register your own services yourself (for example, by using ModuleInit or another entry point in your assembly).
I solved the problem by adding a explicit injection of the viewmodel into the mainwindow constructor.
public MainWindow(MainWindowViewModel _mainwindowviewmodel):base(_mainwindowviewmodel)
{
InitializeComponent();
}
Declaring the field for the AuthenticationProvider interface to the MahAppsService class.
private readonly IAuthenticationProvider _authenticationProvider;
Also adding the dependency of the AuthenticationProvider interface to the constructor.
public MahAppsService(ICommandManager commandManager, IMessageService messageService, IUIVisualizerService uiVisualizerService, IAuthenticationProvider authenticationProvicer)
{
Argument.IsNotNull(() => commandManager);
Argument.IsNotNull(() => messageService);
Argument.IsNotNull(() => uiVisualizerService);
Argument.IsNotNull(() => authenticationProvicer);
_commandManager = commandManager;
_messageService = messageService;
_uiVisualizerService = uiVisualizerService;
_authenticationProvider = authenticationProvicer;
}
And the last step is creating an instance of the viewmodel in the GetMainView in the MahAppsService class.
public FrameworkElement GetMainView()
{
var mainwindowViewModel = TypeFactory.Default.CreateInstanceWithParametersAndAutoCompletion<MainWindowViewModel>(_uiVisualizerService, _authenticationProvider);
return new MainWindow(mainwindowViewModel);
}
Please note that this might not be the best way to do it but it gets the work done. If someone has better way feel free to share it.
Related
i'm trying to develop from scratch a WPF app with the use of Simpleinjector as a IOC container.
I'm new on this topic and i have some issue regards lifetime of object and hot use them correctly.
I started the app by following the WPF integration guide on simpleinjector manual.
But i don't understand how to receive a new instance every time a service needed it
As i ask in my previous post i need to receive a new unitOfWork every time a service need it.
as #Steven say on my previous post
Do note that transient means "allways a new instance is resolved when it is requested from the container." If you're not requesting it again, you will be operating on the same instance, which might explain the ObjectDisposedException.
In the other post i found a solutin but i think it's a little bit over-complicated and it's to create a factory and inject this instead of the instance because i want to call the container.getInstance only on the startup method and not on the service by passing the container as a dependency
It's the only way i have to achieve this or there is something that i don't understand on how to develop in DI way?
Example of code:
public class HeaderViewModelFactory : IWpfRadDispenserViewModelFactory<HeaderviewModel>
{
private readonly ProductionService _service;
public HeaderViewModelFactory(ProductionService service)
{
_service = service;
}
public HeaderviewModel CreateViewModel()
{
return new HeaderviewModel(_service);
}
}
public class HeaderviewModel : ViewModelBase
{
private readonly ProductionService _service;
public HeaderviewModel(ProductionService service)
{
_service = service;
CreateData();
}
private void CreateData()
{
_service.CreateTestCycle();
}
}
public class CycleService : GenericDataService<Cycle>
{
private readonly IUnitOfWork<WpfRadDispenserDbContext> _uowContext;
public CycleService(IUnitOfWork<WpfRadDispenserDbContext> uowContext)
: base(uowContext)
{
_uowContext = uowContext;
}
public void CreateTestCycle()
{
var cycleDataService = new GenericDataService<Cycle>(_uowContext);
var vialDataService = new GenericDataService<Vial>(_uowContext);
Cycle c = new Cycle();
c.BatchName = "test";
Vial v = new Vial();
v.Name = "Test Vial";
c.Vials.Add(v);
_uowContext.CreateTransaction(IsolationLevel.ReadCommitted);
try
{
vialDataService.Create(v);
_uowContext.Persist();
var list = vialDataService.GetAll();
cycleDataService.Create(c);
_uowContext.Persist();
_uowContext.Commit();
}
catch (Exception e)
{
Console.WriteLine(e);
_uowContext.RollBack();
throw;
}
finally
{
_uowContext.Dispose();
}
}
}
private static Container Bootstrap()
{
// Create the container as usual.
var container = new Container();
// Register your types:
// Register your windows and view models:
container.Register<WpfRadDispenserDbContextFactory>(Lifestyle.Transient);
container.Register<IUnitOfWork<WpfRadDispenserDbContext>,WpfRadDispenserUOW>();
container.Register(typeof(CycleService));
container.Register<IWpfRadDispenserViewModelFactory<ProductionViewModel>,
ProductionViewModelFactory>(Lifestyle.Transient);
container.Register<IWpfRadDispenserViewModelFactory<AnagraphicViewModel>,
AnagraphicsViewModelFactory>(Lifestyle.Transient);
container.Register<IWpfRadDispenserViewModelFactory<HeaderviewModel>,
HeaderViewModelFactory>(Lifestyle.Transient);
container.Register<IViewModelAbstractFactory,
ViewModelAbstractFactory>(Lifestyle.Transient);
container.Register<INavigator, Navigator>(Lifestyle.Transient);
container.Register<MainWindowViewModel>();
container.Register<MainWindow>();
//container.Options.EnableAutoVerification = false;
//container.Verify();
return container;
}
in this way every time i create a new viewmodel i receive the same service and ovviously the dbcontext it's not present anymore because disposed.
This is not the rela code but only an example that i made to understand how DI works.
Using Abstract Factory pattern is the most common and recommended approach. Using the container in your application directly is widely considered an anti-pattern, like the Service Locator (Service Locator is an Anti-Pattern) for a very good reason.
Abstract factory allows instantiation of objects without introducing a tight coupling to the actual implementation that knows how to create specific instances.
Most IoC frameworks support this pattern natively. Most of the time they provide the generic interface for the factory. You register the instance (the product) with the container and the framework will export a ready-to use factory for you. You add the dependency to this framework interface to your object e.g. constructor. Then you register the generic factory interface. The framework will automatically create the instance of the factory and inject it into the relevant instances e.g., via constructor.
I am not too familiar with Simple Injector, but the framework really keeps things simple. There is no such code generation.
But the pattern is very simple (that's why this is so easy to automate) and in no way complicated.
Example
The interface required to dynamically create the instances of type TInstance:
interface IFactory<TInstance>
{
TInstance Create();
}
The implementation of this factory:
class SaveItemFactory : IFactory<ISaveItem>
{
ISaveItem Create() => new SaveItem();
}
The type that needs to create a dependency dynamically:
interface IItemManager {}
class ItemManager : IItemManager
{
IFactory<ISaveItem> SaveItemFactory { get; }
public ItemManager(IFactory<ISaveItem> itemFactory) => this.SaveItemFactory = itemFactory;
public void SaveData(object data)
{
ISaveItem saveItem = this.SaveItemFactory.Create();
saveItem.SetData(data);
}
}
Configure the container:
public void Run()
{
var container = new SimpleInjector.Container();
container.Register<IFactory<ISaveItem>, SaveItemFactory>(Lifestyle.Singleton);
container.Register<IItemManager, ItemManager>(Lifestyle.Singleton);
IItemManager itemManager = container.GetInstance<IItemManager>();
itemManager.SaveData("Some Data");
}
It seems that when I resolve a type manually and request the INavigationService in it a different instance is injected than the one being used everywhere else.
To clearify my use-case here are excerpts from the relevant files. As you can see when resolving the type SampleProcess the INavigationService will be injected but the instance is different to the one that I got in ProcessService. (Which btw is the correct instance, that can be used for navigation. The one injected in SampleProcess cannot be used for navigating.)
Any ideas why this is happening and more importantely how I can get the correct instance of INavigationService be injected into SampleProcess. Yes, I could provide it for example by passing it in with a method, but that's not so pretty.
App.xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<ProcessService>();
containerRegistry.Register<Processes.SampleProcess>();
}
ProcessService.cs
public class ProcessService
{
private readonly IContainer container;
private readonly INavigationService navigationService;
public ProcessService(IContainer container, INavigationService navigationService)
{
this.container = container;
this.navigationService = navigationService;
}
public void ExecuteProcess(ProcessEnum processEnumValue)
{
Type processType = processEnumValue switch
{
ProcessEnum.SampleProcess => typeof(Processes.SampleProcess),
_ => throw new NotImplementedException()
};
var process = App.Current.Container.Resolve(processType) as IProcess;
bool test = process.CheckNavigationService(navigationService); // will return false
}
}
SampleProcess.cs
public class SampleProcess : IProcess
{
private readonly INavigationService navigationService;
public SampleProcess(INavigationService navigationService)
{
this.navigationService = navigationService;
}
public bool CheckNavigationService(INavigationService navigationService)
{
return this.navigationService == navigationService;
}
}
Navigation in Prism is based on which page you are Navigating from. For this reason the NavigationService is a transient service. It is constructed special for your ViewModel so that the instance provided for you is aware of which page it will need to Navigate from.
There are several potential solutions for your issue.
Don't inject the INavigationService into another service as it isn't meant to be handled that way.
Provide the INavigationService as a parameter that you pass to whatever service needs to use it
Simply make your ProcessService a transient
Update to Prism 8 and make your Process Service a Scoped Service as we've made a change in Prism 8 that creates a new scope each time a new page is created. This means that any Scoped or Transient services that need access to the NavigationService would be able to inject the same exact instance that is injected into the ViewModel
I fixed the issue by adding an INavigationService parameter to the ExecuteProcess method that gets called from within the viewmodel (that in fact has the correct INavigationService instance). Within the method I then resolve the process by doing the following:
var process = container.Resolve(processType, new[] { navigationService }) as IProcess;
This way the correct INavigationService instance is available within the IProcess instance.
As #Dan Siegel stated this could possibly be further improved by using Prism 8 and registering the ProcessService as a scoped service as this would eliminate the necessity to have a INavigationService parameter in ExecuteProcess.
I've built a small app using MVVM Light, and I've reached a point in which I need to pass parameters between a few different ViewModels in my app. I've explored several different options, but I'm not a huge fan of them really. The most promising I've encountered so far is simply passing messages between the ViewModels, but this is somewhat limiting as the application has the potential to have multiple of the same View open at once, and I need to isolate the parameters to a singular instance of a View/ViewModel.
I'm not currently using the built in INavigationService provided by MVVM Light, but I've made one incredibly similar (and if I can solve the parameter injection, I'll likely switch).
Here is a trimmed down version of my navigation service:
public class NavigationService : INavigationService
{
/* this implementation will not allow us to have the same window open
more than once. However, for this application, that should be sufficient.
*/
public NavigationService()
{
_openPages = new Dictionary<string, Window>();
}
private readonly Dictionary<string, Window> _openPages;
public void ClosePage(string pageKey)
{
if (!_openPages.ContainsKey(pageKey)) return;
var window = _openPages[pageKey];
window.Close();
_openPages.Remove(pageKey);
}
public IEnumerable<string> OpenPages => _openPages.Keys;
public void NavigateTo(string pageKey)
{
if (!AllPages.ContainsKey(pageKey))
throw new InvalidPageException(pageKey);
// Don't re-open a window that's already open
if (_openPages.ContainsKey(pageKey))
{
_openPages[pageKey].Activate();
return;
}
var page = (Window) Activator.CreateInstance(AllPages[pageKey]);
page.Show();
page.Closed += OnWindowClosedHandler;
_openPages.Add(pageKey, page);
}
// Probably a better way to remove this.
private void OnWindowClosedHandler(object sender, EventArgs args)
{
foreach (var item in _openPages.Where(kvp => kvp.Value == sender).ToList())
{
_openPages.Remove(item.Key);
}
}
// Reflection might work for this.
// Might also consider making this more dynamic so it isn't hard-coded into my service
private readonly Dictionary<string, Type> AllPages = new Dictionary<string, Type>
{
["AddPatientView"] = typeof(AddPatientView),
["CheckInView"] = typeof(CheckInView),
["MainView"] = typeof(MainWindow),
["PatientLookupView"] = typeof(PatientLookupView),
["PatientDetailsView"] = typeof(PatientDetailsView)
};
}
Most of my ViewModels use dependency injection to wire-up other injected services, like so:
public class CheckInViewModel : ViewModelBase
{
public CheckInViewModel(ILicenseValidationService licenseValidationService,
IPatientFetchService patientFetchService,
IPatientCheckInService patientCheckInService)
{
if (IsInDesignMode)
{
Title = "Find Member (Design)";
}
else
{
Title = "Find Member";
CanFetch = true;
FindMemberCommand = new RelayCommand(async () => await FindMemberHandler(), () => CanFetch);
CheckInPatientCommand = new RelayCommand<Window>(async (window) => await CheckInPatientHandler(window),
(window) => Patient?.PatientId != null);
_licenseValidationService = licenseValidationService;
_patientFetchService = patientFetchService;
_patientCheckInService = patientCheckInService;
}
}
}
I would like to implement some method of injecting other parameters alongside my injected services. Has anything like this been done in a relatively straightforward way?
The way dependency injection works in almost all cases is when you resolve or getinstance a type that then will use the constructor with the most parameters in providing you with an object.
If you register a concrete object against an interface ( or just a type ) then later resolve/getinstance a class which uses one of those things in it's ctor then DI provides that instance you registered.
With MVVMLight you have SimpleIoc and SimpleIoc.Default is equivalent to that static service you're thinking about.
There is a catch with simpleioc. It's very simple.
With simpleioc once you getinstance a viewmodel of a given type then that is a singleton. You can force a different instance by passing a unique key but they're all cached. You can getinstance with parameters and maybe that replaces the current object. I'm not sure offhand. A more sophisticated DI container might be advisable.
Other than that.
Since you're using different windows this creates a bit of a complication in that you want to instantiate a window and that will have a datacontext you need to provide somehow with your parameters.
What you could use is viewmodel first.
You get inavigationservice out DI or resources or a static.
You have a DoWindow(Object vm) method.
When you want to navigate you presumably know the parameters for the vm. New up your viewmodel with parameters. New up a window you use for all views. Set it's content to your viewmodel. That is templated out into what you have as windows now. Except you make them usercontrols. Use Datatype="vmtype" to associate view as template with viewmodel. Bind the title of your window to Content.Title and of course add a Title property to a base viewmodel.
ALternatively with a single window app you can have a contentcontrol fills the area yor views will be shown in. Bind the content of that to a currentviewmodel property and you can use viewmodel first navigation within that window.
My ExampleViewModel has a parameter (a model) that it needs to function properly. I also have a list variant of the ExampleViewModel that just contains a ObservableCollection<ExampleViewModel> and has some wrapper methods.
The issue shows it self when I try to use dependency injection with MVVM Light. Let me explain the scenario with some code.
default ViewModelLocator:
public class ViewModelLocator {
public ViewModelLocator() {
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic) {
SimpleIoc.Default.Register<IDataService, DesignDataService>();
} else {
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main {
get {
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup() {}
}
Simple MainViewModel:
public class MainViewModel : ViewModelBase {
private IDataService _service;
private MainModel _model;
// Goal. I think?
public MainViewModel(IDataService service, MainModel model) {
_service = service;
_model = model;
}
// Used now
public MainViewModel(MainModel model) {
_service = ServiceLocator.Current.GetInstance<IDataService>();
_model = model;
}
}
And the ListMainViewModel fills it's list like this:
Mains = new ObservableCollection<MainViewModel>(
_service.GetAll().Select(model => new MainViewModel(model))
);
Because of the list creation in the ListMainViewModel I can't inject the data source in the constructor and have to use it as shown under // Used now. Which is fine, but when I try to unit test problems occur, as you can probably imagine.
I use a unit test like this at the moment:
public void ListMainViewModel_DoSomething_Success() {
// Arrange
var mock = new Mock<IDataService>();
mock.Setup(m => m.Create(new MainModel { Naam = "Create" }));
mock.Setup(m => m.Update(new MainModel { Naam = "Update" }));
var listMainViewModel = new ListMainViewModel(mock.Object);
var selected = new MainModel() { Prop = "Test" };
listMainViewModel.SelectedMainViewModel = new MainViewModel(selected);
// Act
listMainViewModel.DoSomething();
// Assert
mock.Verify(x => listMainViewModel.DoSomething(), Times.Exactly(1));
}
Note: Not sure if the unit test is correct. But the problem occurs while creating the SelectedMainViewModel because it doesn't fetch a DataService.
For now I have two choices (I think), I can make the MainModel in MainViewModel optional, which is not something that should be possible and contructor inject the IDataService. Or I can discover a way to make the _service find-able by the MainViewModel.
Is there any elegant way to solve this? Because I have the feeling I have to make some hack using if (InDesignMode) or something similar.
You mention that:
I can't inject the data source in the constructor and have to use it as shown under // Used now.
What are you seeing when you try this? What exception is thrown? Why doesn't the below work?
Mains = new ObservableCollection<MainViewModel>(
_service.GetAll().Select(model => new MainViewModel(model, _service))
);
I think it may help if you get away from using a service locator, which looks to be an anti-pattern for your goal. When using a DI container, it's best to create the entire object graph at the application start or web request (for web apps). But here you are using the new keyword to create your MainViewModel.
See this post.
There is a possibility I'm not understanding how it's supposed to work.
Where I start my app I do this:
IUnityContainer container = new UnityContainer();
container.RegisterInstance<IUnityContainer>(container);
//MainWindow
container.RegisterType<Window, MainWindow>();
//Services
container.RegisterType<IWindowManager, WindowManager>();
//Workspaces
container.RegisterType<WorkspaceViewModel, CompanyWorkspace>("Company");
container.RegisterType<WorkspaceViewModel, DivisionWorkspace>("Division")
//More of this
container.RegisterType<IWorkspaceFactory, WorkspaceFactory>();
Window window = container.Resolve<Window>();
window.DataContext = container.Resolve<ViewModel.MainWindowViewModel>();
window.Show();
My MainWindowViewModel gets resolved and here is it's constructor
public MainWindowViewModel(IWorkspaceFactory workspaceFactory, IWindowManager windowManager)
{
_workspaceFactory = workspaceFactory;
_windowManager = windowManager;
_windowManager.Changed += new EventHandler(DialogChanged);
ControlPanel = new ListCommandsViewModel();
foreach (string s in _workspaceFactory.GetWorkspaceList())
{
ControlPanel.List.Add(new CommandViewModel(s, new RelayCommand<string>(OpenWorkspace)));
}
}
Notice that I subscribe to a event in the windowManager. WorkspaceFactory and WindowManager should are resolved here by Unity so instances of them are created.
Here is a implmentation of IWorkspaceFactory:
public class WorkspaceFactory : IWorkspaceFactory
{
private IUnityContainer _container;
public WorkspaceFactory(IUnityContainer container)
{
_container = container;
}
public ViewModel.WorkspaceViewModel GetWorkspace(string workspace)
{
return _container.Resolve<WorkspaceViewModel>(workspace);
}
public ICollection<string> GetWorkspaceList()
{
return _container.Registrations.Where(r => r.RegisteredType == typeof(WorkspaceViewModel)).Select(r => r.Name).ToList();
}
}
As I registered the original container as a instance it should be what is passed into the factory. So I'm letting the same Container resolve the workspace that grabs IWindowsManager as a ctro parameter. So it should be getting the sama instance as the MainWindowViewModel got right?
But if I fire off the event from inside the workspace the MainView never gets notified, in actuality the Changed event is empty like this is a seperate instance of IWindowManager.
How may that be?
Am I totally off, I was under the impression that if you don't define a LifeTime for types in containers you alwasy get the same instance.
Sorry, but I think you are off - if Unity is like AutoFac then the default behaviour will be "new instance per request".
This is certainly what the docs look like "It will create a new instance of the registered, mapped, or requested type each time" - see http://msdn.microsoft.com/en-us/library/cc440953.aspx
To correct this, provide a LifetimeManager when you Register the type - e.g. ContainerControlledLifetimeManager (see http://msdn.microsoft.com/en-us/library/cc440953.aspx)
By default, Unity will resolve a new instance for registered types, so you need to register WorkspaceViewModel with a different scope. In addition, it's a bad idea to inject the container instead of the real dependencies since it makes it difficult for the client to know what those are.