1

How can I insert a model Tag that belongs to a model Post when I have the models setup like this:

Post

public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
    public Post()
    {
       Tags = new List<Tag>();
    }
}

Tag

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
}

This question suggests to create a Post object then add Tags to the Tags collection, I couldn't get it working: Insert/Update Many to Many Entity Framework . How do I do it?

I want to add Tag to Post already in the database, how can I do that with EF. I'm new to EF.

This is what I've tried, if I send this to the API it doesn't insert any records and I can see that the new tag Id = 0 which doesn't exist in the database, but I'd think that'd cause a foreign key constraint error, not sure If I need to do something to auto generate Id for the tag:

{
    Name: "test"
}

API

[ResponseType(typeof(Tag))]
public IHttpActionResult PostTag(Tag tag)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    var post = new Post();
    var tags = new List<Tag>();
    tags.Add(tag);

    post.Tags.Add(tag);
    post.Id = 10;
    db.Entry(post).State = EntityState.Modified;
    db.SaveChanges();

    return CreatedAtRoute("DefaultApi", new { id = tag.Id }, tag);
}
matt
  • 659
  • 2
  • 8
  • 15
  • *How can I insert a Tag with a PostId?* Tag doesn't have a `PostId`, but what you mean is: how can I store connections between Posts and Tags? That's as simple as adding existing tags (i.e. attached as `Unchanged`) to Post.Tags. – Gert Arnold Dec 30 '18 at 16:11
  • Your original question had the correct mapping, you shouldn't have removed it. – Gert Arnold Dec 31 '18 at 11:56

1 Answers1

1

If you said there is Many-To-Many relation which the PostTag is connection table between Tag and Post then your models don't show any many-to-many relation, so from what I have seen there is one-to-many between Post and Tag because of your model definition.

if you want to make many-to-many relation between them you have to something like below:

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
    public Tag()
    {
        Posts = new HashSet<Post>();
    }
} 
public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public virtual ICollection<Tag> Tags { get; set; } 
    public Post()
    {
        Tags = new HashSet<Tag>();
    }
}

and in OnModelCreating make relation by fluent api as below :

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ 
    modelBuilder.Entity<Tag>()
        .HasMany(s => s.Posts)
        .WithMany(c => c.Tags)
        .Map(cs =>
        {
            cs.MapLeftKey("TagId");//TagId
            cs.MapRightKey("PostId");//PostId
            cs.ToTable("PostTag");
        });
}

or vice versa

modelBuilder.Entity<Post>()
    .HasMany(s => s.Tags)
    .WithMany(c => c.Posts)
    .Map(cs =>
        {
            cs.MapLeftKey("PostId");//PostId
            cs.MapRightKey("TagId");//TagId
            cs.ToTable("PostTag");
        });

as you can see and know there should be a table named PostTag in database which have two columns as keys which have a script like :

CREATE TABLE [dbo].[PostTag](
    [TagId] [int] NOT NULL,
    [PostId] [int] NOT NULL,
 CONSTRAINT [PK_PostTag] PRIMARY KEY CLUSTERED 
(
    [TagId] ASC,
    [PostId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] 

ALTER TABLE [dbo].[PostTag]  WITH CHECK ADD  CONSTRAINT [FK_PostTag_Post] FOREIGN KEY([PostId])
REFERENCES [dbo].[Post] ([Id])
GO 
ALTER TABLE [dbo].[PostTag] CHECK CONSTRAINT [FK_PostTag_Post]
GO 
ALTER TABLE [dbo].[PostTag]  WITH CHECK ADD  CONSTRAINT [FK_PostTag_Tag] FOREIGN KEY([TagId])
REFERENCES [dbo].[Tag] ([TagId])
GO 
ALTER TABLE [dbo].[PostTag] CHECK CONSTRAINT [FK_PostTag_Tag]
GO

take a look at here for more info.

UPDATE:

if you want establish zero-to-many relation between Post and Tag then the models should be like below:

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? PostId { get; set; }
}
public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
    public Post()
    {
        Tags = new HashSet<Tag>();
    }
}

and one to many relation with fluent api :

modelBuilder.Entity<Post>()
   .HasMany(o1 => o1.Tags);

as your comment you don't want Tag have navigate property so you should define a property as Nullable in Tag which is feign key, if there is a relation between them you should establish relation at least by navigate property or Nullable property.

The correct answer came from here (with no change): You want to assign a tag to exist post you should find that Post firstly then add a tag to it, if that tag does exist in DB a relation will be made between Tag and found Post if that Tag does not exist then Tag will be inserted to DB,

Take a look at this :

var post = context.Posts.SingleOrDefault(x => x.Id == 4);//That Id would be you specific Post Id
var existTag = context.Tags.SingleOrDefault(x => x.Id == 1); //Exist Tag in DB
post.Tags.Add(existTag);
context.SaveChanges();
//Inserting new tag to DB and assign it to Post also
Tag newTag = new Tag // Does not exist in DataBase
{
    Name = "tag2"
};
post.Tags.Add(newTag);// By this tag2 will be insert into Tag table first and then added to post (insert new record to PostTag)
context.SaveChanges();
Aria
  • 3,724
  • 1
  • 20
  • 51
  • I don't want a navigation property on the Tag model though, I don't want all the posts available for each tag – matt Dec 30 '18 at 10:22
  • You don't want navigate property as Post in Tag model ? you also don't want posts available for each tag ?, so you want `one-to-many` relation from `Post` to `Tag` which mean each post have many tags or what ? – Aria Dec 30 '18 at 11:10
  • What you suggested is every Post for each tag. I want to know how to do the insert for the relation I've setup, there's no problem with setting up the many to many I've checked the database and the modelBuilder has created the PostTag table. My question is similar to this one: https://stackoverflow.com/questions/4253165/insert-update-many-to-many-entity-framework-how-do-i-do-it, I can't get the insert part working – matt Dec 30 '18 at 11:42
  • Also I used this question to setup the m to m relationship without a navigation property on the Tags: https://stackoverflow.com/questions/16718699/map-many-to-many-relationship-without-navigation-property – matt Dec 30 '18 at 11:42
  • I open a new question thats easier to read I can't close this one though – matt Dec 30 '18 at 11:54
  • @matt, what is the insert code, please provide insert code and the error you faced with. – Aria Dec 30 '18 at 12:04
  • There is no need to ask question twice just edit this. – Aria Dec 30 '18 at 14:05