I know this has probably been asked hundreds, if not thousands of times. I have been working on this for 2 days and I have looked at several answers and tried following several examples. I have been getting more and more confused.
I have a forum app with a Forums table and a Tags table. Each forum can have many tags. Each tag can be assigned to many forums. Each tag in the tags table is unique.
public class Forum : BaseModel
{
public string? Title { get; set; }
public List<Post> Posts { get; set; } = new List<Post>();
public ICollection<ForumTag> ForumTags { get; set; } = new List<ForumTag>();
}
public class Tag : BaseModel
{
public string? Name { get; set; }
public ICollection<ForumTag> ForumTags { get; set; } = new List<ForumTag>()
}
public class ForumTag
{
public int ForumId { get; set; }
public int TagId { get; set; }
[NotMapped]
public Forum forum { get; set; } = new();
[NotMapped]
public Tag tag { get; set; } = new();
}
modelBuilder.Entity<ForumTag>()
.HasKey(t => new { t.ForumId, t.TagId });
modelBuilder.Entity<ForumTag>()
.HasOne(pt => pt.forum)
.WithMany(p => p.ForumTags)
.HasForeignKey(pt => pt.ForumId);
modelBuilder.Entity<ForumTag>()
.HasOne(pt => pt.tag)
.WithMany(t => t.ForumTags)
.HasForeignKey(pt => pt.TagId);
CreateMap<Forum, ForumViewModel>();
CreateMap<ForumViewModel, Forum>()
.ForMember(q => q.CreatedBy, options => options.Ignore());
//.ForMember(q => q.ForumTags, options => options.Ignore());
CreateMap<Post, PostViewModel>();
CreateMap<PostViewModel, Post>()
.ForMember(x => x.CreatedBy, options => options.Ignore());
CreateMap<Tag, TagViewModel>();
CreateMap<TagViewModel, Tag>()
.ForMember(x => x.Id, options => options.Ignore());
//.ForMember(x => x.ForumTags, options => options.Ignore());
//CreateMap<ForumTagViewModel, ForumTag>()
// .ForMember(q => q.forum, options => options.Ignore())
// .ForMember(q => q.tag, options => options.Ignore());
public async Task<ActionResult<ForumViewModel>> PostForum(ForumViewModel forumViewModel)
{
Forum? forum = null;
try
{
forum = _mapper.Map<Forum>(forumViewModel);
_context.Forums.Add(forum);
await _context.SaveChangesAsync();
List<ForumTag> tags = new();
foreach(TagViewModel tag in forumViewModel.SelecteTags)
{
tags.Add(new()
{
ForumId = forum.Id,
TagId = tag.Id
});
}
_context.ForumTags.AddRange(tags);
_context.SaveChanges();
}
catch (Exception ex)
{
return Problem($"Unable to post new forum.\n\n{ex}");
}
forumViewModel.Id = forum.Id;
return CreatedAtAction("GetForumViewModel", new { id = forumViewModel.Id }, forumViewModel);
}
So currently after
forum = _mapper.Map<Forum>(forumViewModel);
_context.Forums.Add(forum);
await _context.SaveChangesAsync();
The forums table and Tag table are correctly populated, as well as the Post table.
After
_context.ForumTags.AddRange(tags);
_context.SaveChanges();
A new null forum record is created and a new null tag record is created. The ForumTag table has the id's from the null forum and tag record.
What I need to do is insert a new forum record, a new post record, and the ForumTag Id's. I'm not having any issues with the Post records.
Can someone please point me in the right direction?
Well, as happens from time to time, I figured it out after I created a post here. I found this blog Updating many-to-many relationships in EF Core 5 and above
public class Forum : BaseModel
{
public string? Title { get; set; }
public List<Post> Posts { get; set; } = new List<Post>();
public List<Tag>? Tags { get; set; }
}
public class Tag : BaseModel
{
public string? Name { get; set; }
public ICollection<Forum> Forums { get; set; }
}
public class ForumTag
{
public int ForumId { get; set; }
public int TagId { get; set; }
public Forum forum { get; private set; }
public Tag tag { get; private set; }
}
modelBuilder.Entity<Forum>().HasMany(x => x.Tags)
.WithMany(x => x.Forums)
.UsingEntity<ForumTag>(
x => x.HasOne(x => x.tag)
.WithMany().HasForeignKey(x => x.TagId),
x => x.HasOne(x => x.forum)
.WithMany().HasForeignKey(x => x.ForumId));
public async Task<ActionResult<ForumViewModel>> PostForum(ForumViewModel forumViewModel)
{
Forum? forum = null;
try
{
var viewModelTags = forumViewModel.SelecteTags;
List<Tag> tags = new List<Tag>();
foreach (var item in viewModelTags)
{
Tag tag = await _context.Tags.FirstOrDefaultAsync(x => x.Name == item.Name);
tags.Add(tag);
}
forum = _mapper.Map<Forum>(forumViewModel);
forum.Tags = new();
forum.Tags.AddRange(tags);
_context.Add(forum);
_context.SaveChanges();
}
catch (Exception ex)
{
return Problem($"Unable to post new forum.\n\n{ex}");
}
forumViewModel.Id = forum.Id;
return CreatedAtAction("GetForumViewModel", new { id = forumViewModel.Id }, forumViewModel);
}