Forum Xamarin Xamarin.Forms

Xamarin.Forms(MVVM) Depdency Injection, “Unable to resolve service for type” Error

Cdn_EuroCdn_Euro Member ✭✭✭

The big picture, I am trying to inject a SQL Lite repository into my viewmodels thats why I installed the Dependecy Injection packages and tried to set it up, but I get the following error when trying to load the Login page:

System.InvalidOperationException: 'Unable to resolve service for type
'Xamarin.Forms.Page' while attempting to activate
'MyXamarinProject.ViewModels.LoginViewModel'.'

I am using the NuGet Packages:

Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection.Abstractions

In Locator.cs:

public static class Locator
    {
        private static IServiceProvider _serviceProvider;

        public static void Initialize()
        {
            var services = new ServiceCollection();

            services.AddSingleton<IMobileUserRepository, MobileUserRepository>();

            services.AddTransient<LoginViewModel>();
            services.AddTransient<MainPageViewModel>();
            services.AddTransient<SignUpPageViewModel>();

            services.AddTransient<LoginView>();      
            services.AddTransient<MainPage>();
            services.AddTransient<SignUpPage>();

            _serviceProvider = services.BuildServiceProvider();
        }

        public static T Resolve<T>() => _serviceProvider.GetService<T>();
    }

in App.xaml.cs:

public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            Locator.Initialize();
            InitializeRepositories();


            if (!IsUserLoggedIn)
            {
                MainPage = new NavigationPage(Locator.Resolve<LoginView>());
                // ERROR happens after this step, after calling the Locator static method: public static T Resolve<T>() => _serviceProvider.GetService<T>();
            }
            else
            {
                MainPage = new NavigationPage(Locator.Resolve<MainPage>());
            }
        }

        private static void InitializeRepositories()
        {
            IMobileUserRepository _mobileUserRepository = Locator.Resolve<IMobileUserRepository>();
            _mobileUserRepository.Initialize();
        }


        protected override void OnStart()
        {
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }

In BaseWithHttp.cs:

public class BaseWithHttp : BindableBase
    {
        public INavigation Navigation { get; set; }
        protected async Task<bool> SendToClientApi(Func<ClientApiClient, Task> clientAction)
        {
            try
            {
                var client = new ClientApiClient(Constants.ClientApiUrl, App.UserToken);
                await clientAction(client);
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
                return false;
            }
        }
    }

in LoginViewModel.cs:

public class LoginViewModel : BaseWithHttp
    {
        private Page _loginPage;
        private readonly IMobileUserRepository _mobileUserRepository;

        public LoginViewModel(Page loginPage,
                              IMobileUserRepository mobileUserRepository)
        {
            _loginPage = loginPage;

            _signupCommand = new DelegateCommand(ExecuteSignupCommand);
            _loginCommand = new DelegateCommand(ExecuteLoginCommand);

            _mobileUserRepository = mobileUserRepository;
        }

        private string _userName;
        public string UserName
        {
            get
            {
                return _userName;
            }
            set
            {
                _userName = value;
                RaisePropertyChanged();
            }
        }

        private string _password;
        public string Password
        {
            get
            {
                return _password;
            }
            set
            {
                _password = value;
                RaisePropertyChanged();
            }
        }

        private string _loginMessage;
        public string LoginMessage
        {
            get
            {
                return _loginMessage;
            }
            set
            {
                _loginMessage = value;
                RaisePropertyChanged();
            }
        }

        private DelegateCommand _signupCommand;
        public ICommand SignupCommand => _signupCommand;
        private async void ExecuteSignupCommand()
        {
            await _loginPage.Navigation.PushAsync(new SignUpPage());
        }

        private DelegateCommand _loginCommand;
        public ICommand LoginCommand => _loginCommand;
        private async void ExecuteLoginCommand()
        {
            var user = new User
            {
                Username = this.UserName,
                Password = this.Password
            };

            DependencyService.Get<ICustomActivityIndicatorPage>().InitActivityPage(new LoggingInActivityIndicatorPage());
            DependencyService.Get<ICustomActivityIndicatorPage>().ShowActivityPage();
            var isValid = await TryLogin(user);
            if (isValid)
            {
                MobileUser mobileUser = new MobileUser // this is just for test
                {
                    UserID = 1,
                    SiteID = 1,
                    LoggerID = 1

                };

                await _mobileUserRepository.AddORUpdateMobileUser(mobileUser);
                var mobileUsers = _mobileUserRepository.GetMobileUsers();

                _loginPage.Navigation.InsertPageBefore(new MainPage(), _loginPage);             
                DependencyService.Get<ICustomActivityIndicatorPage>().HideActivityPage();
                await _loginPage.Navigation.PopAsync();             
            }
            else
            {
                App.IsUserLoggedIn = false;
                this.LoginMessage = "Login failed";
                this.Password = string.Empty;
                DependencyService.Get<ICustomActivityIndicatorPage>().HideActivityPage();
            }
        }

        private async Task<bool> TryLogin(User user)
        {
            var result = await SendToClientApi(async (clientClient) =>
            {
                var clientResponse = await clientClient.Auth_SignInAsync(new ProjectServer.Api.Library.Client.SignInDtoOfUserAndGuid() { User = user.Username, Password = user.Password });
                if (clientResponse != null)
                {
                    App.IsUserLoggedIn = true;
                    App.UserToken = clientResponse?.Token;
                    App.RefreshToken = clientResponse?.RefreshToken;
                }
            });

            return result;
        }
    }

in LoginView.xaml.cs:

[XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class LoginView : ContentPage
    {
        public LoginView(LoginViewModel loginPageViewModel)
        {
            InitializeComponent();

            loginPageViewModel.Navigation = Navigation;
            BindingContext = loginPageViewModel;
        }
    }

Any help is appreciated.

Answers

  • jezhjezh Member, Xamarin Team Xamurai
    edited 9:09AM

    I tried to reproduce this issue by creating a new app according to the code you posted, but failed.

    What's the IMobileUserRepository , MobileUserRepository and the ClientApiClient?

    Could you please post more details about this issue?

  • Cdn_EuroCdn_Euro Member ✭✭✭
    edited October 16

    @jezh Thank you so much for your help. The api client is just a swagger api, calls some endpoints to log in the user witht the db, can reproduce with some fake data, the log in in part shouldnt make a difference. Here are the missing classes:

    public interface IMobileUserRepository
        {
            Task Initialize();
            Task<List<MobileUser>> GetMobileUsers();
            Task AddORUpdateMobileUser(MobileUser user);
        }
    
    public class MobileUserRepository : IMobileUserRepository
        {
            private SQLiteAsyncConnection _connection;
    
            public async Task AddORUpdateMobileUser(MobileUser user)
            {
                if (user.Id != 0)
                {
                    _ = await _connection.UpdateAsync(user);
                }
                else
                {
    
                    _ = await _connection.InsertAsync(user);
                }
            }
    
            public Task<List<MobileUser>> GetMobileUsers() => _connection.Table<MobileUser>().ToListAsync();
    
            public async Task Initialize()
            {
                if (_connection != null) return;
    
                _connection = new SQLiteAsyncConnection(Path.Combine(Xamarin.Essentials.FileSystem.AppDataDirectory, AppConstants.Constants.DatabaseFilename));
    
                await _connection.CreateTableAsync<MobileUser>();
            }
        }
    
  • jezhjezh Member, Xamarin Team Xamurai
    edited 9:08AM

    Thank you so much for sharing your code snippets,but what's the ClientApiClient and ICustomActivityIndicatorPage ?

    If it is convenient for you, could you please share the basic code snippets which could help us to reproduce this issue?

Sign In or Register to comment.