0

I have created a database in SQL Server and have used Entity Framework to create a model in my C# MVC 5 project. In my models I am using System.ComponentModel to give a DisplayName to several properties (or columns). Example:

namespace ProjectTracking.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;

    public partial class EmployeeType
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public EmployeeType()
        {
            this.Employees = new HashSet<Employee>();
        }

        [DisplayName("Employee Type ID")]
        public int PK_EmployeeTypeID { get; set; }

        [DisplayName("Employee Type Name")]
        public string EmployeeTypeName { get; set; }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Employee> Employees { get; set; }
    }
}

The problem that I've run into is that, if I update the database and update my model, I lose all of this information as all of the model classes are regenerated. Is there a way to persist this information?

Salomon Zhang
  • 1,553
  • 3
  • 23
  • 41
DForck42
  • 19,789
  • 13
  • 59
  • 84
  • 3
    ``[DisplayName]`` is a view specific attribute and should be applied to your view models, not your data models ([What is ViewModel in MVC?](https://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc)). And as a side note, use `[Display(Name = "...")]` rather than `[DisplayName]` ([displayname attribute vs display attribute](https://stackoverflow.com/questions/5243665/displayname-attribute-vs-display-attribute)) –  Nov 08 '17 at 21:21

2 Answers2

2

EF is using T4 templates to generate the code. You should never edit these files, because they could be regenerated anytime when you open the designer.

There is a way to do this, using the MetadataTypeAttribute`

EF generates the models as partial classes, so you can add another part in a different file, which won't be regenerated and a the MetadataTypeAttribute to it. This will point to yet another class in which you can duplicate the properties and supply the DisplayNameAttribute. Like this:

[MetadataType(typeof(EmployeeTypeMetaData))]
public partial class EmployeeType  { }

public class EmployeeTypeMetaData
{
    [Required(ErrorMessage = "Title is required.")]
    public object Title;

    // etc
}

This would the job... However:

You should not use this for the following reasons:

  • This will give you a maintenance nightmare. Any time your model changes you need to change the metadata also
  • You should never use your ef models directly in views of some kind. Use DTO objects in between and map between the ef models and the DTO. Mapping can be done either by hand of by a tool like AutoMapper
Ric .Net
  • 5,540
  • 1
  • 20
  • 39
  • 1
    [This](https://www.exceptionnotfound.net/entity-framework-and-wcf-mapping-entities-to-dtos-with-automapper/) seems to be a solid article on a quick Google search. I would however advise to start by mapping by hand before you use AutoMapper to really understand the mapping process and where the benefits are. AutoMapper can act as a magic wand when you don't really understand the process. – Ric .Net Nov 08 '17 at 21:55
  • @Ric.Net especially when Lazy Loading is turned on, Automapper can severely degrade systems. – Erik Philips Nov 08 '17 at 21:56
  • @ErikPhilips exactly! I therefore don't use AutoMapper anymore. Good point! – Ric .Net Nov 08 '17 at 22:18
1

Your best option is to not use Entity Framework entities directly as View Models. Instead create view models that represent what needs to be displayed and map the values from the entities onto the view models (using AutoMapper makes this a cake walk).

What do you mean by "directly as View Models"

Your code probably looks like like:

public class MyContextDb
{
  public DbSet<EmployeeType> EmployeeTypes { get; set; }
}

public class MyController
{
  public ActionResult Index()
  {
    using (var db = new MyDbContext)
    {
      var emp = db.EmployeeTypes.FirstOrDefault();
      return View(emp);  // <-- passing EF entity as view model
    }
  }
}

Instead

public EmployeeTypeVM
{
  // Properties you want to expose and annotate
}

    using (var db = new MyDbContext)
    {
      var emp = db.EmployeeTypes.FirstOrDefault();
      var vm = Mapper.Map<EmployeeTypeVM>(emp);
      return View(vm);  // <-- passing view model
    }

I always recommend this approach as you don't normally change the emp, just the vm with logic and an accidental SaveChanged() doesn't affect real data.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150