【问题标题】:MVVM light application - how to properly clean ViewModelsMVVM 轻量级应用程序 - 如何正确清理 ViewModels
【发布时间】:2015-06-25 05:11:12
【问题描述】:

我正在 WPF 中开发一个食谱窗口应用程序,它由一个窗口和几个用户控件组成,这些用户控件使用来自 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>();
}

我用来切换用户控件的消息也在创建新的 ViewModel 实例,例如:

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

我玩弄了 ViewModel 以使它们成为单例,以确保系统中只有单个副本,但 SimpleIoc 需要公共构造函数来注册。而且我不知道这是否会帮助我解决问题。另外我没有告诉你的是 ViewModelLocator 仅在 xaml 中使用,所以我什至没有它的实例来清理这些东西。 (可能是我用错了但是不知道怎么用)

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

【问题讨论】:

  • 为您正在运行/调试的应用程序创建的进程 (1) 尚未完成,或 (2) 未关闭与数据库文件的所有连接。所以如果你再次构建它,它的文件句柄仍然是打开的,你不能复制打开的文件。请注意,调试时,特别是如果您使用“停止调试”按钮停止调试会话,调试过程不会直接终止。退出 Visual Studio 并重新启动它通常可以解决此问题。
  • 嗯,这些连接位于一些使用数据库服务的 ViewModel 中。我认为那些 ViewModel 被定位器或消息所控制(如果我没记错的话)。我知道重新启动 Visual Studio 可以解决这个问题,但我需要修复它,这样我才能拥有工作的应用程序,因为即使我使用退出按钮或窗口中的十字退出应用程序也会发生这种情况。

标签: c# wpf entity-framework mvvm viewmodellocator


【解决方案1】:

正如 cmets 中提到的,你得到了

警告 1 无法将“...\cookbook\Cookbook.Services\Database1.mdf”复制到“bin\Debug\Database1.mdf”。在 1000 毫秒内开始重试 1。

该进程无法访问文件“...\cookbook\Cookbook.Services\Database1.mdf”,因为它正被另一个进程使用。食谱.服务

警告(以及在充分重试错误后)消息来自构建中的编译器,因为为您正在运行/调试的应用程序创建的进程:

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

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

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

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

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

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

因此它不会创建MainViewModel 的新实例,而是从 IoC 容器返回一个。

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

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

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

【讨论】:

    【解决方案2】:

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

    另一种情况与在 VS 中调试应用程序有关。默认情况下,它是通过 VS 托管进程完成的,因此当您点击“停止调试”按钮时,打开连接的 DbContexts 不会被释放,VS 托管进程也不会被杀死。为避免这种情况,我建议您尝试禁用 VS 托管进程。您可以在项目属性 -> 调试 -> 中设置它并取消选中启用 Visual Studio 托管进程。但是,这可能会缩短应用程序在调试时开始运行的时间。

    【讨论】:

      猜你喜欢
      • 2015-05-21
      • 2018-10-27
      • 1970-01-01
      • 1970-01-01
      • 2012-12-24
      • 1970-01-01
      • 1970-01-01
      • 2011-05-08
      • 1970-01-01
      相关资源
      最近更新 更多