dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.73k stars 3.17k forks source link

Make inheritance mapping conventions more "assertive" #3860

Closed divega closed 8 years ago

divega commented 8 years ago

Conclusion for the moment is that we will just block having types that inherit in CLR but not in the model. This will give an exception in this case - perhaps not the clearest message, but at least you don't inadvertently get the wrong thing.


Currently it is possible to break an inheritance hierarchy by providing entity key configuration on derived types first and then conflicting configuration on a base type.

We should consider throwing an exception at conflicting key configuration regardless of the order.

@rowanmiller could you paste the repro code here?

rowanmiller commented 8 years ago

The following code results in a model where AllergenQuantity and AllergenQuantityMaterial are not in an inheritance hierarchy.

using Microsoft.Data.Entity;
using System;
using System.Collections.Generic;

namespace Repro
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new MyContext())
            {
                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                var peanutButter = new Material { Name = "Peanut Butter" };
                db.Materials.Add(peanutButter);

                var peanuts = new Allergen { Name = "Peanuts" };
                db.Allergens.Add(peanuts);

                db.AllergenQuantityMaterials.Add(new AllergenQuantityMaterial
                {
                    Allergen = peanuts,
                    Material = peanutButter,
                    Quantity = 1000
                });

                db.SaveChanges();
            }
        }
    }

    public class MyContext : DbContext
    {
        public DbSet<Material> Materials { get; set; }
        public DbSet<Allergen> Allergens { get; set; }
        public DbSet<AllergenQuantityMaterial> AllergenQuantityMaterials { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder
                .Entity<AllergenQuantityMaterial>()
                .HasKey(l => new { l.MaterialId, l.AllergenId });

            modelBuilder.Entity<AllergenQuantity>().HasKey(a => a.Quantity);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Repro;Trusted_Connection=True;");
        }
    }

    public class AllergenQuantity
    {
        public decimal Quantity { get; set; }
    }

    public class AllergenQuantityMaterial : AllergenQuantity
    {
        public Guid MaterialId { get; set; }
        public virtual Material Material { get; set; }

        public Guid AllergenId { get; set; }
        public virtual Allergen Allergen { get; set; }
    }

    public class Material
    {
        public Material()
        {
            Id = Guid.NewGuid();
        }

        public Guid Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<AllergenQuantityMaterial> AllergenQuantityMaterial { get; set; }
    }

    public class Allergen
    {
        public Allergen()
        {
            Id = Guid.NewGuid();
        }

        public Guid Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<AllergenQuantityMaterial> AllergenQuantityMaterial { get; set; }
    }

}