I'm building a program on Unity 5 and Prism 7. I want my program to be highly modular so I want it to behave correctly if some modules are unavailable.
For this I want to mark optional dependencies with [OptionalDependency] attribute and let a classes with them to decide themselves what to do if there's null passed in constructor.
But if I mark a dependency with this attribute, Unity doesn't resolve it and just pass null instead of dependency instance, despite the module is available as usual.
How do I make dependency as really nice to have?
Or another option, how do I make Unity to not throw when dependency cannot be resolved, and just pass null instead and let the constructing classes decide what to do?
There's App.xaml.cs of my program:
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
InitializeModules();
this.ShutdownMode = ShutdownMode.OnMainWindowClose;
var shell = Container.Resolve<Shell>();
this.MainWindow = shell;
return shell;
}
protected override void RegisterTypes(IContainerRegistry containerRegistry) { }
/// <summary>
/// Creating catalog of Modules from .dlls in "Modules" folder
/// </summary>
protected override IModuleCatalog CreateModuleCatalog()
{
var catalog = new DirectoryModuleCatalog() { ModulePath = #"./Modules" };
catalog.Initialize();
// by the way, modules aren't getting recognized without
// catalog.Initialize() or InitializeModules in CreateShell.
// Should it be such as that? Seems to me like no..
return catalog;
}
}
Beside main question, I appreciate any suggestions on how to make my code better in the comments under the question, because I'm a noob in Prism+Unity. Thank you!
This approach is not a good idea. Constructor injection means that the dependencies are required. Also, I don't think the OptionalDependencyAttribute works in the ctor, but rather must be applied to a property. Try making a property and applying the attribute to it.
All the code in the CreateShell method is not needed. Just return Container.Resolve<Shell>() and that's it.
Also, the modules not loading is a bug which was fixed and is available in the latest Prism CI biuld on MyGet.
Related
I have a Xamarin forms app that is using dryioc for IoC. It seems all my services get disposed as soon as the view is out of scope
This is how I am registering the service in the app.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IInboxService, InboxService>();;
}
and I resolve it this way in the app.cs
protected override void OnInitialized()
{
_started = DateTime.Now;
InitializeComponent();
InitializeAppSettings();
Container.Resolve<ISyncService>().Init(new List<ISyncableService>
{
Container.Resolve<IInboxService>()
});
}
When I need to use it in a viewmodel I put it in the constructor like so.
public class HomeViewModel {
public HomeViewModel(InboxService inboxService)
{
}
}
The singleton is respected but then it will dispose and create a new one when it needs it. Anyone else run into this ?
Xamarin Version: 5.0.0.2125
Prism Version: 8.1.97
LocIOS Version: 8.1.97
public HomeViewModel(InboxService inboxService)
This injects a concrete InboxService instance, which is independent of the registration of interfaces that it might implement. If you want your singleton, request an IInboxService:
public HomeViewModel(IInboxService inboxService)
I try to give a service implementing a certain interface a meaningful name that's more than just interface's name minus I, like DummyInboxService, TestInboxService, ImapInboxService, Pop3InboxService, RemoteInboxService... thus making clearer what the respective implementation actually does and helping finding these errors (most likely a typo anyway) early.
I'm having trouble using DryIoc for constructor injection into a ViewModel using Prism with Xamarin. I am using the Nuget package Prism.DryIoc.Forms.
In my project I get the following error in AuthenticatePage.xaml.g.cs
Unable to resolve Object {RequiredServiceType=Project.ViewModels.AuthenticatePageViewModel} with 1 arg(s)
in wrapper Func<Xamarin.Forms.Page, Object> {RequiredServiceType=Project.ViewModels.AuthenticatePageViewModel} with 1 arg(s)
from container
with normal and dynamic registrations:
MainPage, {ID=44, ImplType=Project.Views.MainPage}}
NavigationPage, {ID=43, ImplType=Xamarin.Forms.NavigationPage}}
AuthenticatePage, {ID=45, ImplType=Project.Views.AuthenticatePage}}
Specifically, it points to the line
private void InitializeComponent() {
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(AuthenticatePage));
}
Of note is that if I call the following in App.OnInitialized, the object resolves fine:
c.Register<INegotiator, Negotiator>(Reuse.Singleton);
var n = c.Resolve<INegotiator>();
n.ResumeSessionAsync(); // This works fine, no problems.
await NavigationService.NavigateAsync("NavigationPage/AuthenticatePage"); // Error thrown here
If I remove the constructor injection from my ViewModel it works fine (Aside from keeping the default navigationService injection, which works fine). Even trying to inject a basic class like ILogger (no dependencies) fails.
public AuthenticatePageViewModel(INavigationService navigationService, ILogger logger) : base (navigationService)
{
Title = "Authentication Page...";
}
I'm going to keep investigating, but is it obvious to someone here if I'm fundamentally doing something wrong? If I had to guess I would say it's to do with a conflict with Prisms built in Ioc container and DryIoc?
Edit:
I'm using the latest version of Prism.DryIoc.Forms available on NuGet (7.0.0.396) which says it includes DryIoc 2.12.26. I have so far simply followed the template available for Visual Studio which lists setting up navigation as follows:
protected override async void OnInitialized()
{
InitializeComponent();
var c = new Container();
c.Register<ILogger, LoggerConsole>(Reuse.Singleton);
c.RegisterMany(new[] { Assembly.Load("Project.UWP") },
serviceTypeCondition: type => type == typeof (ILocalFileHandler));
c.Register<INegotiator, Negotiator>(Reuse.Singleton);
// var n = c.Resolve<INegotiator>();
// n.ResumeSessionAsync(); // <- This will run fine. Negotiator class has ILogger and ILocalFileHandler injected into it.
await NavigationService.NavigateAsync("NavigationPage/AuthenticatePage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage>();
containerRegistry.RegisterForNavigation<AuthenticatePage>();
}
I can't find any info online on if/how I should be using Prism.DryIoc.DryIocContainerExtensions to set up navigation? Even modifying the sample app to include basic construction injection results in the error "Value Cannot Be Null" in the same xaml.g.cs file?
Prism 7.0 and below allows the exception to bubble up, in order to diagnose the root cause of your issue you want to better diagnose this issue I suggest you do a little try/catch to see what and where the error really is.
protected override void OnInitialized()
{
try
{
// Check if there is an initialization exception
var page = new AuthenticationPage();
// Validate that the page resolves ok
var page2 = Container.Resolve<object>("AuthenticationPage");
// Validate that your ILogger interface is registered and resolves ok
var logger = Container.Resolve<ILogger>();
// Check for Registration/initialization exceptions
var vm = Container.Resolve<AuthenticationPageViewModel>();
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
System.Diagnostics.Debugger.Break();
}
}
You haven't specified at what point you're getting this error, though typically with XAML Compilation enabled you would see exceptions in the {pageName}.xaml.g.cs during compilation and not runtime. Either way, given that your exception is coming from the generated XAML code behind class, this tells me it is most likely a problem with your XAML. A very simple way to validate this is to remove all of the XAML content in your AuthenticationPage so that you have an empty page.
Given the code you've provided as part of your question, I would say you have no registration for your ILogger interface which would likely throw an exception causing the problem you're seeing. Regardless of what/where the error is, the try/catch shown above would be the easiest way to determine the root cause.
Following #Dan S.'s diagnoses suggestion as well as reading this article (http://brianlagunas.com/whats-new-in-prism-for-xamarin-forms-7-0/) I realized that I should have been using the Prism.Ioc.ContainerRegistry abstraction layer to interface with DryIoc. Prior to this I had been working directly with DryIoc's classes.
Once I modified my registration code to use Prism.Ioc.IContainerRegistry it worked perfectly.
protected override void RegisterTypes(IContainerRegistry cr)
{
cr.Register<ILogger, LoggerConsole>();
cr.GetContainer().RegisterMany(new[] { Assembly.Load("Project.UWP") },
serviceTypeCondition: type => type == typeof(ILocalFileHandler));
cr.Register<INegotiator, Negotiator>();
cr.RegisterForNavigation<NavigationPage>();
cr.RegisterForNavigation<MainPage>();
cr.RegisterForNavigation<AuthenticatePage>();
}
Although I have seen Unity DI in apps before at a previous employer, I am adding it to one for the first time and am effectively a Unity Newbie. Consequently I am going slowly and testing as I go with Unity v4.0.1.
I have a console app with a simple dependency graph: LoadPayees, which has a dependency on PayeeLogic, which has a dependency on CommissionsRepository.
I have created a very simple Unity registration class with a single method:
public class UnityRegistration
{
public static void RegisterCommissionsTypes(IUnityContainer container)
{
container.RegisterType<ILoadPayees, LoadPayees>();
}
}
One resolution statement:
var processorInstance = (BaseTaskProcessor) container.Resolve(Type.GetType(classWithAssembly));
where class with assembly resolves to LoadPayees.
I have created a method injector in loadPayees for PayeeLogic:
[InjectionMethod]
public void Initialize(PayeeLogic payeeLogic)
{
this._payeeLogic = payeeLogic;
}
and a constructor injector in PayeeLogic for CommissionsRepository:
private CommissionsRepository _commissionsDal;
public PayeeLogic(CommissionsRepository commissionsDal)
{
this._commissionsDal = commissionsDal;
}
Having created this I then debugged in VS, expecting it to blow up with a null reference exception, since I hadn't explicitly registered PayeeLogic or CommissionsDAL with Unity, but it didn't. The entire process proceeded successfully as it did before I modified it to use Unity.
So either I have missed something stupid (which is likely) or Unity is able to automatically figure out all of LoadPayees' dependencies automatically, which is not what I expected.
So my question to Unity experts is, which of these is more likely?
The answer is yes, once the top level object is registered and resolved with Unity, it can figure out object dependencies, so object injection will work. However, so what. The main point of DI is being able to swap out concrete object instantiation for objects that implement the same interface, so if you want to do that, you need to register and resolve the entire object graph.
I made changes to my code as follows:
public static void RegisterCommissionsTypes(IUnityContainer container)
{
container.RegisterType<ILoadPayees, LoadPayees>();
container.RegisterType<IPayeeLogic, PayeeLogic>();
container.RegisterType<IPayeeDataAccess, CommissionsRepository>();
var payeeLogic = container.Resolve<PayeeLogic>();
var payeeDal = container.Resolve<CommissionsRepository>();
}
(LoadPayee resolution unchanged)
private IPayeeLogic _payeeLogic;
method injection:
[InjectionMethod]
public void Initialize(IPayeeLogic payeeLogic)
{
this._payeeLogic = payeeLogic;
}
constructor injection:
private IPayeeDataAccess _commissionsDal;
public PayeeLogic(IPayeeDataAccess commissionsDal)
{
this._commissionsDal = commissionsDal;
}
This was very much a newbie question, maybe even a stupid question? Hope it helps another Unity newbie to get their head around this product.
Technologies
C# 4.0
Prism 4 with Unity for DI
WPF
MVVM
Preface
There are two projects in my solution, MyApp.Shell and MyApp.ModuleFoo
MyApp.Shell's Unity Bootstrapper
protected override IModuleCatalog CreateModuleCatalog()
{
// Module assemblies are read from a directory.
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
return moduleCatalog;
}
The project MyApp.ModuleFoo contains a View and a View Model.
The ViewModel
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
{
public string FooText
{
get { return "Foo!"; }
}
}
The View
<Label Content={Binding FooText} />
The View's Code-behind
// Unity automatically sees this as Constructor Injection,
// which is exactly what I desire.
public FooView(FooViewModel viewModel)
{
DataContext = viewModel;
...
}
MyApp.FooModule's Initialization
Perhaps registering FooView with the region manager is inadvertently registering FooViewModel with Unity?
public void Initialize()
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("FooRegion", typeof(FooView));
}
The view correctly displays "Foo!".
Problems
How do I tell Unity to register only a single instance of FooViewModel?
Additionally, (and I'm thinking ahead here), how would I tell unity not to register FooViewModel?
Thanks for the help.
Edit:
Added MyApp.FooModule's Initialization code
Edit (Solution):
It turns out RegisterViewWithRegion has two overloads. From Prism's documentation, when the overload I'm using is used, a new instance of the view is created. I'm assuming this also creates a new instance of FooViewModel.
The other overload uses a delegate to resolve FooView. The documentation says this overload is used in the "ViewModel-first" approach. I'm going to make this question as answered, but if anyone has any additional insight, I'd love to hear.
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
...
I am surprised that you say this as Unity does not register types inside the container by default. You have to tell it to do so either programmatically or in the config file.
When you have concrete classes (not interfaces) they will automatically get created by Unity whether they are registered or not. If not the default behavior is to create a new instance each time. No lifetime management is applied also.
As far as your questions:
To register only one type within your initialisation of your module just have.
Container.RegisterType<FooViewModel>(new ContainerControlledLifetimeManager());
The lifetime manager will instruct unity to only create one instance of the view model.
I've been trying to inject the modules from my ModuleCatalog into my Shell's ViewModel but I'm not having much luck...
I'm creating the ModuleCatalog in my Bootstrapper and my module is getting onto the screen from its Initializer without problem. However, I'd love to be able to bind my list of modules to a container with a DataTemplate which allowed them to be launched from a menu!
Here's my Boostrapper file, I'll be adding more modules as times goes on, but for now, it just contains my rather contrived "ProductAModule":
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
Container.RegisterType<IProductModule>();
base.ConfigureContainer();
}
protected override IModuleCatalog GetModuleCatalog()
{
return new ModuleCatalog()
.AddModule(typeof(ProductAModule));
}
protected override DependencyObject CreateShell()
{
var view = Container.Resolve<ShellView>();
var viewModel = Container.Resolve<ShellViewModel>();
view.DataContext = viewModel;
view.Show();
return view;
}
}
Following on from that, here's my Shell's ViewModel:
public class ShellViewModel : ViewModelBase
{
public List<IProductModule> Modules { get; set; }
public ShellViewModel(List<IProductModule> modules)
{
modules.Sort((a, b) => a.Name.CompareTo(b));
Modules = modules;
}
}
As you can see, I'm attempting to inject a List of IProductModule (to which ProductAModule inherits some of its properties and methods) so that it can then be bound to my Shell's View. Is there something REALLY simple I'm missing or can it not be done using the Unity IoC? (I've seen it done with StructureMap's extension for Prism)
One more thing... When running the application, at the point the ShellViewModel is being resolved by the Container in the Bootstrapper, I receive the following exception:
Resolution of the dependency failed, type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = "". Exception message is: The current build operation (build key Build Key[PrismBasic.Shell.ViewModels.ShellViewModel, null]) failed: The parameter modules could not be resolved when attempting to call constructor PrismBasic.Shell.ViewModels.ShellViewModel(System.Collections.Generic.List`1[[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] modules). (Strategy type BuildPlanStrategy, index 3)
Anyway, simple huh... Looks bemused...
Any help would be greatly appreciated!
Rob
I think you could probably just do this:
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
Container.RegisterType<IProductModule>();
base.ConfigureContainer();
}
private static ObservableCollection<IProductModule> _productModules = new Obser...();
public static ObservableCollection<IProductModule> ProductModules
{
get { return _productModules; }
}
protected override IModuleCatalog GetModuleCatalog()
{
var modCatalog = new ModuleCatalog()
.AddModule(typeof(ProductAModule));
//TODO: add all modules to ProductModules collection
return modCatalog;
}
...
}
Then you would have a static property that anything could bind to directly, or could be used from your ViewModel.
Here is how to get a list of module names that have been registered with the module catalog.
public class MyViewModel : ViewModel
{
public ObservableCollection<string> ModuleNames { ... }
public MyViewModel(IModuleCatalog catalog)
{
ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName));
}
}
That's pretty much it. IModuleCatalog and IModuleManager are the only things that are setup in the container for you to access in terms of the modules. As I said, though, you won't get any instance data because these modules (hopefully) are yet to be created. You can only access Type data.
Hope this helps.
I think you misunderstood the purpose of the modules. The modules are just containers for the views and services that you wish too use. The shell on the other hand should just contain the main layout of your application.
What I think you should do is to define a region in your shell, and then register the views (which in your case are buttons) with that region.
How you wish do deploy your views and services in terms of modules is more related to what level of modularity you're looking for, i.e. if you want to be able to deploy the views and services of ModuleA independently of the views and services of ModuleB and so on. In your case it might be enough to register everything in one single module.
Take some time to play around with the examples provided with the documentation, they are quite good.
The reason why your examples throws an example is because your ShellViewModel is depending on List and that type is not registered in Unity. Furthermore you're registering IProductModule with Unity, which makes no sense because an Interface cannot be constructed.
I think I encountered a similar problem today, it turns out that PRISM creates the shell before initializing the modules, so you can't inject any services from the modules into the shell itself.
Try creating another module that depends on all of the others and implements the functionality you want, then you can add it to a region in the shell to display your list of services. Unfortunately I haven't had a chance to try it yet, but this is the solution I plan on implementing.
As a side note, I think you need to mark the property with an attribute to use property injection, but I could be mistake (it's been a while since I played with Unity directly).
Edit: You need to apply the DependencyAttribute to properties to use setter injection in Unity; you can read about it here.
var modules = new IProductModule[]
{
Container.Resolve<ProductAModule>()
//Add more modules here...
};
Container.RegisterInstance<IProductModule[]>(modules);
That's it! Using this code, I can inject my modules into the ShellViewModel and display each module as a button in my application!
SUCH a simple resolution! From a great guy on the CompositeWPF Discussion group. I recommend them without reserve ^_^