How to create a view model from a model interface in MVVM style?

2.1k views Asked by At

I'm using a view model first approach and in some models I expose other models via an interface like this:

public class ModelA : IModel {}
public class ModelB : IModel {}
// plus yet unknown additional models

public class ViewModelA : IViewModel 
{
    ViewModelA(ModelA model){}
}
// a model may have several possible view models that should be selectable somehow
public class ViewModelAVariant : IViewModel 
{
    ViewModelAVariant(ModelA model){}
}
public class ViewModelB : IViewModel {}
{
    ViewModelA(ModelB model){}
}

public class ModelContainer
{
    public IModel[] SubModels { get { return new IModel { new ModelA(), new ModelB()};}}
}

public class ViewModelContainer
{
    private ModelContainer modelContainer;

    public IViewModel[] SubModels
    {
        get
        {
            return modelContainer.SubModels.Select( sm =>  ToViewModel(sm)).ToArray();
        }
    }


    private IViewModel ToViewModel(IModel model)
    {
        // what to insert here?
    }

}

Possible solutions

Variant 1:

Store a collection of IModel to IViewModel converters and use them in the ViewModel.

private IDictionary<type,Func<IModel,IViewModel>> converters;
private IViewModel ToViewModel(IModel model)
{
    return converters[model.GetType()](model);
}

Pros:

  • The model does not have to know about the view models
  • It's possible to select a ViewModels

Cons:

  • The process may fail at runtime if there is no suitable converter

Variant 2:

interface IModel
{
    IViewModel ToViewModel();
}

public class ModelA : IModel 
{
    // this enables view model selection
    public ModelA(Func<ModelA,IViewModel> converter)
    {
        this.converter = converter;
    }
    private Func<ModelA,IViewModel> converter;
    public IViewModel ToViewModel()
    {
        return converter(this);
    }
}

Pros:

  • It's possible to select a ViewModels
  • easy to use, hard to misuse

Cons:

  • The model has to know about the view models

I'm looking for a variant without the drawbacks.

1

There are 1 answers

2
Tseng On

Sounds like a good use case for AutoMapper.

Basically you would create a configuration, in it's simplest case it's:

Mapper.CreateMap<ModelA, ViewModelA>();
Mapper.CreateMap<ModelA, ViewModelAVariant<>();
Mapper.CreateMap<ModelB, ViewModelB>();

This assumes that all properties are named same (in both ViewModel and Model). It can also flatten properties, i.e. ModelA.Property is mapped, by convention to ModelAProperty if it exists.

If you have more complex mappings, you have to configure the mappings.

Then resolve it via

ModelA modelA = ... // retrieve ModelA 
ViewModelA viewModelA = Mapper.Map<ViewModelA>(modelA);

and for variations:

ModelA modelA = ... // retrieve ModelA 
ViewModelAVariation viewModelA = Mapper.Map<ModelA, ViewModelAVariation>(modelA);

You shouldn't use it the other way though, at least it's not recommended to convert from ViewModel to Domain/Business Model. It's main goal is for Model to DTO (Data Transfer Objects) or Model to ViewModel mappings.