MVVM 轻型应用程序 - 如何正确清理视图模型



我正在 WPF 中开发一个说明书窗口应用程序,该应用程序由一个窗口和几个用户控件组成,这些控件使用 MVVM Light 的消息与 relayCommands 相互替换。

应用程序使用从实体框架生成的数据库。除了第一次执行文件之外,所有之后出现的问题是程序显示许多警告和错误,例如:

Warning 1   Could not copy "...cookbookCookbook.ServicesDatabase1.mdf" to "binDebugDatabase1.mdf". Beginning retry 1 in 1000ms. The process cannot access the file '...cookbookCookbook.ServicesDatabase1.mdf' because it is being used by another process. Cookbook.Services

在视图模型定位器中,我有这个:

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

我用来切换用户控件的消息也正在创建视图模型的新实例,例如:

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

我已经玩弄了ViewModels,使它们成为单例,以确保系统中只有一个副本,但是SimpleIoc需要公共构造函数来注册。而且我也不知道这是否会帮助我的问题。另外,我没有告诉您的是,ViewModelLocator仅在xaml中使用,因此我什至没有其实例来清理这些东西。(我可能用错了,但我不知道应该如何使用)

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

如评论中所述,您正在获得

警告 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事件做出反应:

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

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

最新更新