Reputation: 749
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);
}
Upvotes: 1
Views: 306
Reputation: 3844
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();
Upvotes: 1