Model or ViewModel when representing a subset of data in MVVM? - c#

If I have a complex model representing a large amount of data, and I only wish to display a cut-down version of that model (e.g. Name, Description), what is the best approach in MVVM?
Most solutions I can find seem to assume that the data is already present in memory and recommend using a new ViewModel that exposes only the fields required.
However rather than select out all of the data from the database, it would be preferable to select just what is necessary. Do I then create a new model to hold that data? Selecting directly into the ViewModel is possible but feels like the wrong thing to do. Likewise using a new model to represent a different version of the same data also feels off somehow.
What is the accepted method of doing this?
As a simple example (Simple enough class that I wouldn't ordinarily do this):
public class User {
public int UserID {get;set;}
public string FirstName
public string LastName
public int AccessLevelID
public List<Groups> UserGroups
}
but I only really need:
public class PreviewUser {
int UserID
string FirstName
}

You can create another type with is a subset of the business type.
usually this is known as a
DTO - Data transfer Object which encapsulates only what you need. so the database needs to query only the subset of the entity.
public class UserDto
{
public int ID { get;set;}
public string Name{ get;set;}
}
Secondly if you need to add some ui logic to the display it is common to wrap the specific DTO in a more specific UI model.
public class UserUI
{
UserDTO _userDto;
UserUI(UserDTO userDto)
{
_userDto = userDto;
}
public string Name
{
get{return IsAfter_21_hours ? "The user as gone home" : _userDto.Name;}
}
}
the UserViewModel will reference an instance of UserUI.

You can either remove properties you don't need from the model (to slightly improve performance) or you can create a viewmodel that will provide only properties that you want to show.
Here is an example:
public class UserViewModel
{
private readonly User _user;
public UserViewModel(User user)
{
_user = user;
}
public int UserID
{
get { return _user.UserID; }
}
public string FirstName
{
get { return _user.FirstName; }
}
}
...
var viewModels = userRepository.GetUsers().Select(user => new UserViewModel(user));
UPDATED:
If performance is really important for you, you can use inheritance. Base class will be smaller version of the data and derived class will contain complete data. You can use the base class when you need to get only some fields from DB and save bandwidth.
public class BaseUser
{
public int UserID { get; set; }
public string FirstName { get; set; }
}
public class User : BaseUser
{
public string LastName { get; set; }
public int AccessLevelID { get; set; }
public List<Groups> UserGroups { get; set; }
}

There are number of approaches you may use:
use "full version" of source model. Since you're building UI, the user will see only what you want to display;
use view model, and wrap source model into this view model. The implementation is trivial, and amount of data is limited before UI;
use view model, and copy data from source model into this view model. The implementation is more complex (either mapping from existing model, or loading only required data from database), but view model and model are totally decoupled.
Actually, it depends on what is more suitable for you.
Note, that often the difference between "view model" and "model" is blurred. If the model looks like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
...and view model - like this:
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
then throw this view model away. While there's no difference, you don't need to create extra classes.

I think you don't have to create new model class to hold user data for view . Instead create a view model class and map the model properties to VM. See the example below
public class UserViewModel
{
Public UserViewModel(User user)
{
//initialize required viewmodel properties here
}
int UserID {get;set;}
string FirstName{get;set;}
}

Related

Rebuilding a nested ViewModel on !ModelState.IsValid

What are good strategies for rebuilding/enriching a nested or complex ViewModel?
A common way to rebuild a flat ViewModel is shown here
But building and rebuilding a nested ViewModel using that method is too complex.
Models
public class PersonInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int Nationality { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public int AddressTypeID { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}
public class AddressType
{
public int Id { get; set; }
public string Description { get; set; }
}
view models
public class PersonEditModel
{
public int Id { get; set; }
public string Name { get; set; } //read-only
public int Nationality { get; set; }
public List<AddressEditModel> Addresses { get; set; }
public List<SelectListItem> NationalitySelectList { get; set; } //read-only
}
public class AddressEditModel
{
public int AddressTypeId { get; set; }
public string AddressDescription { get; set; } //read-only
public string Country { get; set; }
public string PostalCode { get; set; }
public List<SelectListItem> CountrySelectList { get; set; } //read-only
}
actions
public ActionResult Update(int id)
{
var addressTypes = service.GetAddressTypes();
var person = service.GetPerson(id);
var personEditModel= Map<PersonEditModel>.From(person);
foreach(var addressType in addressTypes)
{
var address = person.Addresses.SingleOrDefault(i => i.AddressTypeId == addressType.Id)
if(address == null)
{
personEditModel.Addresses.Add(new AddressEditModel
{
AddressTypeId = addressType.Id
});
}
else
{
personEditModel.Addresses.Add(Map<AddressEditModel>.From(address));
}
}
EnrichViewModel(personEditModel, person, addressTypes); //populate read-only data such as SelectList
return Index(personEditModel);
}
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var person = service.GetPerson(editModel.Id);
var addressTypes = service.GetAddressTypes();
EnrichViewModel(editModel, person, addressTypes);
return View(editModel);
}
service.Save(...);
return RedirectToAction("Index");
}
//populate read-only data such as SelectList
private void EnrichViewModel(PersonEditModel personEditModel, Person person, IEnumerable<AddressType> addressTypes)
{
personEditModel.Name = person.Name;
personEditModel.NationalitySelectList = GetNationalitySelectList();
foreach(var addressEditModel in personEditModel.Addresses)
{
addressEditModel.Description = addressTypes.Where(i => i.Id = addressEditModel.AddressTypeId).Select(i => i.Description).FirstOrDefault();
addressEditModel.CountrySelectListItems = GetCountrySelectList(addressEditModel.AddressTypeId);
}
}
My code for building and rebuilding the ViewModels (PersonEditModel and AddressEditModel) is too ugly. How do I restructure my code to clean this mess?
One easy way is to always build a new view model instead of merging/rebuilding since MVC will overwrite the fields with the values in ModelState anyway
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var newEditModel = BuildPersonEditModel(editModel.Id);
return View(newEditModel);
}
but I'm not sure that this is a good idea. Is it? Are there other solutions besides AJAX?
I'm going to tackle your specific pain points one-by-one and I'll try to present my own experience and likely solutions along the way. I'm afraid there is no best answer here. You just have to pick the lesser of the evils.
Rebuilding Dropdownlists
They are a bitch! There is no escaping rebuilding them when you re-render the page. While HTML Forms are good at remembering the selected index (and they will happily restore it for you), you have to rebuild them. If you don't want to rebuild them, switch to Ajax.
Rebuilding Rest of View Model (even nested)
HTML forms are good at rebuilding the whole model for you, as long as you stick to inputs and hidden fields and other form elements (selects, textarea, etc).
There is no avoiding posting back the data if you don't want to rebuild them, but in this case you need to ask yourself - which one is more efficient - posting back few extra bytes or making another query to fetch the missing pieces?
If you don't want to post back the readonly fields, but still want the model binder to work, you can exclude the properties via [Bind(Exclude="Name,SomeOtherProperty")] on the view model class. In this case, you probably need to set them again before sending them back to browser.
// excluding specific props. note that you can also "Include" instead of "Exclude".
[Bind(Exclude="Name,NationalitySelectList")]
public class PersonEditModel
{
...
If you exclude those properties, you don't have to resort to hidden fields and posting them back - as the model binder will simply ignore them and you still will get the values you need populated back.
Personally, I use Edit Models which contain just post-able data instead of Bind magic. Apart from avoiding magic string like you need with Bind, they give me the benefits of strong typing and a clearer intent. I use my own mapper classes to do the mapping but you can use something like Automapper to manage the mapping for you as well.
Another idea may be to cache the initial ViewModel in Session till a successful POST is made. That way, you do not have to rebuild it from grounds up. You just merge the initial one with the submitted one in case of validation errors.
I fight these same battles every time I work with Forms and finally, I've started to just suck it up and go fully AJAX for anything that's not a simple name-value collection type form. Besides being headache free, it also leads to better UX.
P.S. The link you posted is essentially doing the same thing that you're doing - just that its using a mapper framework to map properties between domain and view model.

AutoMapper not working as expected?

I have a class at my Domain layer something as below:
public class Employee : AuditableEntity
{
public string FirstName { get; set; }
public string Surname { get; set; }
public double Salary{ get; set; }
public int PhoneNo { get; set; }
public double Bonus
{
get { return Salary + EmployeeAdditionals.Sum(e => e.Value); }
}
// virtual allow lazy loading
public virtual ReadOnlyCollection<EmployeeAdditional> EmployeeAdditionals { get; private set; }
// Paramterless constructor for EF
public Employee()
{
EmployeeAdditionals = new List<EmployeeAdditional>();
}
public void AddAdditionalInfo(EmployeeAdditional additionalInfo)
{
additionalInfo.Employee = this;
additionalInfo.EmployeeId = Id;
((Collection<EmployeeAdditional>) EmployeeAdditionals).Add(additionalInfo);
}
I then have the following viewModel class in my MVC 5 Application:
public class EmployeeDetailsViewModel : BaseViewModel
{
public EmployeeDetailsViewModel()
{
Employee = new Employee();
}
public Employee Employee { get; set; }
//Other fields
}
public class Employee
{
[DisplayName("Employee First Name")]
public string FirstName { get; set; }
[DisplayName("Employee Surname")]
public string Surname{ get; set; }
public double Salary{ get; set; }
[RegEx for Phone Number and Error Mesage]
public int PhoneNo{ get; set; }
public double Bonus { get; set; }
}
I have mapping then in my controllers and viewModel builder to map my viewmodel to my domain model and domain model back to view model and I am attempting to use AutoMapper for this.
Using manual mapping something like below:
// set domain from viewModel
employee.FirstName= model.Employee.FirstName;
//etc for rest of fields
// set view model from domain
viewModel.Employee.FirstName = employee.FirstName;
//etc for rest of fields
Now in my AutoMapperBootstrapper class in Setup method I have the following:
Mapper.CreateMap<Domain.Models.Employee, Employee>();
Mapper.CreateMap<Employee, Domain.Models.Employee>();
Then I am using this is following in my controller and viewmodel builder:
//set domain from viewModel
Mapper.Map(employee, model.Employee);
// set viewmodel from domain
Mapper.Map(viewModel.Employee, employee);
When I hit a breakpoint on mapping from domain to viewModel I see the model.Employee with the values from screen but when I step over this line the values do not get mapped to domain employee and then the model.Emplyee values are reset to null for the strings, 0.0 for double, etc.
Is there something incorrect with my configuration or use of automapper?
You pass wrong parameter. If you want to use existing instance, make sure first parameter is source and second parameter is destination.
Try to switch the parameter.
//set domain from viewModel (viewModel is the source)
Mapper.Map(model.Employee, employee);
^^^^^ ^^^^^
source destination
// set viewmodel from domain (domain is the source)
Mapper.Map(employee, viewModel.Employee);
^^^^^ ^^^^^
source destination
Shouldn't your map code actually be like this? You have to use instances for Map(), it looks like you are using Types
//set domain from viewModel
Mapper.Map(viewEmployee, new model.Employee());
// set viewmodel from domain
Mapper.Map( employee, new viewmodel.Employee());
This is how I would expect you to use it:
var viewModel = Mapper.Map<ViewModel.Employee>(domainModel); <-- this creates a ViewModel for a Domain Model
and the reverse would be:
var domainModel = Mapper.Map<Domain.Employee>(viewModel);

ASP.NET MVC model binding with dynamic viewmodel

What is the recommended way to handle binding a complex viewmodel? For example, using the objects below:
class PersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
//I have also tried a generic object instead of dynamic with the same results
public dynamic PreferredSportsTeam { get; set; }
}
class SportsTeamViewModel
{
public string Name { get; set; }
public string City { get; set; }
}
class BaseballTeamViewModel : SportsTeamViewModel
{
public double TeamERA { get; set; }
}
class HockeyTeamViewModel : SportsTeamViewModel
{
public int TotalSaves { get; set; }
}
I am trying to put either an instance of a BaseballTeamViewModel OR a HockeyTeamViewModel into the "PreferredSportsTeam" property of the PersonViewModel and then pass that PersonViewModel to the Edit View to allow the user to edit all of the fields.
I have successfully used EditorTemplates to display the corresponding View of the object stored in the "PreferredSportsTeam" property. However, when submitting the Edit Form the MVC model binder cannot reconstruct whatever object was stored in the PreferredSportsTeam. It does correctly fill the FirstName and LastName properties but the PreferredSportsTeam property always returns as a generic object that I cannot cast into anything else.
Is there something that I am missing or is it simply not possible with the default MVC model binder? I am aware of custom model binders and think they may be a solution but I am not knowledgeable enough about them to understand how they could be used to fix the problem.
Below code sample shows how to use dynamic object :
var team = new Team();
team.FirstName = "FirstName";
team.LastName = "LastName";
var baseballTeam = new BaseBallTeam();
baseballTeam.TotalSaves = 100;
team.PreferredSportsTeam = new ExpandoObject();
team.PreferredSportsTeam.BaseBallTeam = baseballTeam;
See This for more detailed explanation.

How to automatically set properties on view model with StructureMap

I have an interface ITranslateStuff and a static class and method with a generic parameter that is constrained (where class, ITranslateStuff, new()).
string translation = Translator.TranslateStuff<ITranslateStuff>();
Depending on which implementation of ITranslateStuff that I pass the method returns a different string.
I have ViewModels with a lot of different properties wich returns implementations of ITranslateStuff, for example:
public class ExampleViewModel
{
public string OtherStuff {get; set; }
public string TranslateStuffExample1 Translations { get; set; }
public ExampleViewModel2 SubModel {get; set; }
}
public class ExampleViewModel2
{
public string MoreStuff { get; set; }
public string TranslateStuffExample2 Translations { get; set; }
}
where both DoStuffExample1 and DoStuffExample2 implements ITranslateStuff.
I'm currently populating all theese properties with code like this:
model.Translations = Translator.TranslateStuff<TranslateStuffExample1>();
model.SubModel.Translations = Translator.TranslateStuff<TranslateStuffExample2>();
In the project we use StructureMap. I want to avoid setting all the properties on my view model manually with the same static method call. I have an ActionFilter where I set common properties on my view model, and was thinking I want to do this in an action filter as well.
I've tried finding something in StructureMap that can do this for me.
How can I solve this?
You will want to use 'setter injection'.
http://docs.structuremap.net/ConstructorAndSetterInjection.htm#section4

How to inherit from class that could be inherited by other classes and keep other classes properties (for view model)

I'm stuck in my inheritances bloating here:
First let me explain the premise of my problem.
My Model:
public class Person
{
[Key]
public int PersonId { get; set; }
[MaxLength(100)]
public string Name { get; set; }
}
public class SuperHero:Person
{
[MaxLength(100)]
public string SuperHeroName { get; set; }
public virtual ICollection<SuperPower> SuperPowers{ get; set; }
}
Now, I am trying to create my viewModels for my MVC website, I have those base classes that need to be inherited by all other viewmodel displaying/editing a Person/SuperHero:
public class BasePersonViewModel
{
public string Name { get; set; }
ctors()
}
public class BaseSuperHeroViewModel : BasePersonViewModel
{
public List<string> SuperPowers{ get; set; }
ctors()
}
Here is where I am stuck, I am trying to define only one ViewModel that could be used regarless of the base class and access property of Person and/or SuperHero (if the Person is a superhero). I've been pulling my hair out but so far only found a solution which i don't like:
Example:
public class SomeViewModel<T> where T : BasePersonViewModel
{
public BasePersonViewModel obj;
public DateTime BirthDate { get; set; }
public SomeViewModel(Person data) //: base(data)
{
if (data is SuperHero)
obj = new BaseSuperHeroViewModel (data);
else
obj = new BasePersonViewModel(data);
}
}
While this would work it's really not sexy to use. And on top of that, I could have another ViewModel that inherit from SomeViewModel as well.
Is there a cleaner way to achieve this?
Edit
My main goal is to be able to able to cast my SomeViewModel depending on the one of the baseclass. Let's say do something like in my Controller:
if myclass is SomeViewModel (of type SuperHero)
Exactly how you do it for Person/SuperHero db retrival/check
var data = context.Person.first(w=> w.Id==1)
if (data is SuperHero)
..
I would like this because I would like to use the same viewmodel let's say to list superhero and person, and just display slightly differently if it's a superhero
Edit 2
I was trying to avoid using the whole Model.Obj to be able to see it directly with the Model... But the more i think about it, the more I think this is not possible really... On top of that I would like to extend some other superHero specific properties in SomeViewModel (only if SomeViewModel is a superhero), that are not declared in the BaseSuperHeroModel one... Let's say in SomeViewModel I want the field 'ComesFromPlanet' only if superhero.
Edit 3
I thought about another way to do it, but it obviously creating various ViewModel.
For the most general case (all fields that are shared for all Person) I would keep my base:
public class BasePersonViewModel
{
public string Name { get; set; }
ctors()
}
I interface specific Person:
public Interface IBaseSuperHero
{
[MaxLength(100)]
public string SuperHeroName { get; set; }
public List<string> SuperPowers{ get; set; }
}
I would keep as well OtherViewModel like this:
public class SomeViewModel:BasePersonViewModel
{
Public datetime Birthdate {get;set;}
}
Then I would create a specific SomeviewModel for other Person inheritant and used interfaces to have old and new properties.
For example:
public class SomeViewModelSuperHero:SomeViewModel, IBaseSuperHero
{
public string OriginalPlanet {get;set;}
}
Is this a clean solution?
Sorry I'm sure I am not clear about this, but I try !
Thanks for your input and time.
I am trying to define only one ViewModel that could be used regarless of the base class and access property of Person and/or SuperHero (if the Person is a superhero)
Assuming you'd return default values for super-hero properties when the model is not a super-hero, you could do something like this:
public class PersonOrSuperHeroViewModel {
private Person person;
private SuperHero superHero;
public PersonOrSuperHeroViewModel(Person personOrSuperHero) {
if (personOrSuperHero is SuperHero) superHero = personOrSuperHero;
person = personOrSuperHero;
}
public IsSuperHero { get { return superHero != null; } }
... // super-hero properties only work when IsSuperHero == true
}
How about something like
public class Person {
public virtual BasePersonViewModel MainViewModel {
get { return new BasePersonViewModel(this);}
}
}
public class SuperHero : Person {
public override BasePersonViewModel MainViewModel {
get { return new BaseSuperHeroViewModel(this);}
}
}
So if all your people classes override the MainViewModel property to return the appropriate view, you don't need
public BasePersonViewModel obj;
public SomeViewModel(Person data) {
if (data is SuperHero)
obj = new BaseSuperHeroViewModel (data);
else
obj = new BasePersonViewModel(data);
}
Because you can have
public BasePersonViewModel obj;
public SomeViewModel(Person data) { obj = data.MainViewModel; }
which will work however many subclasses of person you have.

Categories