首页 > 解决方案 > 一个视图中的父子模型(多个模型)(ASP.NET Core (C#) + MVC + Entity Framework)

问题描述

我正在尝试制作一个在一个视图(ASP.NET Core + MVC + Entity Framework)中具有父子模型(多个模型)的 Web 应用程序。

以下为输入(输入 No.1)。这是“视图/博客/index.cshtml”

视图/博客/index.cshtml 的图像

以下也被输入(输入 No.2)。这是“视图/标签/index.cshtml”

视图/标签/index.cshtml 的图像

以下是我期望的输出(输出 No.1)。
视图/博客/index.cshtml 的图像

我写了以下“视图/博客/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>

并执行,我得到了以下错误。(输出 No.2。真正的意外输出)

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 如下。

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​​ 如下。

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; }
    }
}

模型/UlrTag.cs​​ 如下。

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; }
    }
}

有人对这个亲子模型有帮助吗?先感谢您。


Blogs.Controller.cs 如下。

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 如下。

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);
        }
    }
}

junkongli 和 Gimly,谢谢你的回复。

我已经尝试过 Gimly 的想法。

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

然后 Visual Studio 告诉我“'Blog' 不包括 'Tags' 定义”错误。所以我已将标签更改为标签,我的 Visual Studio 说没问题

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

我在调试模式下运行代码,Web 应用程序返回以下错误。

处理请求时发生未处理的异常。InvalidOperationException:传递到 ViewDataDictionary 的模型项的类型为“System.Collections.Generic.List 1[urlapp12.Models.Blog]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable1[urlapp12.Models.UrlTag]”。Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(对象值)


非常感谢你。

我已将代码“/Views/Blogs/Index.cshml”更改如下,并且可以成功执行。

@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>

标签: c#entity-frameworkmodelasp.net-core-mvcparent-child

解决方案


首先,关于您遇到的异常,错误消息非常明确,@junkangli 在其评论中解释说,您没有将正确的对象返回给视图。View 需要一个IEnumerable<UrlTag>,而您正在向它发送一个IEnumerable<Blog>.

现在,关于问题的核心,您需要在查询中加载标签列表以获取博客列表,因此,在控制器的Index操作中,您应该执行以下操作:

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

然后,在您看来,您应该能够访问您的标签并创建一个 while 循环来显示您的所有标签。


推荐阅读