[RESOLVED]Where to place Model classes

Hi I created an ASP.NET MVC 4 Razor Internet Application.

I’ve generated the Model Classes from an existing SQL Server Database.

Then I generated an MVC Controller with read/write actions and views using Entity Framework.

That generated Views for all CRUD methods and a MySystemModel.emdx folder

Inside that folder there are

MySystemModel.Context.tt
MySystemModel.Designer.cs
MySystemModel.edmx.diagram
MySystemModel..tt

Inside MySystemModel..tt there are classes for all tables I have in the database plus a MySystemModel.cs:

TABLE1.cs
TABLE2.cs
TABLE3.cs
MySystemModel.cs

Now here is where I got confused:

I still have a MODELS folder that MVC created in the beginning, altogether with CONTROLLERS and VIEWS and other folders.

Usually in this foder I would place the table properties if I was using a CODE FIRST approach.

I would use a Table1Model.cs class for example to set some DataAnnotations attributes such as:

[Required]
[EmailAddress]
[StringLength(200)]
[Display(Name="Email address")]

public string Email { get; set; }

Question: Should I place these validations in:

A)      the generated classes inside MySystemModel.emdx folder

B)       MVC MODEL folder (in this case I would have to recreate them)

I know it can work different ways but I would like to know what the best practices are.

I’ve also seen some examples where the MODEL was a whole different project (perhaps a class library?) inside the solution.

So I am confused how can these Models be in different places.

Thanks

<div class="comment-right-col">

</div>

skynyrd

Question: Should I place these validations in:

The answer is B, the Models folder.  And not only that, but you "should not" add annotations to auto-generated classes.  The reason for this is that if you decide to change a db column in any way, you’ll lose those annotations when you re-generate the models
for your db.

Instead, you should use what’s called "Meta Data" classes.  These classes are mapped to your generated classes, but do not change should you need to re-generate your db models.

I had a post somewhat recently on creating these classes, but I cannot find it.  sorry

The easy answer is the Models folder. However, I think additional separation of concerns is good and place my models in their own project. I then use View Models in the Web project.

You can use partial "buddy" classes for your annotations. 

public partial class Table1Model.cs
{
[Required]
[EmailAddress]
[StringLength(200)]
[Display(Name="Email address")]

public string Email { get; set; }
}

You can put these in your Models folder. See this for more information on buddy classes: http://www.asp.net/mvc/tutorials/older-versions/getting-started-with-mvc/getting-started-with-mvc-part7

skynyrd

the generated classes inside MySystemModel.emdx folder

These are the domain models and you typically should not expose to the UI layer. So create a similar model (including only the properties needed for the view) in the Model class. Here you can define your attributes. Sometimes the models in the Model folder
is called as ViewModels as they may map to multiple domain models.

Hi,

You need another partial class. Normally this new partial class using the same name with it’s model class. Here we can not make it on the same folder. In this case I add one more folder only for validation. Like the first reply your do not change any model
class on .edmx because it generated automatically and update when it needed. Say on Models folder add a "partial" folder, and change the partial class nameSpace without "partial" folder",

Here the example: new partial class on "partial" folder, where this "partial" folder is in "Models" folder: 

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PersonEntity.Models
{
    [MetadataType(typeof(ProductMetadata))]
    public partial class Product
    {
    }
    public class ProductMetadata
    {
        [DisplayFormat(NullDisplayText = "n/a")]
        [Display(Name = "Product Name")]
        public string Name { get; set; }
    }
}

And this is the auto generated class under .edmx on "Models" folder

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace PersonEntity.Models
{
    using System;
    using System.Collections.Generic;
    
    public partial class Product
    {
        public Product()
        {
            this.BillOfMaterials = new HashSet<BillOfMaterial>();
            this.BillOfMaterials1 = new HashSet<BillOfMaterial>();
            this.ProductCosts = new HashSet<ProductCost>();
            this.ProductCostHistories = new HashSet<ProductCostHistory>();
            this.ProductDocuments = new HashSet<ProductDocument>();
            this.ProductListPrices = new HashSet<ProductListPrice>();
            this.ProductListPriceHistories = new HashSet<ProductListPriceHistory>();
            this.ProductReviews = new HashSet<ProductReview>();
            this.ProductPhotoSets = new HashSet<ProductPhotoSet>();
            this.ProductDescriptions = new HashSet<ProductDescription>();
            this.ProductInventories = new HashSet<ProductInventory>();
        }
    
        public int Id { get; set; }
        public string Name { get; set; }
        public string ProductNumber { get; set; }
        public string CompatibleProductNumber { get; set; }
        public bool MakeFlag { get; set; }
        public bool FinishedGoodsFlag { get; set; }
        public string Color { get; set; }
        public short SafetyStockLevel { get; set; }
        public short ReorderPoint { get; set; }
        public decimal StandardCost { get; set; }
        public decimal ListPrice { get; set; }
        public Nullable<decimal> Size { get; set; }
        public string SizeUnitMeasureCode { get; set; }
        public string WeightUnitMeasureCode { get; set; }
        public Nullable<decimal> Weight { get; set; }
        public int DaysToManufacture { get; set; }
        public string ProductLine { get; set; }
        public string Class { get; set; }
        public string Style { get; set; }
        public Nullable<int> ProductCategoryID { get; set; }
        public Nullable<int> ProductModelID { get; set; }
        public System.DateTime SellStartDate { get; set; }
        public Nullable<System.DateTime> SellEndDate { get; set; }
        public Nullable<System.DateTime> DiscontinuedDate { get; set; }
        public System.DateTime ModifiedDate { get; set; }
    
        public virtual ICollection<BillOfMaterial> BillOfMaterials { get; set; }
        public virtual ICollection<BillOfMaterial> BillOfMaterials1 { get; set; }
        public virtual ProductCategory ProductCategory { get; set; }
        public virtual UnitMeasure UnitMeasure { get; set; }
        public virtual UnitMeasure UnitMeasure1 { get; set; }
        public virtual ICollection<ProductCost> ProductCosts { get; set; }
        public virtual ICollection<ProductCostHistory> ProductCostHistories { get; set; }
        public virtual ICollection<ProductDocument> ProductDocuments { get; set; }
        public virtual ICollection<ProductListPrice> ProductListPrices { get; set; }
        public virtual ICollection<ProductListPriceHistory> ProductListPriceHistories { get; set; }
        public virtual ICollection<ProductReview> ProductReviews { get; set; }
        public virtual ICollection<ProductPhotoSet> ProductPhotoSets { get; set; }
        public virtual ICollection<ProductDescription> ProductDescriptions { get; set; }
        public virtual ICollection<ProductInventory> ProductInventories { get; set; }
    }
}

Summary:

Validation for Product.cs is under "PersonEntity.Models.Partial",

Auto generate for Product.cs is under "PersonEntity.Models"

Both Product.cs has the same NameSpace at the example is "namespace PersonEntity.Models"

This is the way I do. That example is my real project.

Have fun

Great stuff guys.

I am using this Metadata classes and they work great.

I don’t even have to copy all the properties into them, just the ones I want to set some attributes.

Thanks!

Hi,

We also can add a new "Read Only" property on the new partial class, look at the following example. This additional property does not have any effect on database table. We can directly use "FullName" in our business logic. This is the beauty of partial class
and help me a lot reducing coding in both client side and server side.

Here additional partial class:

using System.ComponentModel.DataAnnotations;

namespace PersonEntity.Model
{
    [MetadataType(typeof(PersonMetadata))]
    public partial class Person
    {
        [Display(Name = "Full Name")]
        public string FullName
        {
            get
            {
                string fullName = string.Empty;
                if (NameStyle)
                    fullName = ((FirstName + " " + MiddleName).Trim() + " " + LastName).Trim();
                else
                {
                    fullName = LastName == string.Empty || LastName == null ? string.Empty : (LastName + " " + MiddleName).Trim();
                    fullName = fullName == string.Empty ? FirstName : (fullName + ", " + FirstName).Trim();
                }

                return fullName;
            }
        }

        public class PersonMetadata
        {
            [StringLength(25, ErrorMessage = "First name must be 25 characters or less in length.")]
            [Required(ErrorMessage = "First name is required.")]
            public string FirstName { get; set; }
        }
    }
}

This FullName property is depending on NameStyle, FirstName, MiddleName, and LastName. It also cover eastern name style, and western name style automatically. Again there is no effect on database side.

Here the auto generated class:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace PersonEntity.Model
{
    using System;
    using System.Collections.Generic;
    
    public partial class Person
    {
        public Person()
        {
            this.Attendances = new HashSet<Attendance>();
            this.Sessions = new HashSet<Session>();
            this.FactComments = new HashSet<FactComment>();
            this.FactFindings = new HashSet<FactFinding>();
            this.FactFindings1 = new HashSet<FactFinding>();
            this.FactFindings2 = new HashSet<FactFinding>();
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public string NickName { get; set; }
        public Nullable<int> PersonTypeId { get; set; }
        public bool NameStyle { get; set; }
        public string Title { get; set; }
        public string Suffix { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
        public string Blog { get; set; }
        public string Twitter { get; set; }
        public string Gender { get; set; }
        public string ImageSource { get; set; }
        public string Bio { get; set; }
        public int PersonCategoryId { get; set; }
        public System.DateTime BirthDate { get; set; }
        public string MaritalStatus { get; set; }
        public System.DateTime DateAdd { get; set; }
        public string AdditionalContactInfo { get; set; }
        public string Demographics { get; set; }
        public System.Guid RowGuid { get; set; }
        public System.DateTime ModifiedDate { get; set; }
        public bool IsLocked { get; set; }
    
        public virtual ICollection<Attendance> Attendances { get; set; }
        public virtual ICollection<Session> Sessions { get; set; }
        public virtual ICollection<FactComment> FactComments { get; set; }
        public virtual ICollection<FactFinding> FactFindings { get; set; }
        public virtual ICollection<FactFinding> FactFindings1 { get; set; }
        public virtual ICollection<FactFinding> FactFindings2 { get; set; }
    }
}

Have fun

Leave a Reply