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.
Related
Hi I've a problem with MetadataType with my partial class
In my asp.net mvc project i have a class library (Infrastructure) with a DB directory who contain my partial class scafolded from the DB
Exemple
namespace BibliEasy.Infrastructure.DB
{
public partial class Series
{
public Series()
{
Publications = new HashSet<Publication>();
}
public int IdSerie { get; set; }
public string TitreSerie { get; set; }
public string StatutSerie { get; set; }
public int? VolumesSerie { get; set; }
public virtual ICollection<Publication> Publications { get; set; }
}
}
I add Metadata file for the data annotation
Exemple
namespace BibliEasy.Infrastructure.DB
{
[MetadataType(typeof(SeriesMetaData))]
public partial class Series { }
public class SeriesMetaData
{
[Display(Name = "Titre")]
[Required]
[StringLength(255)]
public string TitreSerie { get; set; }
[Display(Name = "Statut")]
[Required]
[StringLength(50)]
public string StatutSerie { get; set; }
[Display(Name = "Nombre total de volume")]
[Range(0, int.MaxValue)]
public int? VolumesSerie { get; set; }
}
}
And it don't work, in the view this is the name of Serie's property and not the display from SeriesMetaData and the Validation don't work.
What am I missing?
for information the architecture of my project
Infrastructure class library
DB directory
Domaine class library
Services directory with the class who contains acces function to DB
Application class library
ViewModels directory
Services directory who contains controller of view models
MVCApp
Areas
Controllers
Views
Ok in fact I'm in ASP.NET Core so it's not MetadataType but ModelMetadataTypeAttribute
Is there any way possible to generate the database object in model.edmx with DataAnnotations(like Required, StringLength)?
When you use database first approach with EF all your classes are generated as partial classes. Which means you can spilt the definition over two or more source files. If you make changes to the generate class it will be overwritten.
Let's say you have a partial class of User generated by EF as
public partial class User
{
public string Username { get; set; }
public string Telphone { get; set; }
}
Now to use DataAnnotations on the properties of User you would create a new source file with the same name in the same namespace and make use of MetadataType attribute as:
[MetadataType(typeof(UserMetaData))]
public partial class User
{
public string Username { get; set; }
public string Telphone { get; set; }
}
public class UserMetaData
{
[Required]
[Display(Name = "User name")]
public string Username { get; set; }
[DataType(DataType.PhoneNumber)]
[Display(Name = "Telephone number")]
public string Telephone { get; set; }
}
As you can see I've used MetadataType attribute in the second example which specifies the metadata class to associate with a data model class.
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
In my MVC application, I defined the DataAnnotations in the domain models. Although the DataAnnotations properties as Display, etc. can be retrieved when using Domain model, they cannot be retrieved when using the same properties on ViewModel and using this ViewModel. I think it is not seem to good to define the DataAnnotations in ViewModel again. So, is it possible or which way should I follow?
Domain Model:
public class Issue
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Project Number")]
public int ProjectID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Issue Definition")]
public string Description { get; set; }
//... removed for brevity
//Navigation Properties:
public virtual ICollection<FileAttachment> FileAttachments { get; set; }
}
ViewModel:
public class IssueViewModel
{
public int ID { get; set; }
public int ProjectID { get; set; }
public string Description { get; set; }
//... removed for brevity
//Navigation Properties:
public virtual ICollection<FileAttachment> FileAttachments { get; set; }
}
You can create a new buddy class which holds all metadata about properties and class.
public partial class IssueMetadata
{
[Required(ErrorMessage = "Required")]
[Display(Name = "Project Number")]
public int ProjectID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Issue Definition")]
public string Description { get; set; }
}
Then, we must tell the MVC Framework about the buddy class through the MetadataType attribute, which takes the type of the buddy class as its argument. Buddy classes must be defined in the same namespace and
must also be partial classes.
[MetadataType(typeof(IssueMetadata))]
public partial class IssueViewModel
{
//...
public int ProjectID { get; set; }
public string Description { get; set; }
//...
}
[MetadataType(typeof(IssueMetadata))]
public partial class Issue
{
[Key]
public int ID { get; set; }
public int ProjectID { get; set; }
public string Description { get; set; }
//... removed for brevity
//Navigation Properties:
public virtual ICollection<FileAttachment> FileAttachments { get; set; }
}
Additional note:
If IssueMetadata and Issue (or IssueViewModel) classes located in different assemblies, then you can associate classes with their buddy class in runtime, like that:
public class AssociatedMetadataConfig
{
public static void RegisterMetadatas()
{
RegisterPairOfTypes(typeof(Issue), typeof(IssueMetadata));
RegisterPairOfTypes(typeof(IssueViewModel), typeof(IssueMetadata));
}
private static void RegisterPairOfTypes(Type mainType, Type buddyType)
{
AssociatedMetadataTypeTypeDescriptionProvider typeDescriptionProvider
= new AssociatedMetadataTypeTypeDescriptionProvider(mainType, buddyType);
TypeDescriptor.AddProviderTransparent(typeDescriptionProvider, mainType);
}
}
And, just call this static method in global.asax:
AssociatedMetadataConfig.RegisterMetadatas();
#StephenMuecke is right. DomainModel attributes and ViewModel attributes are different and you can use them seperately in your models. But I would use inheretence in this case, if I were you. You can create a Partial class for ViewModel and inherit your DomainModel from this ViewModel class.
Like:
public class IssueVM
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Project Number")]
public int ProjectID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Issue Definition")]
public string Description { get; set; }
//... removed for brevity
//Navigation Properties:
public virtual ICollection<FileAttachment> FileAttachments { get; set; }
}
public class IssueDM : IssueVM
{
// Other Fields
}
That way you have a base class of ViewModel (less fields) and a larger class with more fields for DB operations. Your ViewModel data annotation attributes are also inherited in your DomainClass that way.
I don't claim that this is the best way, but I'am using this and works fine.
I have a Entity class which included an virtual list which is a link to another class:
public virtual Employee Employee1 { get; set; }
So in Edit page I have my item with label Employee1 when I wanted "My Employee" so in my DataAnnotation class I add
[DisplayName("My Employee")]
public virtual Employee Employee1 { get; set; }
But it is not working yet. Other items in DataAnnotation class work well.
It was not working because visual studio when create view automatically, for foreign keys use Labelfor instead of DisplaynameFor like below:
#Html.LabelFor(model => model.Employee1.LastName, "Employee1")
So read that value from second parameter instead of DataAnnotation so We need to change it to:
#Html.LabelFor(model => model.Employee1.LastName, "My Employee")
You need to give DataAnnotation in original class property. If you give DataAnnotation to virtual property, it doesnt work. I tested. But If you give DataAnnotation to property in original class. It works.
Such as:
public partial class Languages
{
DataAnnotation here
public string Code { get; set; }
public string Name { get; set; }
}
public partial class Categories
{
public string Code { get; set; }
public string FKLanguageCode { get; set; }
public string Name { get; set; }
public Nullable<int> Order { get; set; }
DataAnnotation Not Here
public virtual Languages Languages { get; set; }
}