Is it possible to reuse the DataAnnotations in ViewModel? - c#

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.

Related

MetadataType don't work with partial class in ASP.NET MVC

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

Are Metadata classes same as ViewModel in asp.net MVC?

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.

How to model with EntityFramework extensible fields for the entities

I have the following requirement, on my app the Entities will come with some fields, however the user needs to be able to add additional fields to the entity and then values for those fields.
I was thinking something like this but I am not sure if it would be a good approach or not.
The base class is an entity (Not sure which fields I need to add here)
public class Entidad
{
}
Then the Company Class will inherit from Entity
public class Empresa : Entidad
{
[Key]
public int Id { get; set; }
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
As you can see there is an ICollection of additional fields. that class would have the fieldname, type and id
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public Tiposcampo TipoCampo { get; set; }
}
and then the field value would be something like this:
public class ValorCampo
{
public Entidad Entidad { get; set; }
public CampoAdicional Campo { get; set; }
public string ValorTexto { get;set ; }
public int ValorNumerico { get; set; }
}
However I am not sure if this is the correct model classes for my scenario and whether it would create the tables correctly.
EF works with lazy load so at least there are several "virtual" missings.
In all properties that does not use primitive types and in collections.
Can you extend more than one entity with additional fields? If so you need that ValorCampo contains the entity (Entidad) but the entity should have the Id so you need to move the Id from Empresa to Entidad. Otherwise you need ValorCampo should refer to Empresa not to Entidad

Foreign Key Relationship EF Code First

I'm busy creating my first EF code first model and I've come across a slightly confusing problem.
I have a number of model classes that each inherit from a base model class that has three common properties I want used in all model classes. These properties are Id, LastUpdated and LastUpdatedBy.
Id is the primary key of each model class.
LastUpdated is a foreign key to my 'User' model class.
LastUpdatedBy is a datetime field that indicates the last time the record was modified.
So what I'd like to setup is the 1 to 1 foreign key relationship from my base class to my 'User' model class but I'm receiving the exception:
Multiplicity is not valid in Role 'Profile_LastUpdatedByUser_Source'
in relationship 'Profile_LastUpdatedByUser'. Because the Dependent
Role properties are not the key properties, the upper bound of the
multiplicity of the Dependent Role must be '*'
This is my ModelBase class:
public class ModelBase
{
public int Id { get; set; }
public DateTime LastUpdated { get; set; }
[ForeignKey("LastUpdatedByUser")]
[Required]
public int LastUpdatedByUserId { get; set; }
public virtual User LastUpdatedByUser { get; set; }
}
This is one of my model classes:
public class Profile : ModelBase
{
[StringLength(25, MinimumLength=1)]
[Required(ErrorMessage="First Name is Required")]
public string FirstName { get; set; }
[StringLength(25, MinimumLength = 1)]
[Required(ErrorMessage = "Last Name is Required")]
public string LastName { get; set; }
[StringLength(25, MinimumLength = 1)]
[Required(ErrorMessage = "Email Address is Required")]
public string Email { get; set; }
public string Mobile { get; set; }
public string HomePhone { get; set; }
public string WorkPhone { get; set; }
public string ImageSource { get; set; }
public Squable.Model.Enums.MembershipType.MembershipTypeEnum MembershipType { get; set; }
}
This is my user class (Please ignore the Password property, I'll fix that later ;) ):
public class User : ModelBase
{
public string UserName { get; set; }
public string Password { get; set; }
public int ProfileId { get; set; }
public virtual Profile Profile { get; set; }
}
I don't know if what I am doing is best practise but I could do with some advice as to how to either fix the problem or maybe just some pointers in the right direction.
Move
public int Id { get; set; }
to User class and to Profile you can also change the names to UserId and to ProfileId and move
public virtual User LastUpdatedByUser { get; set; }
to Profile class.
I have a bad experience with sharing Id in base entity If you are planning to use Repository and UnitOfWork pattern you will get a lot of problems later. Check your current database structure and tables with SQL Server Management Studio.
More Info TPH

EF4 and MVC3 mapping ViewModels to Entities

I have an EF4 Entity Workgroup. Below is the meta-data for that model for reference.
[MetadataType(typeof(WorkgroupMetaData))]
public partial class Workgroup {
public Contact manager { get; set; }
}
[Bind(Exclude = "id")]
public class WorkgroupMetaData
{
[ScaffoldColumn(false)]
public int id { get; set; }
[DisplayName("Org. Number")]
[Required(ErrorMessage = "Org. Number is required.")]
public string org_number { get; set; }
[DisplayName("Workgroup Name")]
[Required(ErrorMessage = "Workgroup name is required.")]
public string name { get; set; }
[DisplayName("Customer Contact")]
public int customer_contact_id { get; set; }
[DisplayName("Manager")]
public int manager_id { get; set; }
[DisplayName("Tech. Lead")]
public int lead_id { get; set; }
[DisplayName("Time Approver")]
public int time_approver { get; set; }
[DisplayName("Description")]
public string description { get; set; }
[ScaffoldColumn(false)]
public object created_at { get; set; }
[ScaffoldColumn(false)]
public object last_modified_at { get; set; }
}
I've got a ViewModel defined as:
public class WorkgroupViewModel
{
public Workgroup Workgroup { get; set; }
public List<Workgroup> Workgroups { get; set; }
}
On the view I have a grid to dump out the workgroups available. This works but I was wondering how to convert the ID fields to the actual strings from another table. Basically the manager, customer_contact, lead are all references to the Contact entity. I would like to show the names from Contacts instead of just the id.
How can this be accomplished? I've looked around a bit but I can't seem to find a suggestion or an answer. Maybe I looking at this from the wrong perspective?
You might consider using a wrapper around Workgroup (decorator pattern) or Tuple or creating a custom class that binds them together.
public class WorkgroupDisplayModel
{
public Workgroup Workgroup { get; set; }
public Manager Manager { get; set; }
// Add additional properties for each related type
}
In your EF query you can do something like:
var query = from w in Context.Workgroups
join m in Context.Managers
on w.manager_id equals m.uid
// Additional joins for each related table
where w.Description == "Project 1" // Whatever criteria
select Tuple.Create(w, m); // Add param for each type
//or
//select new WorkgroupDisplayModel { Workgroup = w, Manager = m, ... };
var list = query.ToList();
var contact = list[0].Item1; // Tuple has strongly typed Item1 thru ItemN
var manager = list[0].Item2;
Then your view model could have:
List<Tuple<Workgroup, Manager, Customer, Lead>> Workgroups { get; set; }
or
List<WorkgroupDisplayModel> Workgroups { get; set; }

Categories

Resources