1

I'm using ASP.NET Web API 2 with Entity Framework 6.2 to make a small API system.

I'm using code-first approach to create my database. Everything is fine, until I realize there is one column Discriminator in my User table.

I spent hours searching for solutions and almost topics said Discriminator is created if the entity is inherit another. But my User entity is standalone one.

This is my User entity:

public class User
{
    #region Properties

    public int Id { get; set; }

    public string Email { get; set; }

    [JsonIgnore]
    public string Password { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Photo { get; set; }

    public double Birthday { get; set; }

    public UserRoles Role { get; set; }

    #endregion

    #region Navigation properties

    [JsonIgnore]
    public virtual ICollection<SkillCategory> SkillCategories { get; set; }

    [JsonIgnore]
    public virtual ICollection<UserDescription> UserDescriptions { get; set; }

    [JsonIgnore]
    public virtual ICollection<Project> Projects { get; set; }

    [JsonIgnore]
    public virtual ICollection<Hobby> Hobbies { get; set; }

    #endregion
}

This is my database context:

public class CvManagementDbContext : DbContext
{
    #region Constructors

    public CvManagementDbContext() : base("CvManagement")
    {
        Database.SetInitializer<CvManagementDbContext>(null);
    }

    #endregion

    #region Overriden methods

    /// <summary>
    ///     Called when model is being created.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
    {
        // Remove pluralizing naming convention.
        dbModelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        // Prevent database from cascade deleting.
        dbModelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        // Initialize user table.
        InitializeUserTable(dbModelBuilder);

        // Initialize user description table.
        InitializeUserDescriptionTable(dbModelBuilder);

        // Initialize project table.
        InitializeProjectTable(dbModelBuilder);

        // Initialize project skill table.
        InitializeProjectSkillTable(dbModelBuilder);

        // Initialize skill table.
        InitializeSkillTable(dbModelBuilder);

        // Initialize project responsibility table.
        InitializeProjectResponsibilityTable(dbModelBuilder);

        // Initialize responsibility table.
        InitializeResponsibilityTable(dbModelBuilder);

        // Initialize skill table.
        InitializeSkillCategory(dbModelBuilder);

        // Initialize skill category skill relationship table.
        InitializeSkillCategorySkillRelationshipTable(dbModelBuilder);

        base.OnModelCreating(dbModelBuilder);
    }

    #endregion

    #region Properties

    /// <summary>
    ///     List of projects.
    /// </summary>
    public DbSet<Project> Projects { get; set; }

    /// <summary>
    ///     List of skills.
    /// </summary>
    public DbSet<Skill> Skills { get; set; }

    /// <summary>
    ///     List of personal skills.
    /// </summary>
    public DbSet<SkillCategorySkillRelationship> PersonalSkills { get; set; }

    /// <summary>
    ///     List of project responsibilities.
    /// </summary>
    public DbSet<ProjectResponsibility> ProjectResponsibilities { get; set; }

    /// <summary>
    ///     List of project skills.
    /// </summary>
    public DbSet<ProjectSkill> ProjectSkills { get; set; }

    /// <summary>
    ///     List of responsibilities.
    /// </summary>
    public DbSet<Responsibility> Responsibilities { get; set; }

    /// <summary>
    ///     List of skill categories.
    /// </summary>
    public DbSet<SkillCategory> SkillCategories { get; set; }

    /// <summary>
    ///     List of users.
    /// </summary>
    public DbSet<User> Users { get; set; }

    /// <summary>
    ///     List of user descriptions.
    /// </summary>
    public DbSet<UserDescription> UserDescriptions { get; set; }

    #endregion

    #region Table initialization

    /// <summary>
    ///     Initialize user table.
    /// </summary>
    private void InitializeUserTable(DbModelBuilder dbModelBuilder)
    {
        var user = dbModelBuilder.Entity<User>();
        user.HasKey(x => x.Id);
        user.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }

    /// <summary>
    /// Initialize user description tabe,
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeUserDescriptionTable(DbModelBuilder dbModelBuilder)
    {
        var userDescription = dbModelBuilder.Entity<UserDescription>();
        userDescription.HasRequired(x => x.User).WithMany(x => x.UserDescriptions).HasForeignKey(x => x.UserId);
    }

    /// <summary>
    ///     Initialize project table.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeProjectTable(DbModelBuilder dbModelBuilder)
    {
        var project = dbModelBuilder.Entity<Project>();
        project.HasKey(x => x.Id);
        project.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        project.HasRequired(x => x.User).WithMany(x => x.Projects).HasForeignKey(x => x.UserId);
    }

    /// <summary>
    ///     Initialize project skill table.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeProjectSkillTable(DbModelBuilder dbModelBuilder)
    {
        var projectSkill = dbModelBuilder.Entity<ProjectSkill>();
        projectSkill.HasKey(x => new { x.ProjectId, x.SkillId });

        projectSkill.HasRequired(x => x.Skill).WithMany(x => x.ProjectSkills).HasForeignKey(x => x.SkillId);
        projectSkill.HasRequired(x => x.Project).WithMany(x => x.ProjectSkills).HasForeignKey(x => x.ProjectId);
    }

    /// <summary>
    ///     Initialize skill table.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeSkillTable(DbModelBuilder dbModelBuilder)
    {
        var skill = dbModelBuilder.Entity<Skill>();
        skill.HasKey(x => x.Id);
        skill.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }

    /// <summary>
    ///     Initialize project responsibility table.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeProjectResponsibilityTable(DbModelBuilder dbModelBuilder)
    {
        var projectResponsibility = dbModelBuilder.Entity<ProjectResponsibility>();
        projectResponsibility.HasKey(x => new { x.ProjectId, x.ResponsibilityId });

        projectResponsibility.HasRequired(x => x.Project).WithMany(x => x.ProjectResponsibilities)
            .HasForeignKey(x => x.ResponsibilityId);

        projectResponsibility.HasRequired(x => x.Responsibility).WithMany(x => x.ProjectResponsibilities)
            .HasForeignKey(x => x.ResponsibilityId);
    }

    /// <summary>
    ///     Initialize responsibility table.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeResponsibilityTable(DbModelBuilder dbModelBuilder)
    {
        var responsibility = dbModelBuilder.Entity<Responsibility>();
        responsibility.HasKey(x => x.Id);
    }

    /// <summary>
    ///     Initialize skill category.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeSkillCategory(DbModelBuilder dbModelBuilder)
    {
        var skillCategory = dbModelBuilder.Entity<SkillCategory>();
        skillCategory.HasKey(x => x.Id);
        skillCategory.Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        skillCategory.HasRequired(x => x.User).WithMany(x => x.SkillCategories).HasForeignKey(x => x.UserId);
    }

    /// <summary>
    ///     Initialize skill category - skill relationship.
    /// </summary>
    /// <param name="dbModelBuilder"></param>
    private void InitializeSkillCategorySkillRelationshipTable(DbModelBuilder dbModelBuilder)
    {
        var skillCategorySkillRelationship = dbModelBuilder.Entity<SkillCategorySkillRelationship>();
        skillCategorySkillRelationship.HasKey(x => new { x.SkillCategoryId, x.SkillId });

        skillCategorySkillRelationship.HasRequired(x => x.Skill).WithMany(x => x.SkillCategorySkillRelationships).HasForeignKey(x => x.SkillId);
        skillCategorySkillRelationship.HasRequired(x => x.SkillCategory)
            .WithMany(x => x.SkillCategorySkillRelationships).HasForeignKey(x => x.SkillCategoryId);
    }

    #endregion
}

When I run Add-Migration command, it always create the Discriminator.

What wrong have I done ?

Thank you,

** UPDATE **

After having found this topic, I did searching my project and realized there was one class UserViewModel that inherits User entity.

This is my UserViewModel code:

public class UserViewModel : Models.Entities.User
{

}

When I removed the inheritance and Add-Migrations again, Discriminator column was gone magically.

I don't know if this is a feature or bug of Entity Framework 6.2, seems like EF search for all project and find the inheritance, even the inherited class isn't included in Database context file.

Are there any solutions for turning off this "feature" automatically ?

Thanks,

Redplane
  • 2,971
  • 4
  • 30
  • 59
  • That is called TPH (table per hierarchy) or TPC(Table per concrete) mapping strategy. You will find much stuff related to your question. – ilkerkaran Aug 01 '18 at 06:54
  • You can try using the `[NotMapped]` attribute on the `UserViewModel` class if your intention is that this class is not modeled as an entity. – Jurgen Camilleri Aug 01 '18 at 06:56
  • Can I disable TPH automatically ? In future, if I forget adding `[NotMapped]` attribute, there could be the same trouble :( – Redplane Aug 01 '18 at 07:00
  • There's no way to remove the inheritance mapping. At the time EF sees the inheritance on the User class, it does not know whether the inherited class will be part of the model or not (if it isn't excluded explicitly). Since TPH is the default inheritance mapping mode, this results in discriminator columns (those really don't hurt, though, unless you are bound to a DB schema) – DevilSuichiro Aug 01 '18 at 07:47
  • Having your ViewModel inherit from your Model class is not a good idea to start with, btw – Sentry Aug 01 '18 at 08:33
  • @Sentry, what if my view model class has same properties with model ? What should I do ? I just want to follow DRY – Redplane Aug 01 '18 at 09:03
  • @Redplane that is a pretty good and not so easy question, so I'll just point you to this question: https://stackoverflow.com/questions/963871/does-mvvm-violate-dry - Personally, If your ViewModel is the same as your model, I'd have just one class for them. – Sentry Aug 01 '18 at 18:57
  • @Sentry, thanks for your information. In summary, I think there is no fluent API to help me disable the TPH functionality. I have to prevent inheritance between view model and entities completely. – Redplane Aug 07 '18 at 02:51
  • There is a Fluent API equivalent to `[NotMapped]`: `Ignore<...>()` as you can see in [this answer](https://stackoverflow.com/a/30643833/391338). You could also write your own code that somehow iterates over the classes in your model and calls `Ignore` for those in a different namespace. But I don't think this is worth the effort. – Sentry Aug 07 '18 at 09:07

0 Answers0