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.
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.
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!
User contributions licensed under CC BY-SA 3.0