EF Core - one for many with parent and child inheritance

Summary

I am trying to use Inheritance to Hierarchy inheritance in combination with the one-to-many relationship in a .Net Core 1.1 project. Both the parent and child objects use inheritance. I have a very simple entity model. I have one basic parent object: Session , which has two objects that extend from it: QuotingSession and BackOfficeSession . Both of these parent objects contain a set of child entities (one-to-many relationship). In addition, child objects are created using inheritance. There is a base child: Policy . This base child object is extended by two objects: QuotingPolicy and BackOfficePolicy. When I create any of the parent objects and try to save, I get this exception:

InvalidCastException: Unable to cast object of type 'EFTest.QuotingSession' to type 'EFTest.BackOfficeSession

However, when I tell the entity structure to ignore one of the child collections on either of the parents, persistence works for both parent objects without exception. For instance:

 var entity = modelBuilder.Entity<BackOfficeSession>();
 entity.Ignore(c => c.Policies); 

In addition, if I did not configure one of the parents (QuotingSession) at all and just saved the other parent (BackOfficeSession), it saves everything as freed.

Repo

https://github.com/seantarogers/EFTest

More details

I use Microsoft.EntityFrameworkCore 1.1.1and Microsoft.EntityFrameworkCore.SqlServer 1.1.1in the project .Net core 1.1.

I have the following simple database schema:

enter image description here

My classes are as follows:

1. Session

namespace EFTest
{
    public abstract class Session
    {
        public int Id { get; private set; }
    }
}

2. QuotingSession

using System.Collections.Generic;

namespace EFTest
{
    public class QuotingSession : Session
    {
        public string QuotingName { get; private set; }
        public List<QuotingPolicy> Policies { get; private set; }

        private QuotingSession()
        {
        }

        public QuotingSession(string name, List<QuotingPolicy> quotingPolicies)
        {
            QuotingName = name;
            Policies = quotingPolicies;

        }
   }
}

3. BackOfficeSession

using System.Collections.Generic;

namespace EFTest
{
    public class BackOfficeSession : Session
    {
        public List<BackOfficePolicy> Policies { get; private set; }
        public string BackOfficeName { get; private set; }

        private BackOfficeSession()
        {
        }

        public BackOfficeSession(string name, List<BackOfficePolicy> policies)
        {
            BackOfficeName = name;
            Policies = policies;
        }
    }
}

4. Policy

namespace EFTest
{
    public abstract class Policy
    {
        public int Id { get; set; }
        public int SessionId { get; set; }
    }
}

5. QuotingPolicy

namespace EFTest
{
    public class QuotingPolicy : Policy
    {
        public string QuotingPolicyName { get; private set; }

        private QuotingPolicy()
        {

        }

        public QuotingPolicy(string name)
        {
            QuotingPolicyName = name;
        }
    }
}

6. BackOfficePolicy

namespace EFTest
 {
    public class BackOfficePolicy : Policy
    {
        public string BackOfficePolicyName { get; private set; }

        private BackOfficePolicy()
        {
        }

        public BackOfficePolicy(string name)
        {
           BackOfficePolicyName = name;
        }
    }
}

7. EF DB context and free configuration

using Microsoft.EntityFrameworkCore;

namespace EFTest
{
    public class TestDbContext : DbContext
    {
        public TestDbContext(DbContextOptions options)
        : base(options)
        {
        }

        public DbSet<QuotingSession> QuotingSessions { get; set; }
        public DbSet<BackOfficeSession> BackOfficeSessions { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            ConfigureSession(modelBuilder);
            ConfigurePolicy(modelBuilder);
            ConfigureQuotingSession(modelBuilder);
            ConfigureBackOfficeSession(modelBuilder);
            ConfigureBackOfficePolicy(modelBuilder);
            ConfigureQuotingPolicy(modelBuilder);
        }

        public static void ConfigurePolicy(ModelBuilder modelBuilder)
        {
            var entity = modelBuilder.Entity<Policy>();
            entity.ToTable("Policy", "dbo");
            entity.HasKey(x => x.Id);

            entity.HasDiscriminator<int>("SessionType")
                .HasValue<QuotingPolicy>(1)
                .HasValue<BackOfficePolicy>(2);
        }

        public static void ConfigureBackOfficePolicy(ModelBuilder modelBuilder)
        {
            var entity = modelBuilder.Entity<BackOfficePolicy>();
            entity.Property(x => x.BackOfficePolicyName);
        }

        public static void ConfigureQuotingPolicy(ModelBuilder modelBuilder)
        {
            var entity = modelBuilder.Entity<QuotingPolicy>();
            entity.Property(x => x.QuotingPolicyName);
        }

        public static void ConfigureSession(ModelBuilder modelBuilder)
        {
            var entity = modelBuilder.Entity<Session>();
            entity.ToTable("Session", "dbo");
            entity.HasKey(x => x.Id);

            entity.HasDiscriminator<int>("SessionType")
                .HasValue<QuotingSession>(1)
                .HasValue<BackOfficeSession>(2);
        }

        public static void ConfigureBackOfficeSession(ModelBuilder modelBuilder)
        {
            var entity = modelBuilder.Entity<BackOfficeSession>();
            entity.Property(x => x.BackOfficeName);
            entity.HasMany(c => c.Policies).WithOne().HasForeignKey(c => c.SessionId);
       // entity.Ignore(c => c.Policies); uncomment this to see it working
        }

        public static void ConfigureQuotingSession(ModelBuilder modelBuilder)
        {
            var entity = modelBuilder.Entity<QuotingSession>();
            entity.Property(x => x.QuotingName);
            entity.HasMany(c => c.Policies).WithOne().HasForeignKey(c => c.SessionId);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
        }
    }
}

8. To check it out

using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace EFTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<TestDbContext>();
            const string conn = "Server=.\\SqlServer2014;Database=EFTest;Trusted_Connection=True"    
            optionsBuilder.UseSqlServer(conn);
            using (var dbContext = new TestDbContext(optionsBuilder.Options))
            {
                var quotingPolicy = new QuotingPolicy("quotingPolicyname");
                var quotingSession = new QuotingSession("quotingSessionName", new List<QuotingPolicy> {quotingPolicy});

                dbContext.QuotingSessions.Add(quotingSession);
                dbContext.SaveChanges();  // BLOWS UP HERE!
           }
        }
    }
}

thanks for the help

+4

Source: https://habr.com/ru/post/1689414/


All Articles