How to instantiate a Service with parameters in MVVM Light ViewModelLocator? - c#

I have been searching for about 3 hours.
I need to know how do I instantiate a Service with parameters that are produced by another service in MVVM Light's ViewModelLocator.
In my ViewModelLocator class I register my View Models and then register services that they use, like this:
SimpleIoc.Default.Register<INetworkClient>(() => new NetworkClient());
SimpleIoc.Default.Register<IApiClientAuthentication>(() => new ApiClientAuthentication());
SimpleIoc.Default.Register<IApiClient>(() =>
{
INetworkClient networkClient = ServiceLocator.Current.GetInstance<INetworkClient>();
return new ApiClient(networkClient, new Uri("https://someuri.com/"),
authResponse.AdminUserId, authResponse.ApiKey);
});
The problem is that the "authResponse" that I pass to the ApiClient above is a result produced by calling a method on implementation of IApiClientAuthentication (which I also register with IoC container above). That response is not available when I create the ApiClient above. How should I go about it.
Please, at least push me in the right direction.

Related

MAUI dependency injection service resolve in Program.cs

MAUI has dependency injection setup similar to what ASP.NET Core has in the Startup.cs class. This one is set in MauiProgram.cs file by default.
My question is: How can I get a service instance in this file after services registration? I guess, one solution will be the following, but then I must edit this code also if the constrctors of these services change during time:
var keyValueStore = new PreferencesKeyValueStore();
var accountService = new AccountService(keyValueStore);
var profileService = new ProfileService(keyValueStore);
builder.Services.AddSingleton<IKeyValueStore>(keyValueStore);
builder.Services.AddSingleton<IAccountService>(accountService);
builder.Services.AddSingleton<IProfileService>(profileService);
//Here now I can use accountService and profileService to do something
I can not find more elegant solution that will return the service instance for me from the DI container. Something like:
builder.Services.AddSingleton<IKeyValueStore, PreferencesKeyValueStore>();
builder.Services.AddSingleton<IAccountService, AccountService>;
builder.Services.AddSingleton<IProfileService, ProfileService>();
//Now I can't perform something like: var accountService = diContainer.GetInstance<IAccountService>(); or similar.
I don't know how to reach di container and ask it to provide me registered instance.
Actually, the documentation provided a simple way to do so.
Check it here
They recommended to use the Handler property of any object of type Element, there you can write the code :
// Considering you want to resolve a service from your custom interface IMyService
var service = this.Handler.MauiContext.Services.GetService<IMyService>();
// Then you can use the resolved service..
But there are some issues, personally it never worked for me, the Handler property may be null because of the lifecycle of the Element you are calling it on.
To avoid this issue, use a full line like:
var service = Application.Current.MainPage
.Handler
.MauiContext
.Services
.GetService<IMyService>();
// Then you can use the resolved service..
This works fine for me
Hope it helps you ..

C# Autofac New Instance

I have a class "DependencyResolver" where I return instances of objects by hand. There I used "Activator.CreateInstance".
I wanted to change it so it uses autofac.
My function "Get" works fine:
public T Get<T>()
{
return _container.Resolve<T>();
}
But I also have a function "CreateNew" where I need a new instance:
public T CreateNew<T>()
{
return _container.Resolve<T>();
}
The Problem is that I always get the same instance.
My Registration looks like this:
var builder = new ContainerBuilder();
foreach (var dllFileName in DependencyMapping.GetAllDllFilenames())
{
builder
.RegisterAssemblyTypes(Assembly.LoadFile(Path.Combine(GetPathFromInstalledSys(), dllFileName)))
.AsImplementedInterfaces()
.SingleInstance();
}
_container = builder.Build();
So there is a place where I can control the behaviour: "SingleInstance" or "InstancePerDependency". But I dont know whether the user needs a new instance or not. Is there any way to change the behavior when "CreateNew" is called?
Lifetime scope (single instance, instance per dependency) is controlled at registration time, not resolve time. There's no way to change it. This is the case with all DI containers, not just Autofac. Part of the point of it is that the consumer shouldn't have to know what scope they want - they just ask for a thing and get it.
Consumers also generally shouldn't deal with disposal - things get disposed by the container.
Note what you have here is service location (client asks the container for a thing), not dependency injection (client takes dependencies in constructor and doesn't know about the container). While service location is sometimes required, generally try to avoid it if you can; it's not really much better than just calling new in your code.

How can I use Microsoft.Extensions.DependencyInjection in an UNO Platform app

I am new to UNO Platform and I am trying to develop my first application with this framework. I would like to use a DI toolkit also so I chose Microsoft.Extensions.DependencyInjection that should be compliant with this Platform.
By the way, I cannot understand how can I inject dependencies to my ViewModels.
I red this post:
How can I use Microsoft.Extensions.DependencyInjection in an .NET Core console app? and this is my approach:
App.xaml.cs:
public App()
{
ConfigureFilters(global::Uno.Extensions.LogExtensionPoint.AmbientLoggerFactory);
var services = new ServiceCollection()
.AddSingleton<IMessageBroker, MessageBroker>();
var serviceProvider = services.BuildServiceProvider();
this.InitializeComponent();
this.Suspending += OnSuspending;
}
where IMessageBroker and MessageBroker are the interface and the implementation of my class I would like to handle with DI.
Then on my ViewModel I do:
MainPageViewModel.cs
public MainPageViewModel(IMessageBroker broker)
{
// some code
}
So far, so good.
For MVVM toolkit, I chose MVVM helpers.
If I understood correctly, this MVVM toolkit does not provide any convention to attach View to ViewModel so I should do it manually:
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
// How can I get a reference to ServiceProvider for calling GetService
var broker = serviceProvider.GetService<IMessageBroker>()
this.DataContext = new MainPageViewModel(broker);
}
}
How can I get a reference here to ServiceProvider for calling GetService<IMessageBroker>()?
Any hint would be highly appreciated.
one way you could do is having a static class ServiceLocator, which is initialized only from the beginning. This class exposes a readonly ServiceProvider, and you can call it to get the thing you register.
You may read somewhere else, or someone else here may comment this is anti-pattern, which is generally out of context. Depending on what you are trying to do and how your solution is structured, this service locator pattern can be totally fine.
here is a sample project you can use for reference
https://github.com/weitzhandler/UnoRx/tree/f2a0771e6a513863108e58ac7087078f39f7e3ed
an alternative will be, writing a viewmodel locator, which automatically resolves all dependencies through your view. This, however, may or may not overkill your use case.
finally, my personal flavor, having a shell project that depends on auto discovery and auto resolving; and each discovered smaller projects will internally depends on either service locator or function compositions

PRISM creates new ViewModel by each navigation request?

I am using Prism v6.3.0 for a UWP app. I am using the Unity Container for DI.
Following problem occurs: whenever my app navigates to a given view (e.g. MainPage) there seems that a new instance of the corresponding ViewModel is created. I would like to reuse the same VM (basically, to have it only once created).
I am having: prismMvvm:ViewModelLocator.AutoWireViewModel="True" in all of the pages' XAML.
My code never calls directly any VM constructor, so it cannot be that. I am also registering only services in the ConfigureContainer override.
What I've tried is to register the Views in App.xaml.cs (it seems to make no difference):
private void RegisterViews()
{
Container.RegisterInstance(new LoginPage(), new ContainerControlledLifetimeManager());
Container.RegisterInstance(new SettingsPage(), new ContainerControlledLifetimeManager());
Container.RegisterInstance(new MainPage(), new ContainerControlledLifetimeManager());
}
Any hints?
Since no one answered, I tried figuring out on my own once more.
Now, I know that probably the best way to go about it would be to modify the container that Prism uses so it registers all the VMs as singletons. Because that sounded like too much work for this issue, I instead chose to first take a look at the ViewModelLocationProvider class. The ViewModelLocationProvider is used by PRISM to look up the VM for a given View (that has AutoWireViewModel set to true) and to inject the found VM type in the given View's DataContext.
I tried coming up with a factory for a given view that would ensure that only one VM instance would be created ever and found following solution:
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainPage>((delegate
{
var vm = Container.Resolve<MainViewModel>();
if (Container.Registrations.All(r => r.RegisteredType != typeof(MainViewModel)))
Container.RegisterInstance<MainViewModel>(vm, new ContainerControlledLifetimeManager());
return vm;
}));
ViewModelLocationProvider.Register<SettingsPage>((delegate
{
var vm = Container.Resolve<SettingsViewModel>();
if (Container.Registrations.All(r => r.RegisteredType != typeof(SettingsViewModel)))
Container.RegisterInstance<SettingsViewModel>(vm, new ContainerControlledLifetimeManager());
return vm;
}));
}

Multiple LUIS models from config

I use 2 LUIS models in 1 LUIS Dialog via attributes
[LuisModel("model1", "")]
[LuisModel("model2", "")]
[Serializable]
public class LuisDialog
I need to get these models from the config file.
In Autofac I can only register 1
builder.Register(c => new LuisModelAttribute("model1", ""))...
How can I set up multiple Luis models from a config?
I don't think that will work since the LuisModel is being injected to the LuisService you are registering (which probably is the next line in your configuration) and the LuisService is just expecting a single model and not an array of them.
The way I can think this could work is, instead of registering the model(s) to the Autofac container, you should register multiple LuisService defining the value of the model parameter of the constructor to each of your models (see this).
In that way, when you resolve your LuisDialog based dialog, it will inject multiple ILuisService (see this) as it's prepared to receive an array of services.
I haven't tried this, but you could see if something like this works:
var model1 = new LuisModelAttribute("model1", "");
var model2 = new LuisModelAttribute("model2", "");
builder.RegisterType<LuisService>()
.WithParameter("model", model1)
.Keyed<ILuisService>(FiberModule.Key_DoNotSerialize)
.AsImplementedInterfaces()
.SingleInstance(); or // .InstancePerLifetimeScope()
builder.RegisterType<LuisService>()
.WithParameter("model", model2)
.Keyed<ILuisService>(FiberModule.Key_DoNotSerialize)
.AsImplementedInterfaces()
.SingleInstance(); or // .InstancePerLifetimeScope()
Alternatively, you can use RegisterInstance and register the instances of the LuisService with their specific model.

Categories