1

I tried to implement connection to database using Entity Framework and Dependency Injection.
I want to create Host in App.xaml.cs.

public partial class App : Application
{
  
    public static IHost? AppHost { get; private set; }
    public App()
    {
        AppHost = Host.CreateDefaultBuilder()
             .ConfigureServices((hostContext, services) =>
             {
                 services.AddSingleton<LoginWindow>();
                 services.AddSingleton<LoginViewModel>();

                 services.AddDbContext<KnitterNotebookContext>(
                     options =>
                     {
                         string appSettingsPath = Path.Combine(ProjectDirectory.ProjectDirectoryFullPath, "appsettings.json");
                         string appSettingsString = File.ReadAllText(appSettingsPath);
                         AppSettings AppSettings = JsonConvert.DeserializeObject<AppSettings>(appSettingsString)!;
                         options.UseSqlServer(AppSettings.KnitterNotebookConnectionString);
                     });
             })
             .Build();
    }

    protected override async void OnStartup(StartupEventArgs e)
    {
        await AppHost!.StartAsync();
        var startupWindow = AppHost.Services.GetRequiredService<LoginWindow>();
        startupWindow.Show();
        base.OnStartup(e);
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        await AppHost!.StopAsync();
        base.OnExit(e);
    }     

I want to pass DbContext as parameter to ViewModel, but when I do, it throws exception.

 public class LoginViewModel : BaseViewModel
{
    public LoginViewModel(KnitterNotebookContext knitterNotebookContext)
   //public LoginViewModel()
    {
        KnitterNotebookContext = knitterNotebookContext;
        ShowRegistrationWindowCommand = new RelayCommand(ShowRegisterWindow);
        LogInCommandAsync = new AsyncRelayCommand(LogIn);
    }

    private KnitterNotebookContext KnitterNotebookContext { get; set; } 
 }

enter image description here

enter image description here

There is no problem if I use parameterless constructor of LoginViewModel and create new instances of KnitterNotebookContext with new(), but I want to pass it as a parameter. How to solve it?

3
  • 1
    You'll need to include the full stack trace. As text, not an image. We don't need to see visual studio, we need to copy / paste your code and errors. Commented Feb 10, 2023 at 0:11
  • 1
    You're showing us this failing on loginwindow but somehow loginviewmnodel and it's parameterless constructor come into this. How so? Commented Feb 10, 2023 at 9:16
  • 1
    It's usual to add any settings to the di container. Commented Feb 10, 2023 at 9:17

1 Answer 1

1

I happened to encounter the same problem at one time - i'm guessing that you are setting DataContext for MainWindow in xaml, like this:

<Window.DataContext>
    <local:LoginViewModel/>
<Window.DataContext>

Doing it this way, when your LoginWindow is created, tries to create new instance of its DataContext (your LoginViewModel), but it does not know how to resolve constructor - since you injected it with service, it's not parameterless anymore.

You have to provide a way, to enforce DataContext to be instantiated by ServiceProvider (a container for all injected services).

You could achieve this through use of a helper class, which is usable in xaml (inherits from MarkupExtension), like this one below.

public class ServiceDispatcher : MarkupExtension
{
    public static Func<Type, object> Resolver { get; set; }
    public Type Type { get; set; }
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Resolver?.Invoke(Type);
    }
}

And assigning it's service dispatching Resolver delegate in your App.xaml.cs

public App()
{
    AppHost = Host.CreateDefaultBuilder()
         .ConfigureServices((hostContext, services) =>
         {
             services.AddSingleton<LoginWindow>();
             services.AddSingleton<LoginViewModel>();

             services.AddDbContext<KnitterNotebookContext>(
                 options =>
                 {
                     string appSettingsPath = Path.Combine(ProjectDirectory.ProjectDirectoryFullPath, "appsettings.json");
                     string appSettingsString = File.ReadAllText(appSettingsPath);
                     AppSettings AppSettings = JsonConvert.DeserializeObject<AppSettings>(appSettingsString)!;
                     options.UseSqlServer(AppSettings.KnitterNotebookConnectionString);
                 });
         })
         .Build();
         
    ConfigureServiceProvider(AppHost.Services);
}

private static void ConfigureServiceProvider(IServiceProvider appHost)
{
    ServiceDispatcher.Resolver = (type) =>
    {
        return appHost.GetRequiredService(type);
    };
}

Now you can use your ServiceDispatcher to dynamically provide DataContext instance for your Views using syntax below.

<Window.DataContext>
    <base:ServiceDispatcher Type="{x:Type local:LoginViewModel}"/>
</Window.DataContext>
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much. You were right, I had configured DataContext as you thought. Thank you for help, I will fix my application.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.