Entity Framework code-first, Null entries being written to database

49 Views Asked by At

Refer to my code example below where I'm trying to implement a code-first connection to a SQLite database. I am working in .NET 4.8 (not Core). All the column names and database table names are correct. Id is the unique primary key in all database tables. The database tables for Derived_B and Derived_A are distinct. Both have Id, Name and Information columns.

The Widgets table has a foreign key relationship to the Id in Derived_B, via an additional integer column not listed in the above code. I can programmatically create instances of Derived_A, Derived_B and Widgets, add them to the Example_DataContext, and call SaveChanges().

Afterwards, valid entries for Derived_A and Widgets are written to the database. The new entries in the Widgets table have valid foreign-key relationship back to the Derived_B table.

Here is where I am stuck -- while appearing to have the correct number of entries, the new entries in Derived_B are empty apart from the index column. The logged output from the datacontext indicates that just the ID column is being written to Derived_B. Why? What am I missing?

I would expect that the new rows in Derived_B would have valid data.

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;

namespace entityExample
{

    class Baseclass
    {
        [Column(name:"Name")]
        public string name { get; internal set; }
        
        [Column(name: "Information")]
        public string information { get; internal set; }
    }

    [Table("Derived_A")]
    class Derived_A : Baseclass
    {
        [Column(name: "Id")]
        public int Id { get; set; }

        public Derived_A() {}
    }

    [Table("Derived_B")]
    class Derived_B : Derived_A
    {
        public Derived_B() {}

        public List<Widget> widgets { get; internal set; }
    }

    [Table("Widgets"]
    class Widget
    {
        [Column(name: "Id")]
        public int Id { get; set; }

        [Column(name: "Name")]
        public string name { get; internal set; }

        [Column(name: "Description")]
        public string description { get; internal set; }
    }


    class Example_DataContext : DbContext
    {
        public DbSet<Derived_A> Derived_As {get; set;}
        public DbSet<Derived_B> Derived_Bs { get; set;}
        public DbSet<Widget> Widgets { get; set;}
        
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
        
            modelBuilder.Entity<Derived_A>().ToTable("Derived_A");
            modelBuilder.Entity<Derived_B>().ToTable("Derived_B");
        }
    }
}

My code for saving to database:

foreach (Baseclass item in items)
{
    if (item is Derived_B derivedB)
    {
        dbContext.Derived_Bs.Add(derivedB);
    }
    if (item is Derived_A derivedA)
    {
        dbContext.Derived_As.Add(derivedA);
    }
}

dbContext.SaveChanges();
1

There are 1 best solutions below

0
Steve Py On

If you want each subclass to contain the shared columns you need to tell EF explicitly to use Table-per-Concrete configuration, otherwise it will likely just assume that since B inherits from A, and A inherits from Base, then either Base or "A" will contain the name and other columns, and B's Id will link back to A to associate the two.

modelBuilder.Entity<Baseclass>().UseTpcMappingStrategy();
modelBuilder.Entity<Derived_A>().UseTpcMappingStrategy();

I honestly would avoid deep inheritance, and pretty much any inheritance when it comes to EF Entities. Code like:

if (item is Derived_B derivedB)
{
    dbContext.Derived_Bs.Add(derivedB);
}
if (item is Derived_A derivedA)
{
    dbContext.Derived_As.Add(derivedA);
}

is a notable red flag for maintainability.