How to inject .net core EF into WPF application

.net-standard c# entity-framework-core wpf

Question

I would like to inject my .NET Core EntityFramework DbContext (sitting in a .net standard library) into my WPF app.

I tried this Unity approach:

OnStartup

var container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
var mainWindow = container.Resolve<MainWindow>();

base.OnStartup(e);

MainWindow

private ApplicationDbContext _db;
[Dependency]
public ApplicationDbContext Db
{
    get
    {
        return _db;
    }
    set
    {
        _db = value;
    }
}

public MainWindow()
{
    //StandardDatabase.Commands.Test();

    InitializeComponent();
    DataContext = this;
    FrameContent.Navigate(new PageConsignments());
}

But I get this error at container.Resolve<MainWindow>():

The current type, System.Collections.Generic.IReadOnlyDictionary`2[System.Type,Microsoft.EntityFrameworkCore.Infrastructure.IDbContextOptionsExtension], is an interface and cannot be constructed. Are you missing a type mapping?

Does anyone know if I'm doing something wrong? Any suggestions on a better way of doing this are welcome

ApplicationDbContext

public ApplicationDbContext() : base() { }

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
{ }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseLazyLoadingProxies()
        .UseSqlServer("Server=L-TO-THE-APTOP\\SQLEXPRESS;Database=Maloli;Trusted_Connection=True;MultipleActiveResultSets=true");

    optionsBuilder.ConfigureWarnings(x => x.Ignore(CoreEventId.LazyLoadOnDisposedContextWarning));
}

As per Nkosi's suggestion, I removed the ApplicationDbContext(options) ctor from the context, and that got rid of the error.However I am now checking the value of Db here in MainWindow:

private ICommand goPack;
public ICommand GoPack
{
    get
    {
        return goPack
            ?? (goPack = new ActionCommand(() =>
            {
                var c = _db.Parts;
                FrameContent.Navigate(new PageConsignments());
            }));
    }
}

But it returns null

1
1
11/24/2018 3:29:08 PM

Accepted Answer

The original error was because the container was selecting the constructor that expected DbContextOptionsBuilder which the conateinr did not know how to resolve properly.

Since the context is being configured within the OnConfiguring override then there is no need for

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
{ }

Remove that constructor so the container resolve the context without errors.

Depending on the flow of dependency initialization and access to it, that context should really be explicitly injected into a view model and not directly on the View.

Following MVVM, have all the necessary dependencies and bindable properties in the view model

public class MainWindowViewModel : BaseViewModel {
    private readonly ApplicationDbContext db;

    public MainWindowViewModel(ApplicationDbContext db) {
        this.db = db;            
    }

    private ICommand goPack;
    public ICommand GoPack {
        get {
            return goPack
                ?? (goPack = new ActionCommand(() =>
                {
                    var c = db.Parts;
                    FrameContent.Navigate(new PageConsignments());
                }));
        }
    }
}

Update the View to depend on the view model

public class MainWindow : Window {
    [Dependency]
    public MainWindowViewModel ViewModel {
        set { DataContext = value; }
    }

    public MainWindow() {
        InitializeComponent();
        Loaded += OnLoaded;
    }

    void OnLoaded(object sender, EventArgs args) {
        FrameContent.Navigate(new PageConsignments());
    }
}

All that is left now is to make sure all dependencies are registered with container

public class App : Application {
    protected override void OnStartup(StartupEventArgs e) {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<ApplicationDbContext>();
        container.RegisterType<MainWindowViewModel>();
        container.RegisterType<MainWindow>();

        MainWindow mainWindow = container.Resolve<MainWindow>();
        mainWindow.Show();
    }
}

Where ever possible, The Explicit Dependencies Principle via constructor injection should be preferred over property injection.

But since most views do not lend well to constructor injection the latter is usually applied. By making sure the view model has all the necessary dependencies before injecting it into the view you ensure that all required values are available when needed.

4
11/24/2018 3:55:50 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow