I'm looking for the best way to inject a ViewModel into a Shell view.
I'm using Autofac (but I can adopt code from other IoC containers if sample is available). I have got the other VMs injecting correctly - but the method that resolves the VM using ResoleForPage method of the App class.
I'm fairly new to UWP developement and any help is greatly appreciated!
Passing a ViewModel to the Shell is indeed simpler than passing it to the other pages, because the Shell is the only page that is created explicitly by us: so, it should be enough to add a parameter to the constructor of the Shell of type ShellViewModel:
public Shell()
{
Instance = this;
this.InitializeComponent();
}
public Shell(INavigationService navService, ShellViewModel model) : this()
{
navigationMenu.NavigationService = navService;
navigationMenu.RefreshStyles(App.Current.RequestedTheme, true);
this.DataContext = model;
}
then expose the DataContext in a strongly typed way, as with any other pages (useful mainly if you use x:Bind bindings in xaml):
public ShellViewModel ViewModel => DataContext as ShellViewModel;
And now you just have to pass an instance of your ViewModel class, pulling it from your IoC container, when you create the Shell. In the latest Template 10 template for VS2017, it should be in the CreateRootElement method of the App class:
public override UIElement CreateRootElement(IActivatedEventArgs e)
{
var service = NavigationServiceFactory(BackButton.Attach, ExistingContent.Include);
return new Template10.Controls.ModalDialog
{
DisableBackButtonWhenModal = true,
Content = new Shell(service, new ShellViewModel()),
};
}
of course replacing new ShellViewModel() with the code to pull it from Autofac.
Related
I am working on my first Blazor Server application, which is also my first Entity Framework Core application. I am wanting to set up a background service which, once a day in the early morning, checks the database to see if any of a certain record type has been added with yesterday's date. If so, the relevant records are pulled, formatted, and then emailed to a stakeholder.
I have the EF, formatting, and emailing code working just fine when I trigger the report by manually visiting the page. The problem that I have is how to provide the background service with a DbContextFactory so that the EF and related code can execute.
Up to this point I've always used Razor-based dependency injection to inject the IDbContextFactory via an inject IDbContextFactory<OurAppContext> DbFactory at the top of the page, and then accessed the DbFactory via the DbFactory variable.
However, background services are (according to this Microsoft tutorial) set up through Program.cs, so I don't have access to Razor-based dependency injection there.
I have set up my background service (what I call the PhaseChangeReportService) as indicated in the above link, and it dutifully outputs to the console every 10 seconds that it is running with an updated execution count. I don't fully understand what's going on with the various layers of indirection, but it appears to be working as Microsoft intended.
I noted that the constructor for the background service takes in an ILogger as a parameter, specifically:
namespace miniDARTS.ScopedService
{
public sealed class PhaseChangeReportService : IScopedProcessingService
{
private int _executionCount;
private readonly ILogger<PhaseChangeReportService> _logger;
public PhaseChangeReportService(ILogger<PhaseChangeReportService> logger)
{
_logger = logger;
}
public async Task DoWorkAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
++_executionCount;
_logger.LogInformation("{ServiceName} working, execution count: {Count}", nameof(PhaseChangeReportService), _executionCount);
await Task.Delay(10_000, stoppingToken);
}
}
}
}
I was (and am) confused that the constructor is never referenced within Visual Studio, but when I drop a breakpoint on its one line of code it is hit. I tried modifying this constructor's signature so that it took in an IDbFactory<OurAppContext> as well, so that whatever dark magic is allowing an ILogger<BackgroundServiceType> to come in for assignment to _logger might bring in a DbFactory<OurAppContext> as well, like so:
private readonly ILogger<PhaseChangeReportService> _logger;
private readonly IDbContextFactory<miniDARTSContext> _dbContextFactory;
public PhaseChangeReportService(ILogger<PhaseChangeReportService> logger, IDbContextFactory<miniDARTSContext> dbContextFactory)
{
_logger = logger;
_dbContextFactory = dbContextFactory;
}
However, doing so just led to the constructor breakpoint being skipped over and not breaking, with no exception being thrown or any console output of any kind (i.e. the prior execution count console output no longer showed up). So, I gave up on that approach.
Here is the relevant section of Program.cs:
// Configure the database connection.
string connectionString = builder.Configuration.GetConnectionString("miniDARTSContext");
var serverVersion = new MySqlServerVersion(new Version(8, 0, 28));
builder.Services.AddDbContextFactory<miniDARTSContext>(options => options.UseMySql(connectionString, serverVersion), ServiceLifetime.Scoped);
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<ScopedBackgroundService>();
services.AddScoped<IScopedProcessingService, PhaseChangeReportService>();
})
.Build();
host.RunAsync();
Here's IScopedProcessingService.cs:
namespace miniDARTS.ScopedService
{
public interface IScopedProcessingService
{
Task DoWorkAsync(CancellationToken stoppingToken);
}
}
And here's ScopedBackgroundService.cs:
namespace miniDARTS.ScopedService;
public sealed class ScopedBackgroundService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ScopedBackgroundService> _logger;
public ScopedBackgroundService(IServiceProvider serviceProvider, ILogger<ScopedBackgroundService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{nameof(ScopedBackgroundService)} is running.");
await DoWorkAsync(stoppingToken);
}
private async Task DoWorkAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{nameof(ScopedBackgroundService)} is working.");
using (IServiceScope scope = _serviceProvider.CreateScope())
{
IScopedProcessingService scopedProcessingService = scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWorkAsync(stoppingToken);
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{nameof(ScopedBackgroundService)} is stopping.");
await base.StopAsync(stoppingToken);
}
}
I'm confident I'm misunderstanding something relatively fundamental here when it comes to services / dependency injection, but my Googling and review of past StackOverflow answers has not turned up anything I can run with.
The IDbContextFactory is an interface that is used for creating instances of a DbContext. When you add it to your services on program.cs for Blazor (services.AddDbContextFactory(parameters)), it implements the IDbContextFactory for you. This allows you to use the #inject IDbContextFactory<YourDbContext> DbFactory at the top of your razor components and then within your code you can call the CreateDbContext method when you need to create an instance of the DbContext (ex. using var context = DbFactory.CreateDbContext()).
You can pass an injected DbContextFactory as a parameter from a razor component to a class, and then use that DbContextFactory in a method to create an instance of the DbContext (see constructor injection), but that still relies on the razor component to inject the DbContextFactory to begin with.
To create an instance of a DbContext independent of a razor component, you need to use the constructor for your DbContext. Your DbContext will have a public constructor with a DbContextOptions parameter (this is required to be able to use AddDbContextFactory when registering the factory service in program.cs). You can use this constructor to implement your own factory. If you aren't sure which options to use, you can check your program.cs to see what options you used there.
public class YourDbFactory : IDbContextFactory<YourDbContext>
{
public YourDbContext CreateDbContext()
{
var optionsBuilder = new DbContextOptionsBuilder<YourDbContext>();
optionsBuilder.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=Test"));
return new YourDbContext(optionsBuilder);
}
}
Once you've created your own implementation of the IDbContextFactory interface, you can then use it in your code independent of razor components - for example in the background service class.
YourDbFactory DbFactory = new YourDbFactory();
using var context = DbFactory.CreateDbContext();
I'm new at Zenject(Extenject).
My dev environment: Win10, Unity2020, Extenject 9.2.0
Here is my question:
In installer bind the class
Container.Bind<AccountInfo>().AsCached();
Inject it at classA
private AccountInfo accountInfo;
[Inject]
private void Init(GameSetup _gameSetup, AccountInfo _accountInfo)
{
this.gameSetup = _gameSetup;
this.accountInfo = _accountInfo;
}
accountInfo.address = "xxx'; // works fine
Then inject AccountInfo to classB
private AccountInfo accountInfo;
[Inject]
private void Init(AccountInfo _accountInfo)
{
this.accountInfo = _accountInfo;
}
accountInfo.address = "xxx'; //NullReferenceException: Object reference not set to an instance of an object
Why accountInfo changed to null? AsCached() dosen't work? Or something worng else?
Help please~~ Thank you!
Here is my code:
Installer
"ClassA" inject GameSetup, and create instance, works fine
"ClassB" inject GameSetup, Error: null object
"ClassB" Creator, I'm trying use container.Instantiate() to create it
---update---
gameSetup still Null Object
There are two cases, when injection will not work properly in your code.
The code, that uses injected object is executed before Init. For example if this code is placed in the construcor.
You create your GameObject/Component in runtime whithout using IInstantiator. While you use Znject you always should use IInstantiator to create objects. To do it you should inject IInstantiator to the object, that creates another objects. IItstantiator is always binded in the container by default, so you don't have to bind it manually. For example:
public class Creator : MonoBehaviour {
[SerializeField]
private GameObject _somePrefab;
private IInstantiator _instantiator;
[Inject]
public void Initialize(IInstantiator instantiator) {
_instantiator = instantiator;
}
private void Start() {
// example of creating components
var gameObj = new GameObject(); // new empty gameobjects can be created without IInstantiator
_instantiator.InstantiateComponent<YourComponentClass>(gameObj);
// example of instantiating prefab
var prefabInstance = _instantiator.InstantiatePrefab(_somePrefab);
}
}
Not an expert but I think that passing IInstantiator or the container around is not a good practice. If you need to create injected instances at runtime, then you need a Factory.
From the documentation
1.- Best practice with DI is to only reference the container in the composition root "layer"
Note that factories are part of this layer and the container can be referenced there (which is necessary to create objects at runtime).
2.- "When instantiating objects directly, you can either use DiContainer or you can use IInstantiator, which DiContainer inherits from. However, note that injecting the DiContainer is usually a sign of bad practice, since there is almost always a better way to design your code such that you don't need to reference DiContainer directly".
3.- "Once again, best practice with dependency injection is to only reference the DiContainer in the "composition root layer""
So I was working on some sample MVVM project using Dagger. I have a viewmodel factory that goes like this:
class DaggerViewModelFactory #Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = viewModelsMap[modelClass] ?:
viewModelsMap.asIterable().firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
A viewmodel factory module
#Module
abstract class ViewModelFactoryModule {
#Binds
abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
I got a ViewModelModule:
#Module
abstract class MyViewModelModule {
#Binds
#IntoMap
#ViewModelKey(TakePicturesViewModel::class)
abstract fun bindTakePictureViewModel(takePicturesViewModel: TakePicturesViewModel): ViewModel
}
A component that goes like this:
#PerActivity
#Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
fun inject(mainActivity: MainActivity)
}
An a viewmodel that goes like this:
class TakePicturesViewModel #Inject constructor(app: Application): AndroidViewModel(app) {...
So I can either inject my viewmodel in my activity using a view model factory like this:
#Inject
lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
Or with not viewmodel factory at all, like this:
#Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
Both ways work, so I was wondering which one is the right way to work, if using Dagger allows me to inject a viewmodel without needing a viewmodelfactory, is there a good reason to keep it?, or should I just get rid of this viewmodelfactory?
Thanks in advance for any advice.
Greetings
Both ways work, so I was wondering which one is the right way to work, if using Dagger allows me to inject a viewmodel without needing a viewmodelfactory, is there a good reason to keep it?, or should I just get rid of this viewmodelfactory?
Both ways work differently. Try rotating your screen with stored data in your ViewModel and you'll see.
Dagger can create the ViewModel, which is what you make use of in that generic ViewModelFactory. Those view models should be unscoped, thus you'll be creating a new ViewModel every single time. The Android support library will cache that ViewModel and reuse it after rotation so that you can keep your data—the factory method gets called once and there will only ever be one ViewModel created (per lifecycle). You keep your data and everything behaves as expected.
If on the other hand you use Dagger to inject your ViewModel directly none of the above will apply. Like any other dependency, a new ViewModel will be injected on creation, leading to a ViewModel being created every single time it is used—you'll not only use the data stored in it, you won't be able to share state with fragments either.
Of course you could apply a scope to the ViewModel, but that scope should be longer lived than the Activity instance (to keep state between rotations), but no longer lived than the screen is visible. So you can neither scope it to the activity nor to the application lifecycle. You can get it to work by introducing a new scope in-between, but at this point you'd be reinventing the ViewModel library.
tl;dr Inject and use the factory or you'll get a confusing/wrong ViewModel implementation.
I am learning EF4.1 in conjunction with MVVM and in one of these tutorials they create a MainWindowViewModel with a Repository object that they use in calling another view model (EmployeeListViewModel). Here is the code:
public MainWindowViewModel()
{
IObjectContextAdapter adapter = ((IObjectContextAdapter)new SidekickEntities());
_vmRepository = new GenericRepository(adapter.ObjectContext);
EmployeeListViewModel viewModel = new EmployeeListViewModel(_vmRepository);
this.ViewModels.Add(viewModel);
}
public EmployeeListViewModel(GenericRepository repository)
{
if (repository == null)
{
throw new ArgumentNullException("repository");
}
_employeeRepository = repository;
this.AllEmployees = new ObservableCollection<Employee>(_employeeRepository.GetAll<Employee>());
}
What I am wondering is, why is the repository created in the MainWindowViewModel and then passed into EmployeeListViewModel? Why not just create the repository in EmployeeListViewModel like this:
public MainWindowViewModel()
{
EmployeeListViewModel viewModel = new EmployeeListViewModel();
this.ViewModels.Add(viewModel);
}
public EmployeeListViewModel()
{
IObjectContextAdapter adapter = ((IObjectContextAdapter)new SidekickEntities());
_employeeRepository = new GenericRepository(adapter.ObjectContext);
this.AllEmployees = new ObservableCollection<Employee>(_employeeRepository.GetAll<Employee>());
}
I am pretty new to EF but it seems a little cleaner to create a repository in each separate ViewModel. Wouldn't the repository then be cleaned up when the ViewModel in question is no longer used instead of keeping it around until the closing of MainWindowViewModel or does this create too many repository instances?
The problem you will run into when using a separate Context for each ViewModel is that entities that have links to each other come from different contexts so you will have to manually work this out when trying to update something (see Attaching and Detaching Objects)
This has to do with a pattern called Unit Of Work.
UoW helps you with encapsulating functions that should work together. In your case the ObjectContext implements the UoW pattern and is passed to the repository in the constructor.
You should share your ObjectContext between functions that work together and should be seen as one Unit Of Work.
I am looking for some help and I hope that some good soul out there will be able to give me a hint :)
I am building a new application by using MVVM Light. In this application, when a View is created, it instantiates the corresponding ViewModel by using the MEF import.
Here is some code:
public partial class ContractEditorView : Window
{
public ContractEditorView ()
{
InitializeComponent();
CompositionInitializer.SatisfyImports(this);
}
[Import(ViewModelTypes.ContractEditorViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
}
And here is the export for the ViewModel:
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.ContractEditorViewModel)]
public class ContractEditorViewModel: ViewModelBase
{
public ContractEditorViewModel()
{
_contract = new Models.Contract();
}
}
Now, this works if I want to open a new window in order to create a new contract... or in other words, it is perfect if I don't need to pass the ID of an existing contract.
However let's suppose I want to use the same View in order to edit an existing contract. In this case I would add a new constructor to the same View, which accepts either a model ID or a model object.
"Unfortunately" the ViewModel is created always in the same way:
[Import(ViewModelTypes.ContractEditorViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
As far as I know, this invokes the standard/no-parameters constructor of the corresponding ViewModel at composition-time.
So what I would like to know is how to differentiate this behavior? How can I call a specific constructor during composition time? Or how can I pass some parameters during the Import?
I really apologize if this question sounds silly, but I have only recently started to use MEF!
Thanks in advance,
Cheers,
Gianluca.
You CAN do this. Check out the Messenger implementation in MVVM-Light. You can pass a NotificationMessage(Of Integer) to send the right ID to the view model. The view model has to register for that type of message, and load it when a message is sent.
MEF Imports by default only have a parameterless constructor.