Many-to-many relationships in mvc and EF
I created a blog site where users can Post
blog, there is a many-to-many relationship between Posts
and in my model and database using the Entity Framework Tags
.
In my database I also have it PostTagMap
, but I don't see this in my EF model. Not sure what I need. PostTagMap
just contains a unique ID for Post and Tag nothing else.
(it should be noted that I created the database from the model)
In my Create Post
view (and controller), I want users to be able to create tags (along with them), making sure they are identified with each other. However, I cannot find a way to do this.
Post Model:
public partial class Post
{
public Post()
{
this.Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string BlogUserEmail { get; set; }
public int CategoryId { get; set; }
public string Title { get; set; }
public string ShortDescription { get; set; }
[AllowHtml]
public string Description { get; set; }
public string Meta { get; set; }
public string UrlSlug { get; set; }
public bool Published { get; set; }
public System.DateTime PostedOn { get; set; }
public Nullable<System.DateTime> Modified { get; set; }
public virtual BlogUser BlogUser { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
<h2>Create</h2>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>Post</legend>
<div class="editor-label">
@Html.HiddenFor(model => model.BlogUserEmail, User.Identity.Name)
</div>
<div class="editor-field">
@Html.ValidationMessageFor(model => model.BlogUserEmail)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.CategoryId, "Category")
</div>
<div class="editor-field">
@Html.DropDownList("CategoryId", String.Empty)
@Html.ValidationMessageFor(model => model.CategoryId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ShortDescription)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ShortDescription)
@Html.ValidationMessageFor(model => model.ShortDescription)
</div>
<div class="editor-label">
@Html.TextAreaFor(model => model.Description)
</div>
<div class="editor-field">
@Html.ValidationMessageFor(model => model.Description)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Edit:
Can't use field EditorFor
for my tag Create
for tags. This is because the view is Create
bound to @Model MyBlogger.Post
.
Tags are only shown as a collection and as such if I say:
@Html.EditorFor(model => m.Tags)
Then it shows up blank in my view when rendered.
I tried foreach
it however it didn't work. For an instance trying to do this in my view Create
:
@foreach (var item in Model.Tags)
{
@Html.EditorFor(model => item.Name)
@Html.EditorFor(model => item.Description)
@Html.EditorFor(model => item.UrlSlug)
}
I get an error: Object reference not set to an instance of an object.
source to share
In relation Many-to-Many
, if you use DataAnnotation
, EF will create a Many-to-Many relationship. in your case
Post class has collection navigation property for Tag
public virtual ICollection<Tag> Tags { get; set; }
And the Tag has a collection navigation property for Publish
public virtual ICollection<Post> Posts { get; set; }
Which will create a Many-to-Many relationship between the mail and the tag.
In your opinion:
There are several ways to add / get tag ID value, one way to link it to the model. Post - try the following, since you already have a link between the two models:
<div class="editor-field">
@Html.EditorFor(model => model.Tags.Id)
@Html.ValidationMessageFor(model => model.Tags.Id)
</div>
model.Tags
returns a collection of Tags if you need an attribute that you need to specify explicitly (e.g. Id, Name ...)
Paste into M-to-M relation:
if (ModelState.IsValid)
{
Tag tag = new tag{id=post.Tags.Id};
db.Tag.Attach(tag);
db.Tag.Add(tag);
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
source to share
Many of the many tables do not appear in the EF model. To add lines to it you have to do something like PostObject1.Tags.Add (TagObject1) or TagObject1.Posts.Add (PostObject1).
Here's how to do it: Insert / update many in many objects. How to do it?
To select rows, you need an EF object and access to a list of another table.
source to share
Linked (mapping) tables may not show up in the EF model, but you can easily add a new tag
post.Tags.add(new Tag(){ Name = ""});
As a good practice, you should create view models instead of using models directly for views,
here is a link to help you with that Link
For tags, you can either do an EditTemplate (recommended) or for a loop
Here is an example of a loop for a loop
@for(var i=0;i<Model.Tags.Count();i++) { @Html.EditorFor(model => model.Tags[i].Name) }
To create a new
Just click the button Add New
and onclick
, just add a new textbox, but set the Id to Tags[i].Name
where I am the tag Count()+1
.
source to share