如何将.NET Core EF注入WPF应用程序中

3
我希望将我的.NET Core EntityFramework DbContext(位于.NET标准库中)注入到我的WPF应用程序中。我尝试了这种Unity方法OnStartup
var container = new UnityContainer();
container.RegisterType<ApplicationDbContext>();
var mainWindow = container.Resolve<MainWindow>();

base.OnStartup(e);

主窗口

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());
}

但是我在container.Resolve<MainWindow>()处遇到了这个错误:

当前类型System.Collections.Generic.IReadOnlyDictionary`2[System.Type,Microsoft.EntityFrameworkCore.Infrastructure.IDbContextOptionsExtension]是一个接口,无法构造。您是否缺少类型映射?

有人知道我是否做错了什么吗?欢迎任何更好的方法建议。

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));
}

根据Nkosi的建议,我从上下文中删除了ApplicationDbContext(options)构造函数,这样就解决了错误。然而,我现在在MainWindow中检查Db的值:
private ICommand goPack;
public ICommand GoPack
{
    get
    {
        return goPack
            ?? (goPack = new ActionCommand(() =>
            {
                var c = _db.Parts;
                FrameContent.Navigate(new PageConsignments());
            }));
    }
}

但它返回了null

可能是在WPF MDI应用程序中,组合根在哪里?的重复。 - Crowcoder
删除带有选项的构造函数。 - Nkosi
@Bassie 这取决于您何时检查其值。如果您在容器注入依赖项之前检查它,例如在构造函数中设置断点,则在初始化过程的该点上它肯定为 null。 - Nkosi
@Nkosi 我正在检查 ICommand 构造函数调用后的值(我已更新问题)。 - Bassie
@Nkosi 在调用RegisterType之后,container.Resolve<ApplicationDbContext>就能正常工作。 - Bassie
显示剩余8条评论
1个回答

5

最初的错误是由于容器选择了需要DbContextOptionsBuilder的构造函数,而容器无法正确解析它。

由于上下文在OnConfiguring重写中配置,因此不需要

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

移除该构造函数,以便容器无误解析上下文。

根据依赖项初始化和访问的流程,该上下文应该明确地注入到视图模型中,而不是直接放在视图中。

遵循MVVM,将所有必要的依赖项和可绑定属性放在视图模型中。

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());
                }));
        }
    }
}

更新视图以依赖于视图模型

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());
    }
}

现在所剩的就是确保所有依赖项都已在容器中注册。
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();
    }
}

尽可能使用构造函数注入显式依赖原则,而不是属性注入。

但由于大多数视图不适合构造函数注入,通常使用后者。通过确保视图模型在注入到视图之前具有所有必要的依赖项,可以确保在需要时所有所需的值都可用。


这对于 MainWindow 是有效的,但是当尝试在窗口中显示其他页面时,它们缺少必要的依赖项。 - Bassie
这只是设计选择的问题。您可以轻松地注入必要的页面并将它们传递。 - Nkosi
@Nkosi,使用这种方法,我需要显式地Dispose DbContext吗? - Jan Paolo Go

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接