0

I have a domain model that looks like this:

public class Procuct {
    string Name {get;set;}
    double Price {get;set;}
}

I have a repository that gets some item like so:

public Domain.Product GetProduct(int id) {
    // (maps from my ORM model to my domain model)
    return ProductMapping.MapProduct(dataService.GetProductEntity(id));
}

I like this because now I have domain models I can work with wherever I need.

In MVC, I want to use this model, with a few extras, so I have a ProductViewModel:

public class ProductViewModel : Domain.Product {
    public ViewBase Base {get;set;} // standarized helper stuff in here
}

In my controller, I want to grab a domain object from the repository, and return it as the viewmodel type (where I can then add the Base):

public ActionResult ShowProduct(int productID) {

    var model = new ProductViewModel();
    model = repository.GetProduct(productID) AS ProductViewModel;
    model.Base = new ViewBase() { /* settings here */ };

    return View(model);
}

The Smell

This seems like it should work well, but I have a smell about this line that I don't like:

model = repository.GetProduct(productID) AS ProductViewModel;

Apparently downcasting like this tends to be indicative of a less-than-stellar inheritance setup, but I'm not sure how to approach it. Is this generally acceptable if the downcast is to a "light" derived object, or am I doing something particularly wrong here?

What's the correct way to get a domain model into a derived viewmodel?

Further Note

At first I had set up my viewmodel like this:

public class ProductViewModel {
    public Domain.Product Product {get;set;}
    public ViewBase Base {get; set;}
}

... which actually makes this whole thing very easy because I can just get the domain model and apply it to the property. My issue with this is that I can't it wreaks havoc on the client-side and naming with the UI framework I'm using - far, far easier to have the properties at the "model level" (for lack of better term)

jleach
  • 7,410
  • 3
  • 33
  • 60
  • 3
    Generally, I think it is inappropriate for a ViewModel to inherit from a Model like this. The whole point of a ViewModel is to offer a representation of just the fields and properties you will interface with in a View. And it also will make it difficult to create an aggregate object. – stephen.vakil Apr 20 '16 at 15:48
  • @stephen.vakil - I don't disagree, but if I've got 30 fields that are all going into a view for detail editing, it seems like such a waste to create a specific viewmodel just to expose those same fields... I'd think I could just use the model as a base at that point (in many other cases I do use "just the view model"). – jleach Apr 20 '16 at 15:51
  • I agree with @stephen.vakil. Why don't you use [Automapper](http://automapper.org/) ? – adeel41 Apr 20 '16 at 16:07
  • I totally recommend making separate types for your viewmodels, but by all that is holy, stay away from automapper imo. you don't need the headaches that comes from leaky abstractions and automatic refactorings breaking code silently. – sara Apr 20 '16 at 16:14
  • @kai - I'm not much of heavy framework/helper tool guy... I generally prefer the slightly longer route of knowing what's actually going on (and having control of it). (I played with AutoMapper a few months ago and turned it down) – jleach Apr 20 '16 at 16:23

1 Answers1

1

Most of the time, UI requirements are very different from Domain requirements. I would create an additional mapping from your domain model to your view model. You may argue that there is some duplication, but as I mentioned above - your view model should be very different from your domain model. In theory, they serve different purposes and should contain different things.

If you feel that the mapping is too "boilerplate", then use a framework like AutoMapper.

https://github.com/AutoMapper/AutoMapper

William Xifaras
  • 5,212
  • 2
  • 19
  • 21
  • 1
    Between Stephen Vakil's upvoted comment and this, I guess it's yet another set of mappings for me... – jleach Apr 20 '16 at 16:22