I am trying to use Dependency Injection in my project but its still new to me. I've read quite a lot of tutorials but I still don't what is the best practice to create pages or viewmodels..
It seems like everyone is saying, that Service Locator is a bad practice or "anti-pattern" and then they still proceed to do something like this inside of code behind:
_viewModel = IocContainer.Resolve<SomeViewModel>();
Isn't that killing purpose of IoC ?
I have been suggested to inject view model in page's constructor and then instantiate it somewhere else with factory. I should also mention, that I want to pass a parameter to a view model (apart from other injected services).
I'll give you example:
// Order detail
public class OrderDetailViewModel : BaseViewModel
{
public Order Order { get; set; }
public ICommand UpdateOrderCommand => new Command(UpdateOrder);
// I just made that up, so I have at least one service in a constructor
// because it makes things more complicated for me (but I need it), when I have something, that is injected (order manager)
// along with something, thats is passed (order)
private readonly IOrderManager _orderManager;
public OrderDetailViewModel(Order order, IOrderManager orderManager)
{
Title = "Detail";
Order = order;
_orderManager = orderManager;
}
private void UpdateOrder()
{
_orderManager.Update(Order);
}
}
// This class holds list of orders and when I click on some order, order detail is displayed so I have to pass that order to an orderdetail view model
public class OrdersViewModel : BaseViewModel
{
public ObservableCollection Orders { get; set; }
public ICommand SelectOrderCommand => new Command(async (orderInfo) => await SelectOrder(orderInfo));
private readonly IApiService _apiService;
private readonly INavigationService _navigation;
private readonly IViewModelFactory _factory;
// These services are injected as I would expect
public OrdersViewModel(INavigationService navigation, IApiService apiService, IViewModelFactory factory)
{
Title = "Orders";
Orders = new ObservableCollection<OrderRowDto>();
_navigation= navigation;
_apiService = apiService;
_factory = factory;
}
private async Task SelectOrder(OrderRowDto orderInfo)
{
var order = await _apiService.GetOrderAsync(orderInfo.Id);
// THIS IS WHERE I AM UNSURE. IF ITS OK TO DO IT THIS WAY
var viewModel = _factory.CreateOrderDetailViewModel(order);
await _navigation.PushAsync(new OrderDetailPage(viewModel));
}
}
//Factory
public class ViewModelFactory : IViewModelFactory
{
// this will be injected
private readonly IOrderManager _orderManager;
public ViewModelFactory(IOrderManager orderManager)
{
_orderManager = orderManager;
}
public OrderDetailViewModel CreateOrderDetailViewModel(Order order)
{
return new OrderDetailViewModel(order, _orderManager);
}
}
So I removed creation of view model from code behind, because this
_viewModel = IocContainer.Resolve<OrderDetailViewModel>();
would not allow me to pass that "Order" in constructor.
I know I could add this, If I would passed that order in page's constructor
_viewModel.Order = Order;
but it doesnt seems very nice.
So my question is, if that approach with factory is a good idea or not..
Because I dont want it to backfire later on
I was also wondering, if creating pages manually (new OrderDetailPage(..)) is good practice as well when attempting to use DI
Thank you. (sorry about code formating.. `` doesnt seems to do, what I want it to do)