title: EF Core 2.1 Discriminated Type With Many->Many Collection
body: <p>I have a model that is roughly along the following lines:</p>
  
  <pre><code>Expression
  ExpressionGroup
    - Expressions (Polymorphic collection via base type that is Either Expression or ExpressionGroup)
    ^ Achieved by means of a base class that is configured with a discriminator column (Type enum)
  </code></pre>
  
  <p>The system I'm working on requires the following:</p>
  
  <ul>
  <li>Expressions &amp; Groups are uniquely identifiable, and should be shared objects (e.g. Multiple groups can reference the same expression, and the same groups)</li>
  </ul>
  
  <p>I have this configuration set up, with the <code>HasMany(group =&gt; group.Expressions).WithOne()</code> config because it's the case that a group can have many expression components (base type), but that there's no meaningful navigation property backwards.</p>
  
  <p>This has resulted in a column on each component that tracks which group it belongs to, and when I add a new group referencing an existing expression, the previous group that referenced it loses that reference, and the new one takes over.</p>
  
  <p><a href="https://i.stack.imgur.com/Lb2yM.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Lb2yM.png" alt="Resulting SQL Structure &amp; Values"></a></p>
  
  <p>I need to be able to have multiple groups reference the same expression (or group/s), but there's no domain reason for going from an expression to a group.</p>
  
  <p>Would the correct way around this just be to introduce a collection nav property against the expression to link to the group(/s) it belongs to?</p>
  
  <p>Or is there some weird Many&lt;->Many change I need to introduce?</p>
  
  <hr>
  
  <p>Below is the entity configuration for these types.</p>
  
  <pre><code>void IEntityTypeConfiguration&lt;BA.MetaBlockExpressionBase&gt;.Configure(EntityTypeBuilder&lt;BA.MetaBlockExpressionBase&gt; builder)
  {
      builder.ToTable("ExpressionComponents");
  
      builder.HasDiscriminator(e =&gt; e.Type)
          .HasValue&lt;BA.MetaBlockExpression&gt;(DTO.Topics.Blocks.MetaBlockExpressionType.Expression)
          .HasValue&lt;BA.MetaBlockExpressionGroup&gt;(DTO.Topics.Blocks.MetaBlockExpressionType.Group);
  
      builder.Property(e =&gt; e.Token)
          .IsRequired();
  
      builder.Property(e =&gt; e.Type)
          .IsRequired();
  
      builder.HasIndex(e =&gt; e.Token)
          .IsUnique(true);
  }
  
  void IEntityTypeConfiguration&lt;BA.MetaBlockExpression&gt;.Configure(EntityTypeBuilder&lt;BA.MetaBlockExpression&gt; builder)
  {
      builder.Property(e =&gt; e.FacetId)
          .IsRequired();
  
      builder.Property(e =&gt; e.Operation)
          .IsRequired();
  
      builder.Property(e =&gt; e.Values)
          .IsRequired()
          .HasConversion(
              vals =&gt; CSVUtilities.ToCsvStringList(vals),
              str =&gt; CSVUtilities.FromCsvStringList(str).ToArray());
  }
  
  void IEntityTypeConfiguration&lt;BA.MetaBlockExpressionGroup&gt;.Configure(EntityTypeBuilder&lt;BA.MetaBlockExpressionGroup&gt; builder)
  {
      builder.Property(g =&gt; g.Operator)
          .IsRequired();
  
      builder
          .HasMany(g =&gt; g.Expressions)
          .WithOne();
  }
  </code></pre>
  
  <p>Of note is that there is another type that needs to reference many of the groups (just groups), but once again, no need for the reverse.</p>