MVVM light application - 如何正确清理 ViewModels

MVVM light application - how to properly clean ViewModels

我正在编写 WPF 中的食谱 window 应用程序,其中包含一个 window 和几个用户控件,它们使用来自 MVVM Light 的消息与 relayCommands 相互替换。

该应用程序使用从 entityFramework 生成的数据库。除了第一次执行文件之外,最终出现的问题是程序显示许多警告和错误,例如:

Warning 1   Could not copy "...\cookbook\Cookbook.Services\Database1.mdf" to "bin\Debug\Database1.mdf". Beginning retry 1 in 1000ms. The process cannot access the file '...\cookbook\Cookbook.Services\Database1.mdf' because it is being used by another process. Cookbook.Services

在 ViewModelLocator 中我有这个:

public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register<MainWindowViewModel>();
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<FoodTypeViewModel>();
            SimpleIoc.Default.Register<ShoppingCartViewModel>();
            SimpleIoc.Default.Register<MenuViewModel>();
            SimpleIoc.Default.Register<MenuListViewModel>();
            SimpleIoc.Default.Register<MenuCalendarViewModel>();
            SimpleIoc.Default.Register<ChooseFoodWindowViewModel>();
}

我用来切换 userControls 的消息也正在创建 ViewModels 的新实例,例如:

    BackToMainCommand = new RelayCommand(() =>
    {
        Messenger.Default.Send<ViewModelBase>(new MainViewModel());
    },
    () => true);

我已经尝试使用 ViewModel 将它们设为单例,以确保系统中只有单个副本,但 SimpleIoc 需要 public 构造函数进行注册。而且我也不知道这是否会对我的问题有所帮助。另外我没有告诉你的是 ViewModelLocator 仅在 xaml 中使用,所以我什至没有它的实例来清理这些东西。 (我可能用错了,但我不知道应该怎么用)

问题是我不知道如何以及在何处清理所有 ViewModel,因为它们是在我提到的许多地方创建的,其中一些可能包含 *.mdf 文件。

如评论中所述,您将获得

Warning 1 Could not copy "...\cookbook\Cookbook.Services\Database1.mdf" to "bin\Debug\Database1.mdf". Beginning retry 1 in 1000ms.

The process cannot access the file '...\cookbook\Cookbook.Services\Database1.mdf' because it is being used by another process. Cookbook.Services

构建中来自 编译器 的警告(以及多次重试错误后)消息,因为,为应用程序创建的进程是 running/debugging:

  1. 尚未完成,或
  2. 没有关闭与数据库文件的所有连接。

所以当你再次构建它时,它的文件句柄仍然是打开的,你不能复制打开的文件。

很难从您在问题中发布的代码确定造成这种情况的直接原因是什么,但是这一行:

Messenger.Default.Send<ViewModelBase>(new MainViewModel());

显然是有问题的,因为它 return 是一个新实例,而不是 SimpleIoC 容器中的 单例生命周期 实例。虽然从适当的 DI 角度来看仍然很丑陋,但您可以将其更改为:

Messenger.Default.Send<ViewModelBase>(ServiceLocator.Current.GetInstance<MainViewModel>());

因此它不会创建您的 MainViewModel 的新实例,而是 return 来自 IoC 容器的实例。

此外,您可能希望确保您的数据库上下文已在您的容器中注册,并注入到需要它的视图模型中。说明这一点(假设您的数据库 context/service class 被称为 MyDbContext,实现 IMyDbContext,并将连接字符串作为其构造函数参数):

SimpleIoc.Default.Register<IMyDbContext>(() => new MyDbContext(GetMyConnectionString()));

现在,您还必须确保在应用程序退出时执行适当的清理,以便在 IMyDbContext 实例上调用 Dispose,以及应用程序中需要处理的任何其他潜在资源。如果这还没有完成,通过 MVVM Light,您可以通过对 Application 上的 Application.Exit Event 做出反应来做到这一点:

您的问题可能是由您使用 DbContext 的方式引起的。你没有在你的问题中提出你如何处理,所以我会试着猜测你这边会发生什么。您应该始终确保在使用 DbContext 后立即处理它。不应在整个应用程序生存期内都保留它。我没有看到您正在使用 IoC 注册它,所以我假设您只是在 ViewModels 中的某个地方实例化它。在这种情况下,您应该始终将 DbContext 对象放在 using() 中以确保它们被处置。如果你愿意,当你以普通方式关闭你的应用程序时,你当然不应该打开任何连接到你的数据库。

另一个案例与在 VS 中调试您的应用程序有关。默认情况下,它是通过 VS 托管进程完成的,因此当您点击 "stop debugging" 按钮时,打开连接的 DbContexts 不会被释放,VS 托管进程也不会被终止。为避免此类情况,我建议您尝试禁用 VS 托管进程。您可以在项目属性 -> 调试 -> 中设置它,然后取消选中启用 Visual Studio 托管进程。但是,这可能会减少您的应用程序在调试时开始 运行 的时间。