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
Related
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.
I'm a little confused about this one. I read about metadata classes in this article on MSDN.
It says that the reason for creating metadata is not to mess with the auto generated models by EF.
So this is a model generated by EF:
namespace Blog.Models
{
using System;
using System.Collections.Generic;
public partial class Article
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Article()
{
this.ArticleTags = new HashSet<ArticleTag>();
this.Comments = new HashSet<Comment>();
}
public int ArticleID { get; set; }
public string PostTitle { get; set; }
public string PostContent { get; set; }
public string PostLinkText { get; set; }
public Nullable<System.DateTime> PostDateTime { get; set; }
public Nullable<int> PostAuthorID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ArticleTag> ArticleTags { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Comment> Comments { get; set; }
public virtual Admin Admin { get; set; }
}
}
and this is metadata class for Article model:
public class ArticleMetadata
{
[Display(Name = "Post Title")]
public string PostTitle { get; set; }
[Display(Name = "Content")]
public string PostContent { get; set; }
[Display(Name = "Link Text")]
public string PostLinkText { get; set; }
[Display(Name = "Post Date and Time")]
public DateTime? PostDateTime { get; set; }
[Display(Name = "Post Author")]
public int? PostAuthorID { get; set; }
}
linked to model class using PartialClasses.cs:
[MetadataType(typeof(ArticleMetadata))]
public partial class Article
{
}
Is the metadata class the same as a ViewModel??
If so, how are these different and which one should be used in my case?
class ArticleMetadata is a helper class for class Article, needed because you can't otherwise add those Annotation attributes to specific properties.
Together they form the Model part of MVC.
Note that the partial class can't help with the properties because it can't redefine them. It is used here only to link up the MetaData class.
For very simple operations (CRUD pages) you can use the Model directly. In all other case, create a ViewModel for each View. The Article Model will probably be used by an EditArticleViewModel.
In general you should create ViewModels to support Views, not one for each Model. One ViewModel could be composed of data from several Model classes.
No, they aren't the same.
Metadata classes allow you to define/add restraints to your class members.
View models are usually used to make it easier to use your model in a view. Such as having lists of SelectListItems for dropdowns, having properties to accept form post values, etc.
The two are usually used in conjunction, you add validation in your metadata class, and enforce it through the view model.
In your specific case, it seems all you need is a metadata class.
I'm following along the ASP.NET MVC 5 book, but I've ran into an itch that the book doesn't seem to scratch. I have an Album model as so:
namespace MvcMusicStore.Models
{
public class Album
{
public virtual int AlbumId { get; set; }
public virtual int GenreId { get; set; }
public virtual int ArtistId { get; set; }
public virtual string Title { get; set; }
public virtual decimal Price { get; set; }
public virtual string AlbumArtUrl { get; set; }
public virtual Genre Genre { get; set; }
public virtual Artist Artist { get; set; }
}
}
To make a long story short, the Genre and Artist models both have a field called Name. When I list these using the StoreManagerController, it displays simply as "Name" in each of the headers. I can add a DataAnnotation to Genre and Artist suchs as [Display(Name="Artist Name")], but I only want it to display as "Artist Name" in this particular instance. I don't want it to be so specific when I am on the "Edit Artist Page."
I understand that I should go about doing this by using a ViewModel, but I am still confused as the view model would still just be pulling in the object, and that object's Display annotations are set in the model itself.
Or better yet, is this something that's best left to the markup?
Not if you use view models properly. Many people end up creating view models like:
public class FooViewModel
{
public Foo MyFoo { get; set; }
}
That's just a waste of time. Instead, you view models should completely stand in for whatever entity your editing, which means, instead of just referencing the entity, you create properties in your view model for all the properties in your entity that you want to view/edit. Then, in your controller actions, you "map" to and from your entity and view model, which is to say, you just set the properties on one with the values of the appropriate properties on the other.
In your situation then, you would need something like:
public class AlbumViewModel
{
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public GenreViewModel Genre { get; set; }
public ArtistViewModel Artist { get; set; }
}
public class ArtistViewModel
{
public string Name { get; set; }
...
}
public class GenreViewModel
{
...
}
Then, you can set the display name to be whatever you want on this view model. If you need a different display name in another context, create a separate view model for that.
Also, what's up with all the virtuals? The virtual keyword merely means that the property/method can be overridden by a subclass. While it technically doesn't hurt anything to just make everything virtual, it's code smell unless you truly intend something to be overridden, or even subclassed in the first place. Traditionally, on entities, the only thing you'll ever add virtual to is navigation properties, as this allows Entity Framework to apply its lazy loading logic to your entity. (It literally creates subclasses of your entities dynamically, called "proxies", that add the lazy loading logic to the navigation properties' getter.) If you don't have a navigation property or even if you just don't want lazy loading enabled for that navigation property, then you shouldn't use virtual, unless you really mean to.
I would probably do something like this.
public class AlbumViewModel
{
public int AlbumId { get; set; }
public AlbumGenre Genre { get; set; }
public AlbumArtist Artist { get; set; }
}
[MetadataType(typeof(AlbumArtistMetadata))]
public class AlbumArtist : Artist {
private class AlbumArtistMetadata {
[Display(Name="Artist Name")]
public string Name { get; set; }
}
}
[MetadataType(typeof(AlbumGenreMetadata))]
public class AlbumGenre : Genre
{
private class AlbumGenreMetadata
{
[Display(Name = "Genre Name")]
public string Name { get; set; }
}
}
Though I'm not sure I'd inherit from the entities, but instead create models based on the entities.
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".
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 :)