6
votes

I'm using MVC3 VS2010 with EF4.1, I have created my DB using SQL Server and I import it to the MVC3 Web Application.

I have a challenge here, when I come to Update Model from Database I do lost all my models files modifications, for example if I'm using attributes in some models for validation or so all that is overwritten with the new model properties.

Is there anyway to Update Model from Database without losing models' information?

OR

where should I define validation on my models instead of using the models' files directly?

4

4 Answers

11
votes

Update: As this is still relatively popular, I have created a blog post on this.

http://jnye.co/Posts/19/adding-validation-to-models-created-by-entity-framework-database-first-c

If you want to validate your models, and not use viewModels, use partial classes to define validation attributes. For example:

Say you have a model like

public class User {
    public string Name { get; set; }
}

If you wanted to put a string length validator on it you would need to create a partial class and utilise the MetadataTypeAttribute (this lives in System.ComponentModel.DataAnnotations)

The following classes should be defined in their own separate file, NOT put in the same file as your auto generated models.

[MetadataTypeAttribute(typeof(UserMetadata))]
public partial class User {
}

You then define your validation in the UserMetadata class as follows

public class UserMetadata{
    [StringLength(50)]
    public string Name {get; set;}
}

EDIT

I just found this article which explains the solution in a little more detail http://themonitoringguy.com/tips-tricks/validating-microsoft-entity-framework-objects-c-mvc/

3
votes

No, the files will be regenerated every time.

All the classes are defined as partial so you can easily add DataAnnotations using the MetadataTypeAttribute.

Let's say you have a User class defined as follow:

public partial class User {
    public string Name {get;set;}
}

Create a IUser interface

public interface IUser {
   [Required]
   [DisplayName("User name")]
   string Name {get;set;}
}

And then extend the User class to specify that IUser will be used as metadata.

[MetadataType(typeof(IUser))]
public partial class User {} //Empty class body
1
votes

The first rule of any designer is: It it generates any code you can't modify it because it will be completely deleted next time you update anything in the designer.

All generated classes are partial so you can create your own partial part and put your custom logic there. You obviously can't add attributes to properties defined in auto generated part. In case of data annotations it is possible either through buddy classes or by custom T4 template which will contain your own logic to decide which data annotation should be added during code generation. Both scenarios are mostly considered as a bad practice because you should have separate view model per view with validation needed exactly for that view.

1
votes

Check the namespace of the MainClass is same as Partial, and have the same Attributes. That is my solution.

example:

Metadata: Create this everywhere u want

public class FormMetadata
{
    public int Id { get; set; }
    public string Description { get; set; }
    public Nullable<bool> IsEnable { get; set; }
    public Nullable<System.DateTime> CreationDate { get; set; }
    public int CompanieId { get; set; }
    public string RegularExpression { get; set; }

    public virtual ICollection<Field> Fields { get; set; }
    [JsonIgnore]
    public virtual Company Company { get; set; }
}

MainClass

namespace Transactions.Model
{
  public partial class Form
  {
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
      public Form()
      {
          this.Fields = new HashSet<Field>();
      }  
      public int Id { get; set; }
      public string Description { get; set; }
      public Nullable<bool> IsEnable { get; set; }
      public Nullable<System.DateTime> CreationDate { get; set; }
      public int CompanieId { get; set; }
      public string RegularExpression { get; set; }
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
      public virtual ICollection<Field> Fields { get; set; }
      public virtual Company Company { get; set; }
  }
}

Partial To Use the MetadataType

namespace Transactions.Model
{
    [MetadataTypeAttribute(typeof(FormMetadata))]
    public partial class Form
    {
    }
}

If you have problems to Create a Class Partial in the same NameSpace? Don't worry:

  1. Create a Folder
  2. Create the Class Partial in this folder
  3. Change Namespace at the same of MainClass