Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to project ViewModel back into Model

Consider having a ViewModel:

public class ViewModel
{
    public int id { get; set; }
    public int a { get; set; }
    public int b { get; set; }
}

and an original Model like this:

public class Model
{
    public int id { get; set; }
    public int a { get; set; }
    public int b { get; set; }
    public int c { get; set; }
    public virtual Object d { get; set; }
}

Each time I get the view model I have to put all ViewModel properties one by one into Model. Something like:

var model = Db.Models.Find(viewModel.Id);
model.a = viewModel.a;
model.b = viewModel.b;
Db.SaveChanges();

Which always cause lots of problems. I even sometimes forget to mention some properties and then disaster happens! I was looking for something like:

Mapper.Map(model, viewModel);

BTW: I use AutoMapper only to convert Model to ViewModel but vice-versa I always face errors.

like image 674
ghazyy Avatar asked Mar 12 '16 16:03

ghazyy


People also ask

Can a ViewModel have multiple models?

ViewModel is nothing but a single class that may have multiple models. It contains multiple models as a property. It should not contain any method. In the above example, we have the required View model with two properties.

What logic should be in ViewModel?

ViewModel: ViewModel is the middle layer between the view and model. ViewModel contains the business logic, which manipulates the row data to show in the view. Any kind of function and methods should be in the view model. The iNotifyPropertyChanged interface is used in the ViewModel to achieve two-way binding.

Should ViewModel know about model?

Models are just the plain data, and a ViewModel is something that acts like a padding in between the two, that it should get information from the Model and pass it onto the View, and the View should know how to present it.

What is difference between ViewModel and model?

ViewModel in the MVC design pattern is very similar to a "model". The major difference between "Model" and "ViewModel" is that we use a ViewModel only in rendering views. We put all our ViewModel classes in a "ViewModels" named folder, we create this folder.


2 Answers

For this purpose we have written a simple mapper. It maps by name and ignores virtual properties (so it works with entity framework). If you want to ignore certain properties add a PropertyCopyIgnoreAttribute.

Usage:

PropertyCopy.Copy<ViewModel, Model>(vm, dbmodel);
PropertyCopy.Copy<Model, ViewModel>(dbmodel, vm);

Code:

public static class PropertyCopy
{
    public static void Copy<TDest, TSource>(TDest destination, TSource source)
        where TSource : class
        where TDest : class
    {
        var destProperties = destination.GetType().GetProperties()
            .Where(x => !x.CustomAttributes.Any(y => y.AttributeType.Name == PropertyCopyIgnoreAttribute.Name) && x.CanRead && x.CanWrite && !x.GetGetMethod().IsVirtual);
        var sourceProperties = source.GetType().GetProperties()
            .Where(x => !x.CustomAttributes.Any(y => y.AttributeType.Name == PropertyCopyIgnoreAttribute.Name) && x.CanRead && x.CanWrite && !x.GetGetMethod().IsVirtual);
        var copyProperties = sourceProperties.Join(destProperties, x => x.Name, y => y.Name, (x, y) => x);
        foreach (var sourceProperty in copyProperties)
        {
            var prop = destProperties.FirstOrDefault(x => x.Name == sourceProperty.Name);
            prop.SetValue(destination, sourceProperty.GetValue(source));
        }
    }
}
like image 70
Vanice Avatar answered Sep 20 '22 14:09

Vanice


Overall that might be not the answer, that you are looking for, but here's a quote from AutoMapper author:

I can’t for the life of me understand why I’d want to dump a DTO straight back in to a model object.

I believe best way to map from ViewModel to Entity is not to use AutoMapper for this. AutoMapper is a great tool to use for mapping objects without using any other classes other than static. Otherwise, code gets messier and messier with each added service, and at some point you won't be able to track what caused your field update, collection update, etc.

Specific issues often faced:

  1. Need for non-static classes to do mapping for your entities

    You might need to use DbContext to load and reference entities, you might also need other classes - some tool that does image upload to your file storage, some non-static class that does hashing/salt for password, etc etc... You either have to pass it somehow to automapper, inject or create inside AutoMapper profile, and both practices are pretty troublemaking.

  2. Possible need for multiple mappings over same ViewModel(Dto) -> Entity Pair

    You might need different mappings for same viewmodel-entity pair, based on if this entity is an aggregate, or not + based on if you need to reference this entity or reference and update. Overall this is solvable, but causes a lot of not-needed noise in code and is even harder to maintain.

  3. Really dirty code that's hard to maintain.

    This one is about automatic mapping for primitives (strings, integers, etc) and manual mapping references, transformed values, etc. Code will look really weird for automapper, you would have to define maps for properties (or not, if you prefer implicit automapper mapping - which is also destructive when paired with ORM) AND use AfterMap, BeforeMap, Conventions, ConstructUsing, etc.. for mapping other properties, which complicates stuff even more.

  4. Complex mappings

    When you have to do complex mappings, like mapping from 2+ source classes to 1 destination class, you will have to overcomplicate things even more, probably calling code like:

    var target = new Target();
    Mapper.Map(source1, target);
    Mapper.Map(source2, target);
    //etc..
    

    That code causes errors, because you cannot map source1 and source2 together, and mapping might depend on order of mapping source classes to target. And I'm not talking if you forget to do 1 mapping or if your maps have conflicting mappings over 1 property, overwriting each other.

These issues might seem small, but on several projects where I faced usage of automapping library for mapping ViewModel/Dto to Entity, it caused much more pain than if it was never used.

Here are some links for you:

  • Jimmy Bogard, author of AutoMapper about 2-way mapping for your entities
  • A small article with comments about problems faced when mapping ViewModel->Entity with code examples
  • Similar question in SO: Best Practices For Mapping DTO to Domain Object?
like image 20
Red Avatar answered Sep 19 '22 14:09

Red