How does ViewModel work in MVC - c#

I really can't get my head around how to use ViewModel's in MVC. Say I have two simple domain models:
public class Customer
{
public int Id { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
public int Id { get; set; }
public string ProductName { get; set; }
}
And now my goal would be to create a ViewModel that displays (combines) the CustomerName and ProductName to display to a view. I'm confused what to include in the ViewModel to accomplish this. Do I use the same property names as my domain models like so?
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
}
How does the ViewModel know that the properties come from two different classes? Or am I forming my ViewModel incorrectly?

As i can see it you have a bigger design problem here.
Lets say you need to show on the UI only the CustomerName and ProductName. Well then just add those two on to your ViewModel class and you`re good to go, exactly how you described it.
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
}
Getting the data in two variables is not a problem:
Customer customer = service.GetCustomer();
Product product = service.GetProduct()
And now that you have everything you need you can just set the data and pass it to the view.
MyViewModel viewModel = new MyViewModel();
viewModel.CustomerName = customer.CustomerName;
viewModel.ProductName = product.ProductName;
It always depends on what you need to show on the UI and only send what you need and nothing more.
You do not need to have exactly one Model that you pass all over the place in your application, Business, DataAccess, UI. You can have something custom if you really need it.

You would have to set this up yourself in the ViewModel, as a template it could look something like:
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
public void GetCustomerName(int customerId)
{
CustomerName = CustomerServiceLayer.GetCustomerName(customerId);
// CustomerService Layer (I.e. a repository that contains this info;
}
public void GetProductName(int productId)
{
ProductName = ProductServiceLayer.GetProductName(productId);
// ProductService Layer (I.e. a repository that contains this info;
}
}
You would then have two other Service Layers (ProductServiceLayer and CustomerServiceLayer) that speak to the database/repository to obtain the information you want. That information is then returned to the view (via your ViewModel) and displayed to the user.
Alternatively you could pass a Customer and a Product object directly into your ViewModel (via a constructor).
public class MyViewModel
{
public Customer MyCustomer { get; set; }
public Product MyProduct { get; set; }
public MyViewModel(ICustomer customer, IProduct product)
{
MyCustomer = customer;
MyProduct = product;
}
}
The downfall here would be that you expose your entire Customer and Product classes in the View.

You can do it like that but you generally build the viewmodel up on render in the get action and then post parts of that view model back and handle it on a post action. The MVC binding does the magic once getting the values posted back from a form.
I wouldn't put business logic inside the viewmodel but rather build the viewmodel up in your controller using managers/services.
You could also make it so the viewmodel has your complex model types as the properties like so..
public class MyViewModel
{
public Customer Customer { get; set; }
public Product Product { get; set; }
}

ViewModel rapresents a model you use to go to your view. In your controller you'll retrieve data and pass them to your ViewModel.
Imagine you have a checkbox in you view that rapresent a Gold Customer: it's not suitable to change your domain model to add this information and it's not a good practice to make your code dirty with Viewbag and Viewdata (imho).
So you create a model or template that has all the information you need. In our case:
public class MyViewModel
{
public string CustomerName { get; set; }
public string ProductName { get; set; }
public boolean IsGoldCustomer { get; set; }
}
Ofter you'll have to convert your model into a ViewModel and viceversa in order to pass data from a "DOMAIN model" to a "VIEW model".

Related

Should ViewModels contain Models? (ASP.NET, Entity Framework)

Should view models contain models directly as shown below?
public class ViewModel
{
public Car Car { get; set; } // Car model
public Driver Driver { get; set; } // Driver model
public Photo Photo { get; set; } // Photo model
public List<Skill> Skills { get; set; } // List of Skill model
}
or should view models contain another view models:
{
public CarViewModel CarViewModel { get; set; }
public DriverViewModel DriverViewModel { get; set; }
public PhotoViewModel PhotoViewModel { get; set; }
public List<SkillViewModel> Skills { get; set; }
}
What is the best practice?
It depends on what you are doing, but normally, the second option is the best practice, assuming all of the objects inside the Viewmodel are somehow actually displayed in the View.
Also take into aspect that your ViewModel items represent the thing on the UI, and the Model represents the business logic.
So, your UI-Artefacts (ViewModel objects) are not necessarily associatable to one domain object (Business Logic Object).
So, in your example,
"Photo" or "Skills" don't seem to be a logical object in that matter.
They exists on the ViewModel to be displayed, as UI Artefacts, but in the logical domain they would more likely be a property of a logical object, maybe the driver, or something.

How to add multiple model in a single view?

I have 3 models. 1 model am using for the search box rest 2 I am using for display the checkbox list.
for search box I don't want Ienumerable type model but for checkbox I want Ienumerable type model
Data is coming from different controller
How can I do this in a single view?
Please give some example.
Make ViewModel then send your ViewModel to view after setting properties;
public class MyModel1 {}
public class MyModel2 {}
public class ViewModel
{
public MyModel1 MyModel1 {get; set;}
public MyModel2 MyModel2 {get; set;}
}
Then in your view after setting #model ViewModel
you can use any sub models you want like #Model.MyModel1 etc
If you want to use 3 models in a view, you have to declare a "Bag" view model where you wrap all submodels that you want to use.
First, if you must create your submodels, if you haven't then you include them in the bag view model.
public class FirstViewModel
{
public int MyProperty { get; set; }
}
public class SecondViewModel
{
public string MySecondProperty { get; set; }
}
public class ThirdViewModel
{
public DateTime MyThirdProperty { get; set; }
}
public class BagViewModel
{
public FirstViewModel FirstModel { get; set; }
public SecondViewModel SecondModel { get; set; }
public ThirdViewModel ThirdModel{ get; set; }
}
The submodels can be either view or binding models.
And in your razor view, you include the BagViewModel:
#model BagViewModel
Then if you want to use FirstViewModel ( for example ), you do it with:
#Model.FirstViewModel

Is there a alternative to insert each Modelpart individually in a controller?

I asked myself if there is a alternative to insert each modelpart of a model to declare a content from my modelview to my model individually.
For example I have a model class:
public partial class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public string Serial { get; set; }
public string Version { get; set; }
}
and my ViewModel looks like this:
public class ViewModel
{
public List<Data.Models.Product> Products { get; set; }
}
Now in my controller I don't want to do this:
//...
Product newProducts = new Product
{
ProductName= ViewModel.Product.ProductName,
//and so on
}
//...
With this I mean to add every part of the model individually... it's quite time consuming if you have a large database table. It should also be possible to use this shortcut if the database tables aren't quite similar. For example if the ViewModel has an additional column.
I hope you will understand my problem and I am looking forward to your answers.

ViewModel Structure Decision

As my domain classes I have Person and FavoritePerson classes as follows.
public class CompanyPerson : ICompanyPerson
{
[Key]
public Guid PersonId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
public class CompanyFavoritePerson : IFavoritePerson
{
[Key]
public Guid FavoritePersonId { get; set; }
[Column(TypeName = "datetime2")]
public DateTime CreateDate { get; set; }
public Guid? CompanyPerson_PersonId { get; set; }
[StringLength(128)]
public string CompanyUser_UserId { get; set; }
public virtual CompanyPerson CompanyPerson { get; set; }
public virtual CompanyUser CompanyUser { get; set; }
}
In my web application I will need to show List of Favorite Person. So my view model is like this;
public class FavoritePersonViewModel
{
public Guid FavoritePersonId { get; set; }
public DateTime CreateDate { get; set; }
public Guid? CompanyPerson_PersonId { get; set; }
public string CompanyUser_UserId { get; set; }
//Option1: PersonViewModel PersonViewModel {get; set; }
//Option2: public string Title {get;set;}
}
Since I need to show Title of the favorite user in the list (where title belongs to Person class) which way will match with best practices?
Referencing a viewModel from another viewModel or extend viewModel with required extra attributes and fill them in business layer?
After some more research on this topic; I found out at this question
What is ViewModel in MVC?
it is clearly stated that:
View models can combine values from different database entities.
As like below;
So now you have data from the Employees and Departments tables in one
view model. You will just then need to add the following two
properties to your view model and populate it with data:
public int DepartmentId { get; set; }
public IEnumerable<Department> Departments { get; set; }
So I am going with Option 2.
The ViewModel pattern is just one of many patterns that fall into the 'Separated Presentation Pattern' bucket.
It's very important that you think about the requirements of your view before designing the ViewModel. For instance, if you have two widgets in your view and every widget has its own ViewModel, composite ViewModel is suitable in the situation, but if the view is just one that uses multiple domain classes, whether you have View model for each one, composite ViewModel is not suitable because it increases the complexity and every change in one ViewModel can break your code.
Thus, based upon your question
As my domain classes I have Person and FavoritePerson classes.
Since I need to show Title of the favorite user in the list (where title belongs to Person class).
It seems to me that composite ViewModel is not a good choice and you should design a new ViewModel.
It is also worth to read the ViewModel Best Practices

Model and partial model, how to avoid the redundant code?

I have a model and a partial model which contains only the properties that I need to expose in JSON.
But the properties between the model and his partial model are redundant.
How can I avoid that or improve my approach?
namespace Dashboard.Models.UserModels
{
public class UserModel
{
public int id { get; set; }
public string dbName { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public int idExternal { get; set; }
public int idInstance { get; set; }
public string login { get; set; }
public string password { get; set; }
public DateTime? dtContractStart { get; set; }
public DateTime? dtContractEnd { get; set; }
public string emailPro { get; set; }
public string emailPerso { get; set; }
public LuccaUserModel()
{
idInstance = -1;
}
// partial model for json result
// not sure is the best way or have to be here
public class PartialUserModel
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string emailPro { get; set; }
public string emailPerso { get; set; }
public DateTime? dtContractStart { get; set; }
public DateTime? dtContractEnd { get; set; }
public string url { get; set; }
}
// UserModel Methods
}
}
You can rename PartialUserModel UserModelBase class (or leave it as is... it just makes better logical sense to do so) and make UserModel to inherit from it:
public class UserModel : UserModelBase
{
...
}
Of course you'll need to remove all duplicate properties from UserModel in this case.
It's a thin line between doing a proper design and building an overkill design. Answer depends on many inputs, among which I chose to have project and model breadth most important.
In hope to have my answer clearer, I have to say I use different terminology. Data which is adopted for use in UI is usually called ViewModel. In your case, you would build UserViewModel which contains necessary subset of information.
If I'm working on a one-off project, I'll reuse model as a ViewModel. I'll do this by having helper method which removes sensitive information, loads up or cuts off data which is lazy loaded from database and does other preparation on data. All this is done with same model class.
If it's not a short term project, I look to create separate ViewModel classes which I map from model data. Then, if I'm working with mostly flat data I use AutoMapper tool to have data automatically copied, instead of writing my own mappers.
As another answer here states, you write a basic class with data you need in UI and extend it with other model data, however this is not a good approach for several reasons.
If violates separation of concerns. Project dealing with model and persistance should not know about your ViewModel
You may need to flatten data from related objects into ViewModel objects. In that case, your model objects would have fields which should not be there, or would be redundant.
You may need calculated fields and helper methods in ViewModel which would again end up in model, confusing everyone that is not updated about design.
You could want to adopt several unrelated model classes to same ViewModel class
To try and put it shortly, either reuse model class or create ViewModels. There is unfortunately no clever solution. If you find one, please post a comment as I'd like to hear about it :)

Categories

Resources