One to one relationship entities of same type

0

I'm very new in ASP.NetCore and EF Core.

I'm having troubles to figure out how to handle this situation:

I have a list of AvailableStudy, and every study has a list of AvailableStage. But also, a stage can depends on a previous stage (but is not mandatory), and the same happens with studies, a study can depends on a previous study. Here are the model classes:

BusinessObject:

 public class BusinessObject : IBusinessObject
    {
        #region Constructors
        public BusinessObject()
        {
            Id = Guid.NewGuid().ToString();
        }

        public BusinessObject(string name) :  this()
        {
            Name = name;
        }
        #endregion

        #region IBusinessObject implementation
        public string Id { get; set; }
        public string Name { get; set; }

        #endregion
    }

AvailableStudy:

   public class AvailableStudy : BusinessObject
    {
        #region Constructors
        public AvailableStudy() : base()
        {
            Stages = new List<AvailableStage>();
        }

        public AvailableStudy(string name) : base(name)
        {
            Stages = new List<AvailableStage>();
        }
        #endregion


        [ForeignKey("DependsOn")]
        public string DependsOnId { get; set; }

        #region Lazy-Load Properties
        public virtual List<AvailableStage> Stages { get; set; }

        public virtual AvailableStudy DependsOn { get; set; }
        #endregion
    }

AvailableStage:

public class AvailableStage : BusinessObject
    {
        #region Constructors
        public AvailableStage() : base()
        {
        }

        public AvailableStage(string name) : base(name)
        {
        }

        public AvailableStage(string name, AvailableStudy study) : base(name)
        {
            Study = study;
        }
        #endregion


        #region Properties
        [Required]
        public string StudyId { get; set; }

        public double Percentage { get; set; }

        public int Duration { get; set; }

        [ForeignKey("DependsOn")]
        public string DependsOnId { get; set; }
        #endregion

        #region Lazy-Load Properties
        public virtual AvailableStage DependsOn { get; set; }

        [ForeignKey("StudyId")]
        public virtual AvailableStudy Study { get; set; }
        #endregion        
    }

And finally, the OnModelCreating of my context class:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<AvailableStudy>().ToTable("AvailableStudies");
            modelBuilder.Entity<AvailableStudy>().HasMany(i => i.Stages).WithOne(c => c.Study);
           modelBuilder.Entity<AvailableStudy>().HasOne(i => i.DependsOn);

            modelBuilder.Entity<AvailableStage>().ToTable("AvailableStages");
            modelBuilder.Entity<AvailableStage>().HasOne(i => i.Study).WithMany(u => u.Stages);
            modelBuilder.Entity<AvailableStage>().HasOne(i => i.DependsOn);         
        }

I'm parsing a JSON file to generate a list of studies with their stages and dependencies.

A fragment of a JSON file

{
  "Studies": [
    {
      "Name": "Concentrated",
      "DependsOn": "",
      "Stages": [
        {
          "Name": "IsConcentratedDone",
          "Percentage": 25,
          "Duration": 1
        },
        {
          "Name": "WasDescribed",
          "Percentage": 25,
          "Duration": 1
        },
        {
          "Name": "HasPhotos",
          "Percentage": 25,
          "Duration": 1
        },
        {
          "Name": "ReportFinished",
          "Percentage": 25,
          "Duration": 1
        }
      ]
    },
    {
      "Name": "AFT",
      "DependsOn": "Concentrated",
      "Stages": [
        {
          "Name": "IsMountDone",
          "Percentage": 25,
          "Duration": 1
        },
        {
          "Name": "SendedToIrradiation",
          "Percentage": 25,
          "Duration": 1
        },
        {
          "Name": "WasRecepted",
          "Percentage": 25,
          "Duration": 1
        },
        {
          "Name": "WasMeasured",
          "Percentage": 25,
          "Duration": 1
        }
      ]
    }
  ]
}

Then, I add this studies to the proper DBSet in DBContext class and when I try to save changes throws an exception that says:

SqlException: The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "FK_AvailableStages_AvailableStages_DependsOnId". The conflict occurred in database "LateAndesSamples", table "dbo.AvailableStages", column 'Id'. The statement has been terminated.

I'm really don't understand what's happening.

Thansk for your help!

The complete exception result is:

Microsoft.EntityFrameworkCore.DbUpdateException
  HResult=0x80131500
  Message=An error occurred while updating the entries. See the inner exception for details.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(DbContext _, ValueTuple`2 parameters)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IReadOnlyList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
   at LabManager.DataModel.Data.DbSeeder.CreateStudiesWithStages(ApplicationDbContext dbContext) in W:\LabManager\DataModel\src\Data\DbSeeder.cs:line 113
   at LabManager.DataModel.Data.DbSeeder.Seed(ApplicationDbContext dbContext, RoleManager`1 roleManager, UserManager`1 userManager, IHostingEnvironment env) in W:\LabManager\DataModel\src\Data\DbSeeder.cs:line 22
   at LabManagerWebPage.Extensions.ServiceExtensions.ConfigureDBContext(IApplicationBuilder app, IHostingEnvironment env) in W:\LabManager\LabManagerWebPage\Extensions\ServiceExtensions.cs:line 156
   at LabManagerWebPage.Startup.Configure(IApplicationBuilder app, IHostingEnvironment env) in W:\LabManager\LabManagerWebPage\Startup.cs:line 107

Inner Exception 1:
SqlException: The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "FK_AvailableStages_AvailableStages_DependsOnId". The conflict occurred in database "LateAndesSamples", table "dbo.AvailableStages", column 'Id'.
The statement has been terminated.
c#
asp.net-core
ef-core-2.2
asked on Stack Overflow Jul 3, 2019 by joaco • edited Jul 6, 2019 by marc_s

2 Answers

1

Your problem is the base class. It needs to be abstract. Otherwise, it's going to create relational inheritance and the only strategy EF Core supports is table-per-hierarchy, AKA single-table inheritance. That means for a seemingly global base class like BusinessObject, you'll end up with one huge table with all your other types being stuffed into it. In other words, a mess.

By making it abstract, the derived types will inherit the behavior, but they'll still each get their own tables.

answered on Stack Overflow Jul 3, 2019 by Chris Pratt
0

I solved!

I was calling the following methods after parsing JSON file:

//availables is a list of availiables studies generated after parsing json file

dbContext.AvailableStudies.AddRange(availables);
dbContext.SaveChanges();

But when I looked in details, the stages with no dependencies contains empty string on DependsOnId property, instead of null. After replace empty strings with null, it works fine!

answered on Stack Overflow Jul 3, 2019 by joaco

User contributions licensed under CC BY-SA 3.0