views:

145

answers:

6

In my ASP.NET app, I have a Person and a PersonViewModel.

My Person was generated by LinqToSql. It has the properties that will be persisted in the database.

My PersonViewModel has everything Person has, plus a couple "select lists", which will be used to populate combo boxes, as well as a FormattedPhoneNumber (which is basically a PhoneNumber with dashes and parenthases added).

I originally just made Person a property on my PersonViewModel, but I was thinking that would mean the page would have to "know" whether something was a Person property or PersonViewModel property. For example, for name, the view would request pvm.Person.Name, but for the phone number, the view would request pvm.FormattedPhoneNumber. If I use inheritance, then everything the view needs would always be a direct property of the view model, hence pvm.Name.

This sounds great, but, there is no real "is-a" relationship here (i.e., I don't really think it makes sense to say "a PersonViewModel is a Person), and it seems to fly in the face of "preferring composition over inheritance". Still, I'm having difficulty thinking of a scenario where I'd need the ability to swap out a Person for something else. If I do this, it would no longer be a PersonViewModel.

What say you? Inherit from Person or keep Person as a property (or something else entirely)? Why?

Update

Thanks for all the answers.

It looks like the inheritance idea has been almost universally rejected, and for some sound reasons:

  1. Decoupling the classes allows the ViewModel to contain only the properties from the domain model that are needed, plus any additional properties. With inheritance, you naturally expose all public properties from the domain model, which may not be a good idea.

  2. The ViewModel doesn't automatically need to change just because the domain model changed.

  3. As Jay mentioned, decoupling the ViewModel facilitates view-specific validation.

  4. As Kieth mentioned, using a Mapper (such as AutoMapper) can eliminate a lot of the tedious work in mapping common properties between classes.

+7  A: 

Deriving from an object implies an "is a" relationship. So in this case you would effectively be saying that a PersonViewModel is a Person. It seems that this is not likely to be the semantics that you really want to convey, so use composition instead of inheritance here.

In reality it probably doesn't really affect the maintainability of the application (other than having a few more dots here and there) but using composition certainly feels cleaner to me. In addition, if it's a PersonViewModel then presumably the view should know that that's the type it is dealing with.

Greg Beech
This is exactly what the OP said in the question.
Jay
Jay - It's hard to not repeat what he asked as he has already effectively answered his own question, and seems to be just looking for confirmation that composition is really the cleaner way to go. I've added some more narrative which I hope adds some value.
Greg Beech
Greg, I was actually looking for confirmation that inheritance would be "proper" in this particular case, but I'm happy to be talked out of it :) I'm not sure I agree with "the view *should* know that [`Person`] is the type it is dealing with". I was thinking, whatever I return from my controller is the type the view is dealing with. So, in this case, `PersonViewModel`, which is a superset of `Person`, is the type the view is dealing with.
DanM
I think he was saying that the view should know it is dealing with `PersonViewModel`, *not* `Person`.
Jay
Mapping between the 2 is still a better answer than using Composition. Especially when binding back, you should not be using your domain model directly.
Keith Rousseau
+1  A: 

I am thinking a partial class may be better here. keep the generated Person class and add anothe partial Person class with the extra stuff in it.

Pharabus
That's a great suggestion, exactly the same thing I was thinking. This lets you regenerate the Person model if the schema changes and keeps all the extra methods on Person where they belong.
John Duff
DanM
Ya, you're right about that, I didn't really think about having different views for the Person. In that case the composition model makes most sense (PersonModelView has a Person) i think.
John Duff
Your ViewModel and Domain model should be decoupled so they can version independently. That way if either one changes then you don't necessarily need to change the other - you would only need to update the mapping.
Keith Rousseau
+6  A: 

Make Person a private member, and expose the properties that your view requires in the PersonViewModel, even if those properties just pass through the corresponding property of Person.

pvm.Person.Name is the code smell here.


Edit:

You're also limiting yourself to client-side validation and/or validation in the domain model, the latter meaning that if you create a second or subsequent ViewModel for Person, you can't change the validation on Person's members. (Well, you could, but this is a violation of the open-closed principle and you're introducing view concerns into the domain.) By hiding your domain objects from the view, you give yourself a clean space to perform use-case-specific validation before changes get made to your domain object. The domain object may have its own set of validators, but they can be strictly domain-appropriate, without view-specific issues creeping in.

Jay
+1 For satisfying the Law of Demeter and keeping the LinqToSql objects in as limited a scope as possible. There may be no need to swap out a person, but if you ever need a different person provider then you could be in for some pain.
David Hall
...and if anyone else is ever likely to touch this code, it would be very easy for them to create a view that uses pvm.Person.SocialSecurityNumber. For an intellisense user, it appears to be part of the API. Maybe that property doesn't exist now, but if it were added, it would become visible through your viewmodel. Composition with a private member makes the exposure of each property a deliberate choice.
Jay
Thank you for this, Jay, +1. Your answer is probably what I'd naturally do in most circumstances, so I'm comfortable with that. Also, I've been kind of uncertain about how to handle "view validation" versus "model validation", and your edit provides some clarity on that as well.
DanM
+1  A: 

On the one hand:

What if you did this?

Person p = new PersonViewModel { //init some properties };

Would p do everything you'd expect a Person to do? If it would, then sure, use inheritance. If it would have some peculiarities related to the fact that it's really a PersonViewModel, and not a Person, then use composition.

On the other hand:

My inclination is to use inheritance largely as a way of avoiding lots of duplicated code. Since you're only inheriting from one parent to one child (rather than to many children), you're not avoiding a lot of duplicated code in the first place. So it's probably not worth it to use inheritance.

Kyralessa
+1  A: 

One trick I've used, not specifically with ASP.NET MVC but with a similar use case, is to create a class that contains specifically those items specific to the ViewModel class (i.e., those that are not just tunneling through to the person class), and supplying an extension property on the Person class to allow access to the extended properties. This isn't strictly a ViewModel in the classic sense of the word, but I feel it allows for much of the same functionality without introducing awkward inheritance or code duplication with tunneled properties.

Here's a quick example of what I'm talking about:

public static class PersonExtensions {
   public PersonViewData ViewData(this Person p) {
       return new PersonViewData(p);
   }
}

public class PersonViewData {
     public PersonViewData(Person p) {
         this._person = p;
     }

     private Person p;

     public string FormattedPhoneNumber {
         get { return p.PhoneNumber.ToPrettyString(); // or whatever }
     }
}
Ryan Brunner
That's really an interesting (and fluent) solution :) I believe you are also implicitly saying, I should use composition. I will some thought to whether I want to use extensions here, but +1 for the idea.
DanM
+1  A: 

You should not inherit from Person or use composition to have Person on PersonViewModel. Instead you should add the properties that you need to the PersonViewModel and map between them. Tools like AutoMapper (http://www.codeplex.com/AutoMapper) make this dead simple.

You should not expose your domain model directly to the View for a number of reasons, including security (under-posting and over-posting).

Keith Rousseau
I'm intrigued. I'm not quite sure from the documentation--is this a "flattening" scenario? Would you be willing to provide an example of how AutoMapper would be used in this situation? My `PersonViewModel` would need to have all the same properties as `Person` (`Name`, `PhoneNumber`, `PhoneNumberType`, and `Role`), plus two `SelectList` properties (`PhoneNumberTypes` and `Roles`) and `FormattedPhoneNumber`. How would I build my `ViewModel` with AutoMapper?
DanM
If you have all of the same properties then you would just create a mirror of your Domain Model plus the 2 select lists. In the AutoMapper configuration, you would just say to map the 2 entities and it would do so automatically as long as the property names are the same. AutoMapper really shines when there is some sort of conversion between properties, as you can define your conversions in one configuration class. It is dead simple in your case, though. Plus not having your domain model in your ViewModel allows you to change the ViewModel independent of your Domain Model.
Keith Rousseau
@Kieth, Thanks, I'll give it a try.
DanM
@Kieth, I've been playing with AutoMapper, but I was running into a lot of headaches trying to convert between model objects and view models. Then I spotted this answer from Jimmy Bogard himself, http://stackoverflow.com/questions/1701801/asp-net-mvc-automapper-parsing/1708524#1708524. So now, I've got to rethink this again. Any thoughts?
DanM
I think the issue that Jimmy was addressing was in regards to SelectLists only. He is saying that you don't want to use AutoMapper to do the conversions for those. What I typically do for SelectLists is to have a property on my ViewModel that is a getter only that will perform my LINQ query against its source (which is also a property that has been set on the ViewModel.
Keith Rousseau