Reputation: 3
I have this closure table that is supposed to make easier to get all replies of a comment, their replies, all replies to the comment ( even distant inheritants), but when I try to insert the parent - it inserts the CHILD as the parent. I don't recall if I had ever encountered such issue.
public class ReplyClosure
{
[Key]
public int Id { get; set; }
public int ParentId { get; set; }
[ForeignKey(nameof(ParentId))]
public PostReply Parent { get; set; } = null!;
public int ChildId { get; set; }
[ForeignKey(nameof(ChildId))]
public PostReply Child { get; set; } = null!;
public int Depth { get; set; }
}
public class PostReply
{
[Key]
public int Id { get; set; }
public string ForumUserId { get; set; } = string.Empty;
[ForeignKey(nameof(ForumUserId))]
public ForumUser ForumUser { get; set; } = null!;
public int PostId { get; set; }
[ForeignKey(nameof(PostId))]
public Post Post { get; set; } = null!;
[Column(TypeName = "Text")]
public string Content { get; set; } = String.Empty;
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public bool IsDeleted { get; set; }
[InverseProperty(nameof(ReplyClosure.Parent))]
public virtual List<ReplyClosure> Parents { get; set; } = new List<ReplyClosure>();
[InverseProperty(nameof(ReplyClosure.Child))]
public virtual List<ReplyClosure> Children { get; set; } = new List<ReplyClosure>();
}
The problematic code is this: I tried to write this in too many ways and none worked and for some reason I insert the child in the place of parent. I don't know why it is happening. It probably has something to do when trying to create a reply at the same time as reply closure. I tried using the other way around - insert the reply and use the navigation property to add the parent. It did not work either.
PostReply postReply = new PostReply
{
CreatedOn = DateTime.Now,
PostId = replyViewModel.PostId,
UserId = GetUserId(),
Content = replyViewModel.Content,
IsDeleted = false
};
var closure = new ReplyClosure
{
ParentId = replyViewModel.ParentId, // this is the correct id, confirmed
Depth = replyViewModel.Depth,
Child = postReply
};
await _context.RepliesClosures.AddAsync(closure);
await _context.SaveChangesAsync();
The context contains the following db relationship description:
builder.Entity<PostReply>()
.HasOne(entry => entry.Post)
.WithMany(entry => entry.PostsReplies)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ReplyClosure>()
.HasOne(entry => entry.Parent)
.WithMany(entry => entry.Children)
.HasForeignKey(entry => entry.ParentId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<ReplyClosure>()
.HasOne(entry => entry.Child)
.WithMany(entry => entry.Parents)
.HasForeignKey(entry => entry.ChildId)
.OnDelete(DeleteBehavior.Restrict);
Ps. I know there is probably a better way to design a comment area in a simulated forum app, but this was the bare minimum I could think of. I know I might be forgetting something, just not sure what it is. Thanks
I tried to use the inverse navigation properties - to no avail, I can't use a raw sql transaction and I also tried chat gpt, all my db relations seem correct. I want to create the two entities at the same time and use saveChanges just once. I tried first inserting the reply, saving and then inserting the closure. It still inserts the child in both places - the parentId and the childId, so it must be the way I define the relations in the dbcontext or something.
Upvotes: 0
Views: 28
Reputation: 34773
This configuration:
[InverseProperty(nameof(ReplyClosure.Parent))]
public virtual List<ReplyClosure> Parents { get; set; } = new List<ReplyClosure>();
Does not match this config:
builder.Entity<ReplyClosure>()
.HasOne(entry => entry.Parent)
.WithMany(entry => entry.Children)
.HasForeignKey(entry => entry.ParentId)
.OnDelete(DeleteBehavior.Restrict);
The second fluent config looks correct, using the attributes instead should be:
[InverseProperty(nameof(ReplyClosure.Child))]
public virtual List<ReplyClosure> Parents { get; set; } = new List<ReplyClosure>();
[InverseProperty(nameof(ReplyClosure.Parent))]
public virtual List<ReplyClosure> Children { get; set; } = new List<ReplyClosure>();
If you are seeing the wrong FK updated I think EF is using the attributes and ignoring the correct fluent config. I'd recommend using one or the other to avoid this possible confusion.
Upvotes: 0