Search code examples
c#entity-frameworkentity-relationship

Many To Many Relation Insert is not correct


I have many to many relations in my model:

Tag.cs

public class Tag 
{
   // more properites ...

   // user relations with forgin key
   public User CreatedBy { get; set; }
   public Guid CreatedById { get; set; }

   // has many posts
   public ICollection<Post> Posts { get; set; }
}

Here is Tag Canfiguration:

  HasRequired(o => o.CreatedBy)
          .WithMany(user => user.Tags)
          .HasForeignKey(tag => tag.CreatedById)
          .WillCascadeOnDelete(false);

Post.cs

 public class Post
 {
     public ICollocation<Tag> Tag { get; set; }
 }

And Configuration:

 HasMany(post => post.Tags)
          .WithMany(tag => tag.Posts)
          .MapToStoredProcedures();

Here is insert operation code that I insert Post with Multiple tags:

    public async Task InsertAsync(CreatePost postDto)
    {
        using (var transaction = _uow.Database.BeginTransaction())
        {
            // map dto class to Post
            var post = _mapper.Map<Post>(postDto);

            // here split tag names in dto
            var postTgs = postDto.TagNames.Split(',');

            // i get tags that contains postTags names 
            var tags = await _tags.AsNoTracking().Where(x => postTgs.Contains(x.Name)).ToListAsync().ConfigureAwait(false);

            _posts.Add(post);

            await _uow.SaveAllChangesAsync().ConfigureAwait(false);

            // get added post for add tags
            var newPost = await _posts.Include(o => o.Tags).SingleAsync(x => x.Id == post.Id).ConfigureAwait(false);

            foreach (var tag in tags)
            {
                // here add tags
                newPost.Tags.Add(new Tag(){Name = tag.Name,CreatedById = tag.CreatedById});
            }

            await _uow.SaveAllChangesAsync().ConfigureAwait(false);
            transaction.Commit();
        }
    }

Problem is insert post tags that it insert Multiple tag in database. this opration add new tag in Tag table but i want add exist tags to PostTags that Ef Generated it Automaticaly.

  1. how can I solve this?
  2. why this operation insert new tag?

I for solving this problem see other documents and questions but don't get the result.I think that I must create PostTags in my model for solving this.

Note: Tag has a foreign key with user.


Solution

  • why this operation insert new tag?

    Because you are adding Tag instances created with new Tag { ... }. Since they are not attached to the DbContext, EF assumes they are new records (not just new links to the existing records).

    how can I solve this?

    Make sure the Tag instances are properly attached to the DbContext with state indicating an existing record.

    It's not quite clear from your code, but if _posts and _tags are DbSets from the same DbContext used by the _uow, all you need is to remove AsNoTracking when getting the tags that contains postTags names, thus enabling correct tracking them as existing, and then simply assign that list as post tags before adding the post to the context, thus telling EF to add links:

    var post.Tags = await _tags.Where(x => postTgs.Contains(x.Name)).ToListAsync().ConfigureAwait(false);
    
    _posts.Add(post);
    
    await _uow.SaveAllChangesAsync().ConfigureAwait(false);
    
    transaction.Commit();
    

    Note that transaction is redundant in this case because SaveChanges already does that for you.