Home > OS >  C# EF - Loading a Collection as child types
C# EF - Loading a Collection as child types

Time:01-02

In my example, I have a Farm with Animals. The Animals within my collection is <Animal, Dog, Cat>. Dog & Cat inherit from Animal.

When loading theFarm entity. How can I make Entity Framework load my instances as their respective subtypes <Animal, Dog, Cat> instead of <Animal, Animal, Animal> ?

    var farm = new Farm();
    farm.Animals.Add(new Animal { Name = "Parrot" });
    farm.Animals.Add(new Dog { Name = "Waffles", FavoriteToy = "slinky" });
    farm.Animals.Add(new Cat { Name = "Pitou", FavoriteFood = "Salmon" });

enter image description here enter image description here

using Microsoft.EntityFrameworkCore;

public class Program
{
    public static void Main(string[] args)
    {
        var p = new Program();
        p.CreateFarm();
        p.Display();

    }
    public void CreateFarm()
    {
        using var context = new MyContext();
        if (context.Farms.FirstOrDefault() == null)
        {
            var farm = new Farm();
            farm.Animals.Add(new Animal { Name = "Parrot" });
            farm.Animals.Add(new Dog { Name = "Waffles", FavoriteToy = "slinky" });
            farm.Animals.Add(new Cat { Name = "Pitou", FavoriteFood = "Salmon" });

            context.Farms.Add(farm);
            context.SaveChanges();
        }
    }
    public void Display()
    {
        using var context = new MyContext();
        var animals = context.Farms.Include(f => f.Animals).First().Animals;
        foreach (var a in animals)
        {
            var txt = $"animal of type : {a.GetType().Name}";
            if (a is Dog or Cat)
            {
                txt  = ". It's a pet!";
            }
            Console.WriteLine(txt);
        }
    }
}
public class MyContext : DbContext
{
    public DbSet<Farm> Farms { get; set; }
    public DbSet<Animal> Animals{ get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlite("Data Source=c:\\temp\\mydb.db;");
    }
}
public class Farm
{
    public int ID { get; set; }
    public virtual ICollection<Animal> Animals { get; set; } = new HashSet<Animal>();
}

public class Animal
{
    public int ID { get; set; }
    public int? FarmID { get; set; }
    public string Name { get; set; }
    public virtual Farm? Farm { get; set; }
}

public class Dog : Animal {
    public string FavoriteToy { get; set; }
}

public class Cat : Animal
{
    public string FavoriteFood { get; set; }
}

CodePudding user response:

Here How I configured context.

   public class MyContext : DbContext
    {
        public DbSet<Farm> Farms { get; set; }
        public DbSet<Animal> Animals { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlite("Data Source=c:\\temp\\mydb.db;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Animal>()
                .HasDiscriminator<string>("AnimalType")
                .HasValue<Animal>("animal")
                .HasValue<Dog>("animal_dog")
                .HasValue<Cat>("animal_cat");
           
        }
    }
    public class Farm
    {
        public int ID { get; set; }
        public virtual ICollection<Animal> Animals { get; set; } = new HashSet<Animal>();
    }

    public class Animal
    {
        public int ID { get; set; }
        public int? FarmID { get; set; }
        public string Name { get; set; }
        public virtual Farm? Farm { get; set; }
    }

    public class Dog : Animal
    {
        public string FavoriteToy { get; set; }
    }

    public class Cat : Animal
    {
        public string FavoriteFood { get; set; }
    }

From above code if you look at the configure.

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Animal>()
                .HasDiscriminator<string>("AnimalType")
                .HasValue<Animal>("animal")
                .HasValue<Dog>("animal_dog")
                .HasValue<Cat>("animal_cat");

        }

Here few things.

  • Discriminator is one column in table and it hold type like dog,cat or animal.
  • Then there are three possible type possible. One is Dog, Cat and Animal itself and it configured with HasValue.

Example or demo code is similar to what is in the question so I am not repeating that over here.

  • Related