1

Here are the classes:

public class Parent 
{
     public int Id { get; set; }
     public string Name { get; set; }
     public IEnumerable<Child> Childs { get; set; }
     public IEnumerable<ParentLog> Logs { get; set; }
}

public class ParentLog 
{
     public int Id { get; set; }
     public DateTime On { get; set; }
     public string Note { get; set; }
     public int ParentId { get; set; }
     public string Name { get; set; }
}

public class Child 
{
     public int Id { get; set; }
     public string Name { get; set; }
     public int ParentId { get; set; }
     public IEnumerable<ChildLog> Logs { get; set; }
}

public class ChildLog 
{
     public int Id { get; set; }
     public DateTime On { get; set; }
     public string Note { get; set; }
     public int ChildId { get; set; }
     public string Name { get; set; }
     public int ParentId { get; set; }
}

In DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
     modelBuilder.Entity<Parent>().HasMany(a => a.Childs);
     modelBuilder.Entity<Parent>().HasMany(a => a.Logs);
     modelBuilder.Entity<Child>().HasMany(a => a.Logs);
}

The idea is for log entries for both Parent and Child be saved also, when Parent and Child is saved. This works.

Codes for saving:

DateTime now = DateTime.Now;
foreach (var a in parents)
{
     a.Logs = new List<ParentLog>();
     a.Logs.Add(new ParentLog
     {
          On = now,
          Note = "Created",
          Name = a.Name
     });

     foreach(var b in a.Childs)
     {
         b.Logs = new List<ChildLog>();
         b.Logs.Add(new ChildLog
         {
              On = now,
              Note = "Created",
              Name = b.Name,
              ParentId = b.ParentId //not needed with the Accepted Answer.
          });
     }
}

int recordsAffected = new ParentRepository().SaveParents(parents);
public int SaveParents(List<Parent> items)
{
     foreach(var item in items)
     {
          _db.Parents.Add(item);
     }

     return _db.SaveChanges();
}

The issue I'm encountering is with the ChildLog.ParentId. It doesn't have the value of the expected value, which is coming from Child.ParentId I suppose.

My question, how to resolve it?

Lester S
  • 720
  • 7
  • 21
  • Since `ChildLog` does not have any direct relation with entity `Parent`, just saving a change log from `Child` entity... you must pass the `Child.ParentId` property value to the `ChildLog.ParentId` property. – Luis Aug 08 '20 at 05:28
  • Luis, I tried that too, the value saved for ChildLog.ParentId is still 0 ,while for ChildLog.ChildId, it's the actual Child.Id. – Lester S Aug 08 '20 at 05:37
  • You don't show the saving code you have problems with. – Gert Arnold Aug 08 '20 at 11:31
  • Gert Arnold, I just added the codes for saving. – Lester S Aug 08 '20 at 12:18
  • Doesn't show the most essential part: are all of these objects new? I guess they are, so `ParentId` simply doesn't have its generated value yet. You can only assign them after saving. – Gert Arnold Aug 08 '20 at 12:43
  • Yes. They're all new, even Parents and Childs which were passed from ajax call and already shaped to List parents. Only ChildLog.ParentId is not getting the generated value, while ParentLog.ParentId is getting it. Included the SaveParents method. – Lester S Aug 08 '20 at 12:53
  • @LesterS You just confirmed my point, you are trying to save all entities at once but, since there is not relation between Parent and ChildLog, the `ChildLog.ParentId` is never updated with the generated ID. You either define a relation between the entities to allow EF handle the update or you create/update the `ChildLog.ParentId` in a second transaction. – Luis Aug 08 '20 at 16:29
  • Luis, I'm thinking EF can handle that in a single SaveChanges. In that case I'll do a 2nd SaveChanges since having a Parent.ChildLog does not make sense. Thanks. – Lester S Aug 08 '20 at 19:31

2 Answers2

0

Add a navigation property to the Child model, ChildLog >Child.

Then you'll be able to access the ParentId via the Child.

Further reading - https://learn.microsoft.com/en-us/ef/ef6/fundamentals/relationships

Teebee15
  • 43
  • 1
  • 6
0

It turns out EF can handle it in one SaveChanges(). Though, I needed to define the relationships explicitly.

Here are the classes that have been updated:

public class Child 
{
     public int Id { get; set; }
     public string Name { get; set; }
     public int ParentId { get; set; }
     public IEnumerable<ChildLog> Logs { get; set; }
     //Added
     public Parent Parent { get; set; }
}

public class ChildLog 
{
     public int Id { get; set; }
     public DateTime On { get; set; }
     public string Note { get; set; }
     public int ChildId { get; set; }
     public string Name { get; set; }
     public int ParentId { get; set; }
     //Added
     public Child Child { get; set; }
}

and here's the updated OnModelCreating method in DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
     var parent = modelBuilder.Entity<Parent>();
     parent.HasKey(a => a.Id);
     parent.Property(a => a.ParentId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
     parent.HasMany(a => a.Logs); //ParentLog

     var child = modelBuilder.Entity<Child>();
     child.HasKey(a => new { a.Id, a.ParentId });
     child.Property(a => a.ChildId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
     child.HasRequired(a => a.Parent).WithMany(a => a.Childs).HasForeignKey(a => a.ParentId);

     var childLog = modelBuilder.Entity<ChildLog>();
     childLog.HasKey(a => new { a.Id, a.ChildId, a.ParentId });
     childLog.Property(a => a.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
     childLog.HasRequired(a => a.Child).WithMany(a => a.Logs).HasForeignKey(a => new { a.ChildId, a.ParentId });
}

This helps me with this -> How to fix: The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical?

So much trouble for that little but crucial thing.

Lester S
  • 720
  • 7
  • 21