16

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.

Amal K
  • 4,359
  • 2
  • 22
  • 44
ghazyy
  • 649
  • 2
  • 7
  • 22
  • 2
    If you face some problems with automapper then post the code you use to map models to viewmodels. – Eugene Podskal Mar 12 '16 at 16:05
  • you mean i can use automapper in vise versa mode ? – ghazyy Mar 12 '16 at 16:08
  • My experience with automapper is limited, but I am fairly sure that it is possible to configure which properties should be mapped and in a which way, so you can ignore excess properties or calculate the missing ones. – Eugene Podskal Mar 12 '16 at 16:10
  • @ghazyy: you can use it, but you need create the map from viewmodel to model too, like `Mapper.CreateMap()` – Arturo Menchaca Mar 12 '16 at 16:11
  • 3
    @EugenePodskal: AutoMapper should only be used for Models to ViewModels or Model to DTO transformations, not the other way around as it's not meant for this type of mapping, which is also the official stance of the author of AutoMapper – Tseng Mar 12 '16 at 20:40
  • @Tseng Thanks, I just didn't consider possible issues that such mapping may cause. Though it is possible to accomplish with automapper, I agree that it is probably not something that should be done. – Eugene Podskal Mar 12 '16 at 22:07

4 Answers4

14

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:

Community
  • 1
  • 1
Red
  • 2,728
  • 1
  • 20
  • 22
5

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));
        }
    }
}
Vanice
  • 676
  • 5
  • 15
0

I want to address a specific point in your question, regarding "forgetting some properties and disaster happens". The reason this happens is that you do not have a constructor on your model, you just have setters that can be set (or not) from anywhere. This is not a good approach for defensive coding.

I use constructors on all my Models like so:

    public User(Person person, string email, string username, string password, bool isActive)
    {
        Person = person;
        Email = email;
        Username = username;
        Password = password;
        IsActive = isActive;                    
    }

    public Person Person { get; }          
    public string Email { get;  }
    public string Username { get; }
    public string Password { get; }
    public bool IsActive { get; }

As you can see I have no setters, so object construction must be done via constructor. If you try to create an object without all the required parameters the compiler will complain.

With this approach it becomes clear, that tools like AutoMapper don't make sense when going from ViewModel to Model, as Model construction using this pattern is no longer about simple mapping, its about constructing your object.

Also as your Models become more sophisticated you will find that they differ significantly from your ViewModels. ViewModels tend to be flat with simple properties like string, int, bool etc. Models on the other hand often include custom objects. You will notice in my example there is a Person object, but UserViewModel would use primitives instead like so:

public class UserViewModel
{
   public int Id { get; set; }
   public string LastName { get; set; }
   public string FirstName { get; set; }
   public string Email { get; set; }
   public string Username { get; set; }
   public string Password { get; set; }
   public bool IsActive { get; set;}
}

So mapping from primitives to complex objects limits AutoMapper's usefulness.

My approach is always manual construction for the ViewModels to Model direction. In the other direction, Models to ViewModels, I often use a hybrid approach, I would manually map Person to FirstName, LastName, I'd but use a mapper for simple properties.

Edit: Based on the discussion below, AutoMapper is better at unflattering than I believed. Though I will refrain from recommending it one way or the other, if you do use it take advantage of features like Construction and Configuration Validation to help prevent silent failures.

Louise Eggleton
  • 969
  • 2
  • 15
  • 27
  • @LucianBargaoanu Thanks, that's actually more than I expected from AutoMapper (I actually use Value Injecter), but it's still doesn't address mapping from primitives to custom objects. I am aware that you can have custom type conversions with AutoMapper, but at that point you are pretty much creating a builder / factory anyway, so why not just use the builder / factory approach in the first place without Automapper? – Louise Eggleton Jan 03 '20 at 15:11
  • http://docs.automapper.org/en/latest/Reverse-Mapping-and-Unflattening.html – Lucian Bargaoanu Jan 03 '20 at 16:03
  • @LucianBargaoanu This relies on my DTOs / ViewModels following a specific naming convention. Fair enough I could live with that, but will the compiler complain if there is a misspelling or property missing? Admittedly my experience is based on ValueInjector, but I find mappers tend to silently fail in those scenarios. – Louise Eggleton Jan 03 '20 at 16:31
  • http://docs.automapper.org/en/latest/Configuration-validation.html I would get up to date if I were you :) – Lucian Bargaoanu Jan 03 '20 at 16:33
  • @LucianBargaoanu Fair enough, I will have another look at AutoMapper as you have answered my objections, but one question remains: Does Jimmy Bogard still recommend against mapping from DTO to Model? He has said that it was never intended to be used in that way. Is that old info? – Louise Eggleton Jan 03 '20 at 16:43
  • It's not about what @jbogard is doing, it's about what _you_'re trying to do :) You just need to understand the tool and what it can do for you. – Lucian Bargaoanu Jan 03 '20 at 16:46
  • @LucianBargaoanu Well, when the creator of a tool says not to use it is a specific way, I tend to listen, he knows it best. – Louise Eggleton Jan 03 '20 at 16:47
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205307/discussion-between-louise-eggleton-and-lucian-bargaoanu). – Louise Eggleton Jan 03 '20 at 16:52
0

Use Newtonsoft.Json to serialize viewmodel first and deserialize it to model.

First we need to Serialize the viewmodel:

var viewmodel = JsonConvert.SerializeObject(companyInfoViewModel);

Then Deserialize it to model:

var model = JsonConvert.DeserializeObject<CompanyInfo>(viewmodel);

Hence, all the data is passed from viewmodel to model easily.

One Line Code:

var company = JsonConvert.DeserializeObject<CompanyInfo>(JsonConvert.SerializeObject(companyInfoViewModel));
adumred
  • 144
  • 5