简体   繁体   中英

Parent-Child model (multiple model) in one view (ASP.NET Core (C#) + MVC + Entity Framework)

I am trying to make a Web App which has Parent-Child model (multiple model) in one view (ASP.NET Core + MVC + Entity Framework).

The following is input (input No.1). This is "Views/Blogs/index.cshtml"

Views / Blogs / index.cshtml的图片

The following is also input (input No.2). This is "Views/Tags/index.cshtml"

Views / Tags / index.cshtml的图像

The following is my expecting output (output No.1).
Views / Blogs / index.cshtml的图片

I have written the following "Views/Blogs/cshtml".

@model IEnumerable<urlapp12.Models.UrlTag>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Blog.Userid)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Blog.Url)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Blog.LastUpdatedAt_UtcDt)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Blog.LastUpdatedAt_LocalDt)
            </th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Blog.Userid)
            </td>
            <td>
                <a href="@Html.DisplayFor(modelItem => item.Blog.Url)">@Html.DisplayFor(modelItem => item.Blog.Title)</a>
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Blog.LastUpdatedAt_UtcDt)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Blog.LastUpdatedAt_LocalDt)
            </td>
            <td>
                @foreach (var childItem in item.Tag)
                {
                    @Html.DisplayFor(modelItem => childItem.tagItem)
                }
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Blog.BlogId">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Blog.BlogId">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Blog.BlogId">Delete</a>
            </td>
        </tr>
        }
    </tbody>
</table>

And executed, I have gotten the following error. (output No.2. The real unexpected output)

An unhandled exception occurred while processing the request.
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List`1[urlapp12.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable`1[urlapp12.Models.UrlTag]'.
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)

Models/Blog.cs is the following.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace urlapp12.Models
{
    public partial class Blog
    {
        public Blog()
        {
            Post = new HashSet<Post>();
        }

        [Key]
        public int BlogId { get; set; }
        public string Userid { get; set; }
        public string Url { get; set; }
        public string Title { get; set; }

        public string CreatedBy { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime CreatedAt_UtcDt { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime CreatedAt_LocalDt { get; set; }

        public string LastUpdatedBy { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime LastUpdatedAt_UtcDt { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime LastUpdatedAt_LocalDt { get; set; }

        public ICollection<Tag> Tag { get; set; }
        public ICollection<Post> Post { get; set; }
        /*
        public List<Tag> Tag { get; set; }
        public List<Post> Post { get; set; }
        */
    }
}

Models/Tag.cs is the following.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace urlapp12.Models
{
    public class Tag
    {
        public int Id { get; set; }
        public int DispOrderNbr { get; set; }
        public string tagItem { get; set; }
        public int BlogId { get; set; }

        public string CreatedBy { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime CreatedAt_UtcDt { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime CreatedAt_LocalDt { get; set; }

        public string LastUpdatedBy { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime LastUpdatedAt_UtcDt { get; set; }
        [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")]
        public DateTime LastUpdatedAt_LocalDt { get; set; }

        [System.ComponentModel.DataAnnotations.Schema.ForeignKey("BlogId")]
        public Blog blog { get; set; }
    }
}

Model/UlrTag.cs is the following.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace urlapp12.Models
{
    public class UrlTag
    {
        public Blog Blog { get; set; }
        public IEnumerable<Tag> Tag { get; set; }
    }
}

Does someone help about this Parent-Child model? Thank you in advance.


Blogs.Controller.cs is the following.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using urlapp12.Models;

namespace urlapp12.Controllers
{
    public class BlogsController : Controller
    {
        private readonly Blogging02Context _context;
        // Stores UserManager
        private readonly UserManager<ApplicationUser> _manager;
        private UserManager<ApplicationUser> _userManager;

        public BlogsController(Blogging02Context context, UserManager<ApplicationUser> userManager)
        {
            _userManager = userManager;
            _context = context;
        }

        // GET: Blogs
        public async Task<IActionResult> Index()
        {
            return View(await _context.Blog.ToListAsync());
        }

        // GET: Blogs/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var blog = await _context.Blog
                .SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }

            return View(blog);
        }

        // GET: Blogs/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Blogs/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]        [ValidateAntiForgeryToken]

        public async Task<IActionResult> Create([Bind("BlogId,Userid,Url,Title")] Blog blog)
        {
            if (ModelState.IsValid)
            {
                /*
                string strCurrentUserId;
                strCurrentUserId = User.Identity.GetUserId(this IIdentity identity);
                var currentUserName = User.Identity.Name ;
                var user = await UserManager<ApplicationUser>.FindByIdAsync(User.Identity.GetUserId());
                var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDbContext()));
                var UserManager = new UserManager(IUserstore<ApplicationUser>);

                var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

                 var user = await GetCurrentUserAsync();
                var userId = user?.Id;
                string mail = user?.Email;

                var userid = GetCurrentUserClaims().userid;

                var userClaims = new UserClaims();
                var claims = _httpContextAccessor.HttpContext.User.Claims.ToList();
                var userid2 = await IGenericRepository < User > userRepository.GetByIdAsync(_currentUserGuid);

         UserManager<ApplicationUser> _userManager;
        SignInManager<ApplicationUser> _signInManager = new SignInManager<ApplicationUser>();
        var info = await _signInManager.GetExternalLoginInfoAsync();
              */
                // Stores UserManager
                //        private readonly UserManager<ApplicationUser> _manager;
                //        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
                //               var result = await _userManager.CreateAsync(user, model.Password);

                var user = await _userManager.GetUserAsync(HttpContext.User);
                var currentLoginUserid = user.Id;
                blog.Userid = user.Id;


                int maxIdInDb = 0;
                int BlogRecCnt = _context.Blog.Count();
                if (_context.Blog.Count() == 0)
                {
                    maxIdInDb = 0;
                }
                else
                {
                    maxIdInDb = _context.Blog.Max(p => p.BlogId);
                }
                int NextId = maxIdInDb + 1;
                blog.BlogId = NextId;


                blog.CreatedAt_LocalDt = DateTime.Now;
                blog.CreatedAt_UtcDt = DateTime.UtcNow;
                blog.CreatedBy = user.Id;

                blog.LastUpdatedAt_LocalDt = DateTime.Now;
                blog.LastUpdatedAt_UtcDt = DateTime.UtcNow;
                blog.LastUpdatedBy = user.Id;

                _context.Add(blog);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(blog);
        }



        // GET: Blogs/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var blog = await _context.Blog.SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }

            var user = await _userManager.GetUserAsync(HttpContext.User);
            var currentLoginUserid = user.Id;
            blog.Userid = user.Id;

            blog.LastUpdatedAt_LocalDt = DateTime.Now;
            blog.LastUpdatedAt_UtcDt = DateTime.UtcNow;
            blog.LastUpdatedBy = user.Id;

            return View(blog);
        }

        // POST: Blogs/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("BlogId,Userid,Url,Title")] Blog blog)
        {
            if (id != blog.BlogId)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(blog);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!BlogExists(blog.BlogId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(blog);
        }

        // GET: Blogs/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var blog = await _context.Blog
                .SingleOrDefaultAsync(m => m.BlogId == id);
            if (blog == null)
            {
                return NotFound();
            }

            return View(blog);
        }

        // POST: Blogs/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var blog = await _context.Blog.SingleOrDefaultAsync(m => m.BlogId == id);
            _context.Blog.Remove(blog);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool BlogExists(int id)
        {
            return _context.Blog.Any(e => e.BlogId == id);
        }
    }
}

TagsController.cs is the following.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using urlapp12.Models;

namespace urlapp12.Controllers
{
    public class TagsController : Controller
    {
        private readonly Blogging02Context _context;

        public TagsController(Blogging02Context context)
        {
            _context = context;
        }

        //int id, [Bind("BlogId,Userid,Url,Title")] Blog blog
        // GET: Tags
//        public async Task<IActionResult> Index()
        public async Task<IActionResult> Index(int id, [Bind("BlogId,Userid,Url,Title")] Blog blog)
        {
            /*
            return View(await _context.Tag.ToListAsync());
            */
            var blogging02Context = _context.Tag.Include(t => t.blog);
            return View(await blogging02Context.ToListAsync());

//            return View (await _context.Tag.ToListAsync());
        }

        // GET: Tags/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var tag = await _context.Tag
                .Include(t => t.blog)
                .SingleOrDefaultAsync(m => m.Id == id);
            if (tag == null)
            {
                return NotFound();
            }

            return View(tag);
        }

        // GET: Tags/Create
        public IActionResult Create()
        {
            ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title");
            return View();
        }

        // POST: Tags/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,DispOrderNbr,tagItem,BlogId")] Tag tag)
        {
            if (ModelState.IsValid)
            {
                _context.Add(tag);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title", tag.BlogId);
            return View(tag);
        }

        // GET: Tags/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var tag = await _context.Tag.SingleOrDefaultAsync(m => m.Id == id);
            if (tag == null)
            {
                return NotFound();
            }
            ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title", tag.BlogId);
            return View(tag);
        }

        // POST: Tags/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("Id,DispOrderNbr,tagItem,BlogId")] Tag tag)
        {
            if (id != tag.Id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(tag);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!TagExists(tag.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            ViewData["BlogId"] = new SelectList(_context.Blog, "BlogId", "Title", tag.BlogId);
            return View(tag);
        }

        // GET: Tags/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var tag = await _context.Tag
                .Include(t => t.blog)
                .SingleOrDefaultAsync(m => m.Id == id);
            if (tag == null)
            {
                return NotFound();
            }

            return View(tag);
        }

        // POST: Tags/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int? id)
        {
            var tag = await _context.Tag.SingleOrDefaultAsync(m => m.Id == id);
            _context.Tag.Remove(tag);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool TagExists(int id)
        {
            return _context.Tag.Any(e => e.Id == id);
        }
    }
}

junkangli and Gimly, thank you for reply.

I have tried the Gimly's idea.

public async Task<IActionResult> Index()
{
    return View(await _context.Blog.Include(b => b.Tags).ToListAsync());
}

Then Visual Studio told me that "'Blog' does not include 'Tags' definition" error. So I have changed Tags to Tag, My Visual Studio says it is OK

public async Task<IActionResult> Index()
{
    return View(await _context.Blog.Include(b => b.Tag).ToListAsync());
}

I run my codes for Debug mode, the web application returned the following error.

An unhandled exception occurred while processing the request. InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List 1[urlapp12.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable 1[urlapp12.Models.UrlTag]'. Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)


Thank you very much.

I have changed the code "/Views/Blogs/Index.cshml" like the following, and I could execute it successfully.

@model IEnumerable<urlapp12.Models.Blog>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Userid)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Url)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.LastUpdatedAt_UtcDt)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.LastUpdatedAt_LocalDt)
            </th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Userid)
            </td>
            <td>
                <a href="@Html.DisplayFor(modelItem => item.Url)">@Html.DisplayFor(modelItem => item.Title)</a>
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.LastUpdatedAt_UtcDt)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.LastUpdatedAt_LocalDt)
            </td>
            <td>
            @foreach (var childItem in item.Tag)
            {
                @Html.DisplayFor(itemItem => childItem.tagItem)
            }
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.BlogId">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.BlogId">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.BlogId">Delete</a>
            </td>
        </tr>
        }
    </tbody>
</table>

First, about the exception you're getting, the error message is pretty explicit, and @junkangli explained it will in its comment, you're not returning the correct object to the View. View expects an IEnumerable<UrlTag> and you're sending it an IEnumerable<Blog> .

Now, about the core of your issue, you'll want to load the list of tags in your query for getting the list of Blogs, so, in your controller's Index action, you should do something like:

public async Task<IActionResult> Index()
{
    return View(await _context.Blog.Include(b => b.Tags).ToListAsync());
}

Then, in your view, you should be able to access your tags and create a while loop to display all your tags.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM