Rollback and ViewModels with InterstedIn Attribute

Topics: Feature requests
Dec 7, 2012 at 7:01 AM

Hello

When i click an button on my view, the corresponding viewmodel saves Data into the Database with EF. But also other viewmodels are InterstedIn this SaveCommand. And so they have also to write some data into the database.

Now I want to SaveChanges in the database only when none of the viewmodels have an error. Is this possible to let the sending viewmodel know that everything is ok in the "interestedin" viewmodels

Coordinator
Dec 7, 2012 at 6:36 PM

No, you cannot validate "interestedin" view models. You can only validate chains of view models that are children or parents of each other.

Dec 10, 2012 at 9:36 AM
Edited Dec 10, 2012 at 11:47 AM

Ok.

But is there something to let me now that the last InterstedIn ViewModel is ready.

Something like OnPropertyChanging and OnPropertyChanged for commands.

 

Mybe OnCommandExecuting and OnCommandExecuted :-)  

That would be extremely helpful.

Developer
Dec 10, 2012 at 1:42 PM

Probably you could solve your issue using the ValidationToViewModel attribute if you have your viewmodels sorted in a parent child way.

In a parent viewmodel you can do this:

[ValidationToViewModel(Tag = "Person", IncludeChildViewModels = true )]
IValidationSummary personValidationSummary;
....

and initialize the save command (for instance):

SaveCommand = CommandHelper.CreateCommand(OnSave, () => personValidationSummary);

Then in your child viewmodels the only thing that you have to do is tag the validation results just like this:

        protected override void ValidateFields(List<IFieldValidationResult> validationResults)
        {
            if (string.IsNullOrEmpty(Name))
            {
                validationResults.Add(FieldValidationResult.CreateErrorWithTag(NameProperty, "The name field is empty", "Person"));   
            }      
         }

Dec 10, 2012 at 1:54 PM

Hm I dont think that this can solve my problem.

My problem:

One ViewModel sends a SaveCommand. Other viewmodels save their Propertys to database. At the end the sendig viewmodel have to make a Commit() on the DbContext. On Error it makes a Rollback.

But how do i know when all viewmodels are ready to save.

My viewmodels are not sorted in a parent child way.

Developer
Dec 10, 2012 at 2:57 PM
Edited Dec 10, 2012 at 4:31 PM

I don't know your problem but I think that always is a way to sort the UI in a parent-child but is a design decision.

Probably you can transform your UI into a main window (parent-viewmodel) with a Ribbon or a single button to save and a tabbed interface with the rest of your controls (child viewmodels) (repeat I don't know your problem). Then if you tag your errors you will be able to know when your child-viewmodels are ready to save from your main one.

The childs can be intersted in the main view model to do its job when the save command is executed.

It is a point of view.

Dec 11, 2012 at 6:45 AM
Edited Dec 11, 2012 at 8:08 AM

Hm.

Diffucult to understand. I think its not so easy to change my design.

I have a Tabpage as View and and ViewModel as Datacontext. On this Tabpage I have lots of UserControls (the count  is dynamic not static)  with their own ViewModels. Now the TabpageViewModel sends the SaveCommand,

So you mean my UserControls are the cilds and the Tabpage is the parent?

And I don't know what you mean with "tag the errors". Sorry.

Its enough for me to know that the InterestedIn ViewModels are ready.

Sorry if I understand your answer not in every point.

Developer
Dec 11, 2012 at 12:40 PM

Sorry, I will try to be clear

1) Yes, TabPage is the parent. The dynamic user controls are child. At the TabpageViewModel you can retrieve the child viewmodel validation result in a validation summary, with tag or not. The save command can hook to a validation summary.

2) If you want to label some validation results with a "tag" you can do this (for instance):

FieldValidationResult.CreateErrorWithTag(NameProperty, "The name field is empty", "Person") ==> where "Person" is the tag.

But this is an option, probably you don't need this. But is useful to filter the validation results.

3) If the case that viewmodel's parent-child relationship is not actually created you can ensure it existence just  calling the methods RegisterChildViewModel and SetParentViewModel.

Dec 13, 2012 at 6:06 AM

Ok. I try it. Maybe then I have more questions :-)

Dec 13, 2012 at 1:01 PM

Mybe you can give me a link in the documentation. I don't know how to start.

Developer
Dec 13, 2012 at 4:45 PM
Edited Dec 14, 2012 at 12:48 PM

Here are the documentation links:

http://catel.catenalogic.com/gs_viewscontrols_nested_user_controls_problem.htm

http://catel.catenalogic.com/getting_a_summary_of_validatio.htm

http://catel.catenalogic.com/validation_hooking_a_command_to_validatio.htm

Dec 14, 2012 at 7:33 AM
Edited Dec 14, 2012 at 7:37 AM

Ok. I think the validation helps me with an other problem. So thanks for that.

But in this case I think its not the solution.

Try to explain it with code.

 

[InterestedIn(typeof(SaveRecipeViewModel))]
public class PlaceViewModel : VieModelBase
{
    //...
 
    protected override void OnViewModelCommandExecuted(IViewModel viewModel, ICatelCommand command, Object commandParameter)    
    {       
        if (viewModel is SaveRecipeViewModel)       
        {            
            if (TagHelper.AreTagsEqual(command.Tag, "SaveRecipe"))            
            {                
                var vm = viewModel as SaveRecipeViewModel;                
                vm.SavePlacement(vm.RecipeName, _locationAddress, ActiveComponent.ID);            
            }        
        }  
     }
 }


 public class SaveRecipeViewModel : ViewModelBase
 {
    public SaveRecipeViewModel()
    {     
        SaveRecipe = new Command(OnSaveRecipeExecute,null,"SaveRecipe");
    }


    public void SavePlacement(string recipeName, LocationAddress locationAddress, int unitId)
    {
        _recipeDataManager.SavePlacement(recipeName, locationAddress, unitId);
    }
 
    private void OnSaveRecipeExecute()
    {

    // here I need to know when all Instances of Placeviewmodel are ready
        _recipeDataManager.Commit();
    }
}

Developer
Dec 14, 2012 at 12:33 PM
Edited Dec 14, 2012 at 12:48 PM

If your "ready" state notification is out of the validation loop or has no sense as validation then try with the message mediator http://catel.catenalogic.com/messaging_message_mediator.htm for a message base approach.

Dec 17, 2012 at 9:08 AM

My problem is that the button is disabled when the ready state is not set to true.

I don't want to disable the button. Just let me know when the childs are ready after pressed the button.

This is not possible with the validation Rules?

Developer
Dec 17, 2012 at 4:38 PM
Edited Dec 17, 2012 at 4:59 PM

Yes, you can retrieve  a validation summary with the extension method of the ViewModelBase GetValidationSummary just after you press the button.

This method includes the option to get  the child viewmodels validation results.

Dec 18, 2012 at 2:32 PM

Ok.

1. I press the button. A command is called in the sending viewmodel

2. The InterstedIn viewmodels have to notice about this command and have to do their action.

3. After that they set the ready property.

4. The sending viewmodel have no to commit the changes in da database. But in which method now call the GetValidationSummary to get notice of the readystate of the other viewmodels?

 

Maybe its easier to change a property in the command. And the InterestedIn ViewModels get notice of this Property. So I can use OnPropertyChanging and OnPropertyChanged. And I also don't need the Parent Child Order. Or what do you mean.

Developer
Dec 18, 2012 at 4:03 PM
Edited Dec 18, 2012 at 4:20 PM

Hm, there is a order issue.

Why the nested view models didn't know its ready state before you press the button?

All view models have it's own validation loop, I think that you don't have to wait for the interested-in signal to know the ready state.

So,

1) for each nested view models validation loop is executed setting they ready state.

2) press the button and a command is called, then call then GetValidationSummary at the parent view model to check the nested validation results.

Dec 18, 2012 at 6:21 PM

The problem ist that i want to Save in the database. So I press the button save. Then the viemodels save their value in the database. Without a commit. After that the sending viewmodel can make a commit. When any viemodel cant save to the database the sending viemodel can make a rollback.

So maybe all validationrules are ok. But they can not save their value in the database. I think there are lots of reasons why can't save to database.

 

So thats the reason why the nested view models didn't know its ready state before I press the button?

 

- >First press button, then save, when all ready make a commit.

Developer
Dec 18, 2012 at 7:22 PM

We will review the options to implement this feature, but I think you can solve your issue with a message base approach.

Dec 19, 2012 at 5:57 AM
Edited Dec 19, 2012 at 6:43 AM

Oh thanks. Sounds good. Now I will  have a look at the message base. Thank you for spending your time for me.