Search code examples
entity-frameworkef-code-firstef-database-firsthybrid

Entity Framework generate sql with column that does not exist in database or code first model


I have a situation where the entity framework is adding a contract_id column to a contract model and the column does not exist as a property on C# model and it does not exist in the database either. I have already combed the database and the solution for any reference to 'contract_id' and I am scratching my head for this one.

[Export(typeof(IModel))]
[Grid.GridNavigator(cloneToTop = true, add = true, del = true, refresh = true, refreshstate = Grid.RefreshState.current, addfunc = "$.modules.grids.contract.add")]
[Grid.Events(serializeGridData = "$.modules.grids.common.serialize")]
public class Contract
    : Model
    , ILogicalDelete
{
    #region properties
    [Key]
    [Grid.Column("ContractID", TypeName = "int", Label = "Contract ID", Width = 75, VisibleTo = new Type[] { typeof(Contract) })]
    [Grid.Search(SearchOperators.equal)]
    [Grid.Controls.Hidden]
    public override int ID { get { return base.ID; } set { base.ID = value; } }

    [Grid.Column("CorrelationID", Hidden = true)]
    [Grid.Controls.Hidden]
    public Guid? CorrelationID { get; set; }

    //[Grid.Column("StatusID", ModelNavigation = "Status", HiddenFromNavigation = true)]
    //[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    //public int StatusID { get; set; }

    [MaxLength(100)]
    [Grid.Column("ContractDesc", TypeName = "varchar", Label = "Description")]
    [Grid.Search(SearchOperators.begins_with | SearchOperators.contains | SearchOperators.ends_with)]
    [Grid.Controls.CustomEditable(Grid.ControlType.autocomplete, Url = "/api/contractservice/list?name=contract&column=ContractDesc&_type={0}", KeyField = "ID", TextField = "ContractDesc", ForReferenceUse = true)]
    public string ContractDesc { get; set; }

    [MaxLength(100)]
    [Grid.Column("ContractType", TypeName = "varchar", Label = "Type", VisibleTo = new Type[] { typeof(Contract) }, HiddenFromNavigation = true)]
    [Grid.Search(Grid.SearchType.select, url = "/api/contractservice/list?name=ContractType", build = "$.services.grid.ctrls.select.build")]
    //[Grid.Controls.Select(Url = "/api/contractservice/list?name=ContractType", BuildSelect = "$.services.grid.ctrls.select.build")]
    public string ContractType { get; set; }

    [Grid.Column("Audited", TypeName = "bit", FormatterType = Grid.FormatterType.checkbox, Alignment = Grid.Position.center, Width = 50, HiddenFromNavigation = true)]
    [JsonConverter(typeof(Grid.Serialization.BooleanConverter))]
    public bool Audited { get; set; }

    [MaxLength(255)]
    [Grid.Column("Comment", TypeName = "varchar", HiddenFromNavigation = true)]
    public string Comment { get; set; }

    [Grid.Column("CreatedOn", TypeName = "date", Label = "Created On", FormatterType = Grid.FormatterType.date, Alignment = Grid.Position.center, Width = 75, Hidden = true, HiddenFromNavigation = true)]
    public DateTime? CreatedOn { get; set; }

    [Grid.Column("IsDeleted", TypeName = "bit", Hidden = true, Default = false, HiddenFromNavigation = true)]
    public bool is_deleted { get; set; }
    #endregion

    #region constructor
    public Contract()
    {
        Members = new HashSet<ContractMember>();
        PricingTiers = new HashSet<TierMatrix>();
        Terms = new HashSet<ContractDetail>();
    }
    #endregion

    //Status _status = null;

    //public virtual Status Status { get { return _status; } set { StatusID = value.IsNotNull(oObject => oObject.ID, StatusID); _status = value; } }

    [JsonIgnore]
    public virtual ICollection<ContractMember> Members { get; set; }

    public virtual ICollection<ContractMemberDetail> MemberDetails { get; set; }

    [JsonIgnore]
    public virtual ICollection<TierMatrix> PricingTiers { get; set; }

    [JsonIgnore]
    public virtual ICollection<ContractDetail> Terms { get; set; }
}

Configuration:

class ContractConfiguration
    : EntityTypeConfiguration<Models.Contract>
{
    internal ContractConfiguration(string table, string schema = "dbo")
    {
        ToTable(table, schema);

        HasKey(Model => Model.ID);

        Property(Model => Model.ID)
            .HasColumnName("ContractID")
            .HasColumnType("int")
            .HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);

        Property(Model => Model.ContractDesc)
            .HasColumnName("ContractDesc")
            .HasColumnType("varchar")
            .IsOptional()
            .HasMaxLength(100);

        Property(Model => Model.ContractType)
            .HasColumnName("ContractType")
            .HasColumnType("varchar")
            .IsRequired()
            .HasMaxLength(100);

        Property(Model => Model.Audited)
            .HasColumnName("Audited")
            .HasColumnType("bit")
            .IsRequired();

        Property(Model => Model.Comment)
            .HasColumnName("Comment")
            .HasColumnType("varchar")
            .IsOptional()
            .HasMaxLength(255);

        Property(Model => Model.CreatedOn)
            .HasColumnName("CreatedOn")
            .HasColumnType("smalldatetime")
            .IsRequired();

        Property(Model => Model.CorrelationID)
            .HasColumnName("CorrelationID")
            .HasColumnType("uniqueidentifier")
            .IsOptional();

        Property(Model => Model.is_deleted)
            .HasColumnName("IsDeleted")
            .HasColumnType("bit")
            .IsRequired();

        HasMany(Model => Model.Members)
           .WithRequired(Model => Model.Contract);

        HasMany(Model => Model.PricingTiers)
            .WithRequired(Model => Model.Contract);

        HasMany(Model => Model.Terms)
            .WithRequired(Model => Model.Contract);
    }
}

Model Builder:

protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new Configurations.ContractConfiguration("Contract", Common.Schema.contract_management));
    }

watching this variable: Context().Set(typeof(Models.Contract)).ToString()

generates the following:

SELECT 
[Extent1].[ContractID] AS [ContractID], 
[Extent1].[CorrelationID] AS [CorrelationID], 
[Extent1].[ContractDesc] AS [ContractDesc], 
[Extent1].[ContractType] AS [ContractType], 
[Extent1].[Audited] AS [Audited], 
[Extent1].[Comment] AS [Comment], 
[Extent1].[CreatedOn] AS [CreatedOn], 
[Extent1].[IsDeleted] AS [IsDeleted], 
[Extent1].[Contract_ID] AS [Contract_ID] //<--- this is what does not belong
FROM [cm].[Contract] AS [Extent1]

I have reviewed the database drop and create scripts for the table and Contract_ID does not appear in them. I have reviewed all the foreign keys related to this table and made sure they are ContractID not Contract_ID. the text Contract_ID appears no where in the solutions code base either. we also do not have any triggers on this database table.

I am running out of places to check in the code. I am contemplating a band aid that will create a computed column called Contract_ID that regurgitates the primary key value. If anyone can give me an idea on where to look that would be awesome.


Solution

  • okay, I finally rebuilt the C# model from scratch and I have found the cause of the extra column and problem was in the foreign key mappings

        [JsonIgnore]
        public virtual ICollection<ContractMember> Members { get; set; }
        [JsonIgnore]
        public virtual ICollection<ContractMemberDetail> MemberDetails { get; set; }
        [JsonIgnore]
        public virtual ICollection<TierMatrix> PricingTiers { get; set; }
        [JsonIgnore]
        public virtual ICollection<ContractDetail> Terms { get; set; }
    

    located on the C# model.

    turns out mapping this from the perspective of the contract model.

            HasMany(Model => Model.Members)
               .WithRequired(Model => Model.Contract);
    
            HasMany(Model => Model.MemberDetails)
               .WithOptional(Model => Model.Contract);
    
            HasMany(Model => Model.PricingTiers)
                .WithRequired(Model => Model.Contract);
    
            HasMany(Model => Model.Terms)
                .WithRequired(Model => Model.Contract);
    

    removes the need from mapping collections from perspective of these four models. in fact including the foreign key mappings on the Member, MemberDetails, PricingTiers and Terms. did something to EF where the framework logic decided that it needed Contract_ID column on the contract model when none was found.