EF Core: Circular entity reference

asp.net-core c# entity-framework-core

Question

I would say that it takes a lot of time to get to know ASP.NET Core to understand how to achieve things then previous versions with webforms, but I understand that ASP.NET Core is bigger and you are able to build more complex solutions.

I'm quite new to ASP.NET Core and I'm trying to understand EF Core and related data. I'm using https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro to learn the basics and create my first ASP.NET Core application.

I have a Entity "Standard" that can have multiple Forms (Form entity). The entities share a couple of same properties so I've made them both inherit from a master class called MasterDocument. Previously called Document.

Standard:

namespace Skjemabasen.Models.Document
{
    public class Standard : MasterDocument
    {
        [Display(Name = "Kategori")]
        public virtual Category Category { get; set; }
        [Display(Name = "Dokumenter")]
        public ICollection<Form> Forms { get; set; }
    }
}

Form:

public class Form : MasterDocument
{
    public Format Format { get; set; }
    public virtual Member Assignee { get; set; }
    public String Status { get; set; }
    [ForeignKey("Standard")]
    public int StandardId { get; set; }
    public Standard Standard { get; set; }
    public ICollection<Customer.Subscription> Subscribers { get; set; }
}

MasterDocument:

namespace Skjemabasen.Models.Document
{
    public class MasterDocument : IDocument
    {       
        public int ID { get; set; }
        [Required]
        [Display(Name = "EStandard")]
        [StringLength(50)]
        public string EStandard { get; set; }
        [Required]
        [Column("Betegnelse")]
        [Display(Name = "Betegnelse")]
        [StringLength(60)]
        public string Betegnelse { get; set; }
        [Display(Name = "Kommentar")]
        public string Comment { get; set; }
    }
}

I understand that this can cause circular request or circular deletion so I inserted a DeleteBehavior.Restrict on Standard:

        modelBuilder.Entity<Standard>()
            .HasOne(d => d.Forms)
            .WithMany()
            .OnDelete(DeleteBehavior.Restrict);

My complete context class:

namespace Skjemabasen.Data
{
    public class SkjemabasenContext : DbContext
    {
        public SkjemabasenContext(DbContextOptions<SkjemabasenContext> options) :base(options)
        {

        }

        public DbSet<Member> Members { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Standard> Standards { get; set; }
        public DbSet<Form> Forms { get; set; }
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Revision> Revisions { get; set; }
        public DbSet<Subscription> Subscriptions { get; set; }
        public DbSet<MasterDocument> Documents { get; set; }

        public IQueryable<Customer> CurrentCustomers
        {
            get { return Customers.Where(c => c.Inactive == false); }
        }

        public IQueryable<Customer> InActiveCustomers
        {
            get { return Customers.Where(c => c.Inactive == true); }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {

            modelBuilder.Entity<Member>().ToTable("Member");
            modelBuilder.Entity<Category>().ToTable("Category");
            modelBuilder.Entity<Standard>().ToTable("Standard");
            modelBuilder.Entity<Form>().ToTable("Form");
            modelBuilder.Entity<Customer>().ToTable("Customer");
            modelBuilder.Entity<Revision>().ToTable("Revision");
            modelBuilder.Entity<Subscription>().ToTable("Subscription");
            modelBuilder.Entity<MasterDocument>().ToTable("Document");


            modelBuilder.Entity<Standard>()
                .HasOne(d => d.Forms)
                .WithMany()
                .OnDelete(DeleteBehavior.Restrict);
        }
    }
}

When I try to run the application I get the error:

System.ArgumentException: 'The entity type 'System.Collections.Generic.ICollection`1[Skjemabasen.Models.Document.Form]' provided for the argument 'clrType' must be a reference type.' Because all Forms must have a parent Standard and both 'Standard' and 'Form' inherits from MasterDocument, I understand that ASP.NET Core warns about circular deletion, but I'm not sure how to achieve this. The error says something about ICollection of 'Forms' not being a reference type. Is something missing in 'Standard' related to the relation between and 'Form'.

Based on https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro I can't figure out what I'm missing here.

1
0
7/18/2018 10:26:16 AM

Popular Answer

I'm assuming you don't actually want to have polymorphic entities by inheriting from MasterDocument. So, from what I see, you want Form and Standard to share the same properties of MasterDocument while MasterDocument being itself an Entity. If that's the case, just abstract away those properties to a base class:

public abstract class MasterBaseDocument
{       
    public int ID { get; set; }
    [Required]
    [Display(Name = "EStandard")]
    [StringLength(50)]
    public string EStandard { get; set; }
    [Required]
    [Column("Betegnelse")]
    [Display(Name = "Betegnelse")]
    [StringLength(60)]
    public string Betegnelse { get; set; }
    [Display(Name = "Kommentar")]
    public string Comment { get; set; }
}

public class Form : MasterBaseDocument
{
    ...
}
public class Standard : MasterBaseDocument
{
    ...
}

public class MasterDocument : MasterBaseDocument
{
    // right now, empty here...
}

That should fix it.

Another approach to your model would be to have a MasterDocument FK on Forms and Standard. That way you don't get the duplicates fields on the tables.

Further improving: Also, keep in mind that you can achieve all those configurations you have using attributes with FluentAPI. This way your classes are keep and decouple from EF stuff. That just adds noise and makes it very hard to read. Should be examples on Fluent API on EF docs as well.

0
6/18/2017 1:04:13 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow