Forum Xamarin.Forms

Dependency Injection best practice ?

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)

Answers

  • PaulDistonPaulDiston USUniversity ✭✭✭✭

    Not sure this is best practise, but you might want to consider creating your ViewModel before dealing with the View side of things, that way you can set your order property. Then you can construct your View, set it's BindingContext and then Push to the View. That way you are not tying your View directly to a particular ViewModel type, which could allow you to switch out the ViewModel for a different ViewModel at a later stage (although this might be rare).

Sign In or Register to comment.