Prism with Unity wrong instance gets passed into constructor - c#

I am trying to understand/learn Prism with Unity
I created following classes:
==========================================
Seperate Assembly containing a "Module":
using GlobalContracts;
using Prism.Ioc;
using Prism.Modularity;
namespace ModuleA
{
[Module(ModuleName = MyModuleA.NAME, OnDemand = true)]
public class MyModuleA : IModule
{
public const string NAME = "MyModuleA";
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<MyControlA>();
containerRegistry.Register<IView, MyControlA>(NAME);
containerRegistry.Register<PluginViewModelBase, MyControlViewModel>(NAME);
}
public void OnInitialized(IContainerProvider containerProvider)
{
}
}
}
==========================================
A ViewModel
using GlobalContracts;
namespace ModuleA
{
public class MyControlViewModel : PluginViewModelBase
{
public MyControlViewModel(IView view) : base(view)
{
}
}
}
==========================================
The Host Application (other assembly):
public partial class App : PrismApplication
{
private Shell mShell;
private ShellViewModel mShellViewModel;
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog(){ModulePath = #"..\..\..\..\ModulesOutput"};
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IView, Shell>();
containerRegistry.Register<IViewModel, ShellViewModel>();
}
protected override Window CreateShell()
{
mShellViewModel = Container.Resolve<ShellViewModel>();
mShell = (Shell)mShellViewModel.View;
return mShell;
}
(...)
Now my question is:
How do I tell Prism to resolve the IView-Parameter passed to the
constructor of the ViewModel properly?
It resolves it as "Shell" and not as "MyControlA".
Further tips regarding my code are welcome
I found some sources in the web but they used "RegisterType" method of a container. And for now I do not have dependencies to Unity in my ModuleA and I would not know how to get the container to call the "RegisterType". All sources are outdated in the web..

By default, it resolves the default registration, which in your case is Shell.
Registering a type with a name does not mean that that name is automatically used to resolve dependencies. You have to do that manually, with parameter override, injection factory or the like. But I'd try to avoid that as it makes things a bit fragile and tedious.

Related

Prism Dialog: Unable to resolve Resolution root Object

I tried this with Prism 8.0.0.1909 and dotnet core 3.1 and 5.
For my dialog I have a view:
<UserControl
x:Class="xzy.Modules.CapillaryBatchwise.Dialogs.NewBatchDialog"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
...
</UserControl>
and a View Model with nothing really in it for now:
namespace zxy.Modules.CapillaryBatchwise.ViewModels
{
public class NewBatchDialogViewModel : BindableBase, IDialogAware
{
...
public string Title => "MyTitle";
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog() => true;
public void OnDialogClosed()
{ }
public void OnDialogOpened(IDialogParameters parameters)
{ }
}
}
I registered the View and View Model in my App.xaml.cs
namespace xyz.CapillaryJournal
{
public partial class App
{
...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterDialog<NewBatchDialog, NewBatchDialogViewModel>();
}
}}
And then call it from my actual ViewModel
public class CapillaryBatchNavigationViewModel : BindableBase
{
private readonly IDialogService dialogService;
public CapillaryBatchNavigationViewModel(//...
IDialogService dialogService)
{
///...
ShowNewBatchDialogCommand = new DelegateCommand(ShowNewBatchDialog);
this.dialogService = dialogService;
//...
}
public DelegateCommand ShowNewBatchDialogCommand { get; }
private void ShowNewBatchDialog()
{
dialogService.ShowDialog(nameof(ShowNewBatchDialog));
}
//...
}
However when I call ShowNewBatchDialogCommand from my View I get this exception, that I can't make any sense of:
Prism.Ioc.ContainerResolutionException: 'An unexpected error occurred while resolving 'System.Object', with the service name 'ShowNewBatchDialog''
Inner Exception
ContainerException: code: Error.UnableToResolveFromRegisteredServices;
message: Unable to resolve Resolution root Object {ServiceKey="ShowNewBatchDialog"}
from container without scope
with Rules with {TrackingDisposableTransients, UseDynamicRegistrationsAsFallbackOnly, FuncAndLazyWithoutRegistration, SelectLastRegisteredFactory} and without {ThrowOnRegisteringDisposableTransient, UseFastExpressionCompilerIfPlatformSupported}
with FactorySelector=SelectLastRegisteredFactory
with Made={FactoryMethod=ConstructorWithResolvableArguments}
with normal and dynamic registrations:
("NewBatchDialog", {FactoryID=160, ImplType=xyy.Modules.CapillaryBatchweise.Dialogs.NewBatchDialog, Reuse=TransientReuse}) ("TaskPresenter", {FactoryID=177, ImplType=xyy.Modules.CapillaryBatchweise.Views.TaskPresenter, Reuse=TransientReuse})
This is basically slightly modified what was done in this doc: https://prismlibrary.com/docs/wpf/dialog-service.html
I can't spot what is wrong with my code.
You should call dialogService.ShowDialog(nameof(NewBatchDialog)); since the name of the view's NewBatchDialog while ShowNewBatchDialog is the name of some unrelated method.
Or you can register the view with a specific name like containerRegistry.RegisterDialog<NewBatchDialog, NewBatchDialogViewModel>( "ShowNewBatchDialog" );...

Defining Ninject's binding without scope to keep the code DRY

I have a Web project and a Windows Service project in my solution. I have created 2 different binding modules for these 2 projects, but as you can see it has a lot of duplicate code... the only difference is that I am using InRequestScope() for the Web project and InTransientScope() for Windows Service project.
Is it possible combine the bindings, and add the scope depending on the project/entry point?
public class WebModule : NinjectModule
{
public override void Load()
{
Bind<ApplicationDbContext>().ToSelf().InRequestScope();
Bind<IMyRepository>().To<MyRepository>().InRequestScope();
// more types ... InRequetScope();
}
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ApplicationDbContext>().ToSelf().InTransientScope();
Bind<IMyRepository>().To<MyRepository>().InTransientScope();
// more types ... InTransientScope();
}
}
Update:
As explained by ninject team, we can use InRequestScope() in both scenarios... since there is no concept of Request in a Windows Service project, ninject would use the default scope, which is InTransientScope() in the service project.
Original Answer
The best solution that I have come up with is to create an extension method:
public static class NinjectExtensions
{
public static IBindingNamedWithOrOnSyntax<T> GetScopeByName<T>(this IBindingInSyntax<T> syntax, string scope)
{
if (scope.Equals("request", StringComparison.InvariantCultureIgnoreCase))
{
return syntax.InRequestScope();
}
else if (scope.Equals("thread", StringComparison.InvariantCultureIgnoreCase))
{
return syntax.InThreadScope();
}
else if (scope.Equals("singleton", StringComparison.InvariantCultureIgnoreCase))
{
return syntax.InSingletonScope();
}
return syntax.InTransientScope();
}
}
And set the scope dynamically.
public class MyModule : NinjectModule
{
private string _scope = "transient";
public MyModule()
{
if (Convert.ToBoolean(ConfigurationManager.AppSettings["IsWebProject"]))
{
_scope = "request";
}
}
public override void Load()
{
Bind<ApplicationDbContext>().ToSelf().GetScopeByName(_scope);
Bind<IMyRepository>().To<MyRepository>().GetScopeByName(_scope);
// more types ... InRequetScope();
}
}
Note: I am not sure if there is a better solution... this is just the cleanest approach that has come to my mind.

How to get the same instance of object from Unity container in various modules of Prism application?

I have a Prism application where I have three modules:
SharedServiceModule(I am using SharedServices to communicate between modules)
ModuleA
ModuleB
SharedServiceModule just has interface and its implementation CommonService:
public interface ICommonService
{
string SomeStorage { get; set; }
}
public class CommonService : ICommonService
{
string fooStorage;
public string FooStorage
{
get
{
return fooStorage;
}
set
{
fooStorage = value;
OnPropertyChanged("FooStorage");
}
}
}
What I want is to create communication between modules using Shared Services. So I assign «ModuleAValue» at ViewModelA of ModuleA and then I want to read this value in ViewModelB of ModuleB. Let's see details.
I create a single instance of ICommonService in ViewModelA of ModuleA and assign a value "ModuleAValue" to FooStorage:
Method of ViewModelA:
unityContainer = new UnityContainer();
unityContainer.RegisterType<ICommonService, CommonService>(new ContainerControlledLifetimeManager());
IMyService someFoo = unityContainer.Resolve<ICommonService>();
someFoo.FooStorage = "ModuleAValue";//value is "ModuleAValue" in FooStorage
Then I want to read this data in viewModelB of ModuleB. But value of FooStorage is not 'Module A', but just empty value:
Method of ViewModelB:
IUnityContainer unityContainer=new UnityContainer//creation of UnityContainer in ModuleB
ICommonService someFoo = unityContainer.Resolve<CommonService>();
string str=someFoo.FooStorage;//value is empty in
FooStorage, but it should be "ModuleAValue"
My Bootstrapper is:
public class Bootstrapper:UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<IShellViewModel, ShellViewModel>();
RegisterTypeIfMissing(typeof(IMyService), typeof(MyService), true);
}
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
mappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
return mappings;
}
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(ModuleAModule));
catalog.AddModule(typeof(ModuleBModule));
return catalog;
}
}
What am I doing wrong? In my view, Unity always creates new instance of CommonService. What am I doing wrong while taking the same instance from Unity container?
Any help will be greatly appreciated!
Your app's bootstrapper creates an UnityContainer for you, see UnityBootstrapper.cs:
protected virtual IUnityContainer CreateContainer()
{
return new UnityContainer();
}
The bootstrapper should also register the container as viewmodel factory:
protected override void ConfigureContainer()
{
base.ConfigureContainer();
ViewModelLocationProvider.SetDefaultViewModelFactory( type => Container.Resolve( type ) );
}
In your module definition class, you can have this 'global' container injected as dependency:
public class ClientModule : IModule
{
private readonly IUnityContainer _unityContainer;
public ClientModule( IUnityContainer unityContainer )
{
_unityContainer = unityContainer;
}
}
In your module's initialization, you register types or instances with this container:
public void Initialize()
{
// Register services
_unityContainer.RegisterType<IGameClock, LocalGameClock>( new ContainerControlledLifetimeManager() );
_unityContainer.RegisterType<IWorldStateService, LocalWorldStateService>( new ContainerControlledLifetimeManager() );
}
In your views (in the xaml), you can now use ViewModelLocator.AutoWireViewModel="True" to automatically create viewmodels for your views. The ViewModelLocationProvider will use the 'global' container to resolve the viewmodels (as defined above), so all viewmodels will receive the same instance of our, say, IGameClock.
Ancilliary piece of advice: you don't want to call Resolve directly yourself. Using it this way defeats the whole purpose of using unity in the first place. It's better to have your dependencies injected as constructor parameters, and to only use the container at the highest level, i.e. in module initialization. And you should never need to create more than one container, lest you know exactly what you're doing.
You shouldn't create the new container instance. Usually there should be only one container instance in your application.
If you are using prism your view model should be created from a container also (if prism is responsible for view model creation than it is created from container ). In such case just create the constructor in your view model with parameter of type ICommonService like this:
public ViewModelA(ICommonService service) { ... }
then during creation of the ViewModel the same instance of the service will be injected to that ViewModel.
And usually common services are registered in the Shell during application startup. But if you want you can also register a service in a module, just use the same unity container that was created during application start. In the viewmodel use constructor with parameter of type IUnityContainer.

Prism for Xamarin, dependency failed for INavigationService

I am working on a sample Xamarin.Forms App for iOS, using PRISM.
But when I tried to use INavigationService in ViewModel, it is failed saying dependency failed for interface INavigationService
I dont know what wrong I am doing, how can we get around it or is it a bug in INavigationServices injection.
Thanks
Divikiran Ravela
Heres the code
public App()
{
// The root page of your application
Prism = new PrismBootStrap();
Prism.Run(this);
}
public class PrismBootStrap : UnityBootstrapper
{
protected override Page CreateMainPage()
{
return new NavigationPage(Container.Resolve<HomePage>());
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<HomePage, HomePageViewModel>();
}
}
public class HomePage : ContentPage
{
public HomePageViewModel ViewModel { get; set; }
public HomePage()
{
ViewModel = App.Prism.Container.Resolve<HomePageViewModel>();
Content = new StackLayout
{
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
}
public class HomePageViewModel : BindableBase
{
private readonly INavigationService _navigationService;
public HomePageViewModel(INavigationService navigationService)
{
_navigationService = navigationService; //Fails here
}
}
The INavigationService depends on the use of the ViewModelLocator. If you don't use it, then the service cannot be injected. Instead of manually resolving your VM, use the ViewModelLocator.AutowireViewModel attached property. Besides, you don't want to be using your container like that. You only want one composition root when using DI.
Also, I recommend using the latest 6.1 preview. It has a ton of navigation improvements.

Ninject injection chain isolation

I'm working on an application that is split over multiple assemblies. Each of the assemblies provides Interfaces to the outside world, instances are generated via Ninject-based factories.
Ah well, let there be Code. This is from the executing Assembly.
public class IsolationTestModule : NinjectModule
{
public override void Load()
{
ServiceFactory sf = new ServiceFactory();
Bind<IService>().ToMethod(context=>sf.CreatService()).InSingletonScope();
}
}
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IKernel kernel = new StandardKernel(new IsolationTestModule());
IService service = kernel.Get<IService>();
}
}
The ServiceFactory also relies on Ninject, but has its own Kernel and its own Module:
public interface IService
{
void Idle();
}
public interface IDependantService
{
void IdleGracefully();
}
public class ServiceImpl : IService
{
[Inject]
public IDependantService DependantService { get; set; }
public void Idle()
{
DependantService.IdleGracefully();
}
}
public class DependantServiceImpl : IDependantService
{
public void IdleGracefully() { }
}
public class ServiceFactory
{
private IKernel _kernel = new StandardKernel(new SuppliesModule());
public IService CreatService()
{
return _kernel.Get<IService>();
}
}
public class SuppliesModule : NinjectModule
{
public override void Load()
{
Bind<IService>().To<ServiceImpl>().InSingletonScope();
Bind<IDependantService>().To<DependantServiceImpl>().InSingletonScope();
}
}
What actually happens : All's well until the ServiceFactory has completed to build the ServiceImpl-instance. In the next step, the application's kernel tries to resolve ServiceImpl dependencies via IsolationTestModule and - of course - fails with an exception (no binding available, type IDependantService is not self-bindable). In my understanding the factory's kernel should do that...
Actually I never knew Ninject was that eager to resolve dependencies even in those instances it did not immediately create, which surely opens up new horizons to me ;-)
To temporarily solve this, I change the ServiceImpl to Constructor based injection as depicted below:
public class ServiceImpl : IService
{
public IDependantService DependantService { get; set; }
[Inject]
public ServiceImpl(IDependantService dependantService)
{
DependantService = dependantService;
}
public void Idle()
{
DependantService.IdleGracefully();
}
}
Nevertheless, I would prefer a solution that does not force me to change my Injection strategy. Does anyone have an idea how I can separate the Injection chains?
Your observations are correct. Ninject will do property injection for objects created by ToMethod. Also your solution to use constructor injection is the right way to go. Constructor injection is the perfered way to use Ninject anyway. Property Injection should be used for optional dependencies only.
What you should consider is to use just one kernel. It is very unusual to use multiple kernel instances in an application.

Categories