Lewis Cianci
Lewis Cianci

Reputation: 1065

For a small MVC Application, how do you populate DTO's and ViewModels?

At the moment I know that, for MVC applications, Views should be populated out of ViewModels, Controllers should be kept slim, and that you shouldn't ever really expose your Entity Framework entities directly to the controllers.

Where I'm getting stuck, is where to put the functions that take the Model (from the database) and translate it into ViewModel data.

At the moment I have an entity that represents a series of tasks in the database. This is the class:

 public class Task
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Contact Contact { get; set; }
        public string Description { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateModified { get; set; }
        public int? QuotedHours { get; set; }
        public int? UsedHours { get; set; }
        public virtual ICollection<Attachment> Attachments { get; set; }
        public virtual ICollection<Comment> Comments { get; set; }
        public virtual Status Status { get; set; }
        public int RecoveryStatusID { get; set; }
        public virtual RecoveryStatus RecoveryStatus { get; set; }
    }

I have a DTO class which looks exactly the same, but uses Lists instead of virtual ICollections. It looks like this:

  public class Task
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Contact Contact { get; set; }
        public string Description { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateModified { get; set; }
        public int? QuotedHours { get; set; }
        public int? UsedHours { get; set; }
        public List<Attachment> Attachments { get; set; }
        public List<Comment> Comments { get; set; }
        public virtual Status Status { get; set; }
        public int RecoveryStatusID { get; set; }
        public RecoveryStatus RecoveryStatus { get; set; }
    }

Then I have a View Model that I would like to populate.

  public class TaskIndexViewModel
    {
        public string CategoryName { get; set; }
        public List<DTO.Task> Tasks { get; set; }
    }

So:

Any help would be greatly appreciated :)

Upvotes: 0

Views: 840

Answers (3)

Dean Swiatek
Dean Swiatek

Reputation: 1567

I use AutoMapper, and it is a very easy and convenient way to map a lot of classes together.

Mapper.CreateMap<Task, DTO.Task>()
  .ForMember(dest => dest.Attachments, opt => opt.MapFrom(src => src.ToList<Attachment>())
  .ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src.ToList<Comment>())
  .ReverseMap()
    .ForMember(dest => dest.Attachments, opt => opt.MapFrom(src => src as ICollection<Attachment>)
    .ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src as ICollection<Comment>);

Now you can say

var taskDto = Mapper.Map<Task, DTO.Task>(task);

But the reversemap part also lets you do the reverse as well.

var task = Mapper.Map<DTO.Task, Task>(taskDto);

Ideally you will want to put your mappings into a profile.

Also, there is no harm in not creating DTOs. If you decide not to create DTOs, you are binding your controllers to your entities, and in a small application this may never be a problem but if the application is to ever grow, you may find implementing differing logic may become difficult and you may be forced to put something in the controller that technically doesn't belong there. So if you decide not to use DTOs, you should be sure that the application will remain small.

Upvotes: 0

trashr0x
trashr0x

Reputation: 6565

I disagree with the last section of @raykrow's answer. This kind of structure:

var vm = new TaskIndexViewModel
{
    Tasks = db.Tasks.ToList(),
    CategoryName = "This Cool Cat"
};

...results in the Tasks viewmodel property becoming tightly bound to the EF entity it's doing a .ToList() on. What happens if you decide to make the switch from EF to another ORM?

Having a viewmodel which is (even) identical to the domain class is still beneficial. As to how can you easily map the results from your model to your viewmodel, this is why libraries like AutoMapper exist. Without AutoMapper:

// tasks becomes an IEnumerable<TaskViewModel>
var tasks = db.Tasks.Select(x => new TaskViewModel { A = x.A, B = x.B }).ToList();

var vm = new TaskIndexViewModel
{
    Tasks = tasks,
    Foo = bar
};

Upvotes: 0

rayepps
rayepps

Reputation: 2092

Check out this blog post I made

http://krow.tech/posts/Essentials-of-AutoMapper

(I'm going to mock some code here)

var db = new MyEfContext(); // Or however you create or get your ef context IoC maybe?

IEnumerable<Task> tasks = db.Tasks
    .Select(e => Mapper.Map<DTO.Task>(e));

var vm = new TaskIndexViewModel
{
    Tasks = tasks,
    CategoryName = "This Cool Cat"
};

With this you can map your Task Entity to your Task DTO. Your mapping might look like...

config.CreateMap<Task, DTO.Task>()
    .ForMember(dest => dest.Attachments.ToList(), opt => opt.MapFrom(src => src.Attachments))

However, given the names on your Entity and DTO are identical you might be better off writing some TypeResolvers (see link above) for AutoMapper to use when converting ICollection to IList then simply tell AutoMapper:

config.CreateMap<Task, DTO.Task>();

I hope that answers your question. As we talked about in comments I still think the mapping to a designated DTO is overkill, even in this case where you pass the Entity directly to the VM (anyone feel free to correct me). I would simply to this:

var vm = new TaskIndexViewModel
{
    Tasks = db.Tasks.ToList(),
    CategoryName = "This Cool Cat"
};

See the post linked above for more context if needed.

Upvotes: 2

Related Questions