locked
Dependency Injection best practice ? RRS feed

  • Question

  • User369799 posted

    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)

    Tuesday, August 7, 2018 7:20 PM

All replies

  • User56293 posted

    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).

    Friday, August 10, 2018 9:06 AM