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

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.

Related

CQRS DDD - Commands and Domain Models relationship

I'm practicing to implement a project using CQRS and DDD to the best I can and have come up with a question once building the commands.
Scenario: The user sends a command to create a bookshelf in the system which itself could also contain a collection of books.
The command looks like this:
public class CreateNewBookShelfCommand : ICommand
{
public long CommandInitiatorId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Book> Books { get; set; }
}
All of the Commands reside in the Contracts project that itself is inside the Application solution folder.
The Model project residing in the Domain solution folder hosts the BookShelf entity class which includes a collection of Books.
Questions 1: The command above has this Books property which is of type Book. My question is: should the Commands project directly reference the Model project so that the Book data type would be resolved in the CreateNewBookShelfCommand? Myself, I don't think that the Contracts are allowed to reference any project other than the CommandHandlers or anything else which might be Cross Cutting.
Question 2: So, is this a good practice to replicate part of the Book entity class here at the Contracts project and utilize it in the CreateNewBookShelfCommand?
And this is the Model project I have for Book and BookShelf:
public class BookShelf : BaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public BookShelfAccess Access { get; set; }
public virtual BookShelfOwner Owner { get; set; }
public long OwnerId { get; set; }
public ICollection<Book> Books { get; set; }
}
public class Book : BaseEntity
{
public string Name { get; set; }
public DateTime? PublishedAt { get; set; }
public int NumberOfPublishes { get; set; }
public virtual BookShelf Shelf { get; set; }
public long ShelfId { get; set; }
}
I hope my explanation has been enough if there is any other information I should add here let me know.
Typically, you would not use the entities in your domain model as elements of your command.
Commands are fundamentally messages, and as such they really ought to be immutable. You want to know that what is received is the same thing that was sent. In DDD terms, you might think of the messages as being value objects.
It doen't make sense to use an entity from your domain model as part of the in memory representation of your command, because you should never be invoking any of the entity methods that change its state.
Commands are much closer in nature to data transfer objects than they are to domain model entities.
Normally, all of the entity objects in your system should live behind an aggregate root interface, and the arguments that you pass to that interface are values. If the domain model needs an entity, it can create one from the values provided.

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

How does ViewModel work in MVC

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".

nested partial views with viewModels

edit - just rewording the question
If I have countries>states>cities
In my country controller, I fill a viewmodel with the country useing id, along with its states.
In the view, I can then say "for each state, show population".
What i don't understand, is how to also say, in the same view, "for each state, show each city".
Is everything in the same view model? Am I supposed to use nested partial views? How do I pass models from country view to state partial view to city partial view?
end edit
I have data organized in a parent>child>child>child fashion
Here are my viewmodel entities
public country country { get; set; }
public IList<state> states { get; set; }
public IList<city> cities { get; set; }
Here is my view
#model myproject.viewModel.vwcountry
#for (var c = 0; c < Model.country.states.Count(); c++)
{
#Html.Partial("_states", Model.states[c]);
}
And the first partial view model
#model myModel.state
//partial view displays stuff from model
This all was working fine.
Now, I want to add the children of state. This is where I'm lost.
I feel that I should be passing the view model instead of the state model, but that won't even compile.
I feel there should be a seperate view model like this...
public state state { get; set; }
public IList<cities> cities { get; set; }
But this view model can't be made available to the view becuase in the current context, it doesn't exist.
I imagine this is a common pattern, but I haven't found a similar example...
your model is not correct. here is the correct model:
public class vwcountry
{
public country country { get; set; }
public IList<state> states { get; set; }
}
public class state
{
public state state { get; set; }
public IList<city> cities { get; set; }
}

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