简体   繁体   中英

MVVM light application - how to properly clean ViewModels

I am working on a cookbook window application in WPF which consist of one window and several userControls that are replacing each other with relayCommands using messages from MVVM Light.

The application works with a DB that is generated from the entityFramework. The problem that occurs after all but the first execution of the file is that the program shows many warings and errors such as this one:

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

In the ViewModelLocator I have this:

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

And also messages I am using to switch the userControls are creating new instances of ViewModels, such as:

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

I have toyed with the ViewModels to make them singleton to make sure there are only single copies in the system, but SimpleIoc needs public constructors for registering. And also I don't know if that would even help my problem. Also what I didn't tell you is that the ViewModelLocator is used only in xaml so I don't even have its instance to clean the stuff. (I am probably using it wrong but I don't know how it should be used)

The problem is that I don't know how and where to clean all the ViewModels since they are beying created on many places I've mentioned and some of them are probably holding the *.mdf file.

As mentioned in the comments, you are getting the

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

warning (and after sufficient retries error) message from the compiler in a build because, the process created for application that you were running/debugging:

  1. has not yet completed, or
  2. not closed all connections to the database file.

So when you build it again, its file handle is still open and you cannot copy over the open file.

It is difficult to establish from the code you have posted in your question what the direct cause of this is, but this line:

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

clearly is problematic, because it returns a new instance, instead of the singleton lifecycle instance from the SimpleIoC container. Although still ugly from a proper DI perspective, you could change it to:

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

So it will not create a new instance of your MainViewModel , but return the one from the IoC container.

Furthermore, you may want to make sure that your database context is registered in your container, and injected into the view models that need it. Illustrating this (assuming your database context/service class is called MyDbContext , implements IMyDbContext , and takes a connection string as its constructor argument):

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

Now, you must also ensure that on application exit, proper cleanup is performed so that Dispose is called on the IMyDbContext instance, and any other potential resources in your application that require disposal. If this is not already done, through MVVM Light, you can do that by reacting to the Application.Exit Event on your Application :

Your problem is probably caused by the way you use your DbContext. You did not present in your question how you handle so I will try to guess what happens on your side. You should always make sure that after using DbContext you immediately dispose it. It should not be kept for the whole application living time. I do not see that you are registering it with your IoC so I assume you just instantiates it somewhere within your ViewModels. In such case you should always have your DbContext objects within using() to assure they are disposed. If you will fullfil that you certainly should not have any connection open to your db when you close your application in ordinary way.

The other case is connected to debugging your application in VS. It is done by default with VS hosting process, so when you hit "stop debugging" button DbContexts with opened connections are not disposed and VS hosting process is not killed. To avoid such situations I would recommend you to try to disable VS hosting process. You can set it in project properties -> Debug -> and uncheck Enable the Visual Studio hosting process. However this may lower down a bit time in which your application starts to run when you debug it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM