MVVM轻量级应用程序 - 如何正确清理ViewModels

时间:2015-04-18 08:03:23

标签: c# wpf entity-framework mvvm viewmodellocator

我正在使用WPF中的cookbook窗口应用程序,它包含一个窗口和几个userControls,它们使用来自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的消息也在创建ViewModel的新实例,例如:

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

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

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

2 个答案:

答案 0 :(得分:2)

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

  

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

     

该进程无法访问文件'... \ cookbook \ Cookbook.Services \ Database1.mdf',因为它正由另一个进程使用。 Cookbook.Services

来自构建中编译器

警告(以及在经过足够的重试错误之后)消息,因为为正在运行/调试的应用程序创建了进程:

  1. 尚未完成,或
  2. 未关闭与数据库文件的所有连接。
  3. 因此,当您再次构建它时,其文件句柄仍处于打开状态,您无法复制打开的文件。

    很难根据您在问题中发布的代码确定导致此问题的直接原因,但这一行:

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

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

答案 1 :(得分:0)

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

另一种情况与在VS中调试应用程序有关。它默认使用VS主机进程完成,所以当你点击&#34;停止调试&#34;没有处理打开连接的按钮DbContexts,并且不会杀死VS主机进程。为避免这种情况,我建议您尝试禁用VS主机进程。您可以在项目属性中设置它 - &gt;调试 - &gt;并取消选中启用Visual Studio托管过程。但是,这可能会降低您在调试应用程序时开始运行的时间。