Home>

Hello.
Since this is my first question, I think there is something wrong with it, but thank you.

In Windows10 ASP.NET Core 2.2, Model and View originally created a page with a one-to-one correspondence, but if you create a page that can display multiple Models in one View using ViewModel, it will be the source. I can no longer display a page that refers to only one of the multiple Models.
I would appreciate it if you could tell me why it happened.
The language is C #.

Source code

Below is a code example.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel;
namespace DBAccessSample.Models
{
    public class hogeList
    {
        public int Id {get;set;}
        public string hoge {get;set;}
// I will omit it here, but in reality there are more properties.
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel;
namespace DBAccessSample.Models
{
    public class hogehogeList
    {
        public int Id {get;set;}
        public string hogehoge {get;set;}
‥
// I will omit it here, but in reality there are more properties.
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DBAccessSample.Models
{
    public class HogeViewModel
    {
        public int Id {get;set;} // Need a primary key?
        public IEnumerable<hogeList>hogeLists {get;set;}
        public IEnumerable<hogehogeList>hogeLists {get;set;}
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DBAccessSample.Data;
using DBAccessSample.Models;
namespace DBAccessSample.Controllers
{
    public class HogeViewModelController: Controller
    {
        public IActionResult Index ()
        {
            HogeViewModel myView = new HogeViewModel ();
            myView.hogeLists = GethogeLists ();
            myView.hogehogeLists = GethogehogeLists ();
            return View (myView);
        }
        private List<hogeList>GethogeLists ()
        {
            List<hogeList>hogeLists = new List<hogeList>();
            return hogeLists;
        }
        private List<hogehogeList>GethogehogeLists ()
        {
            List<hogehogeList>hogehogeLists = new List<hogehogeList>();
            return hogehogeLists;
        }
    }}

View and Controller of hogeList were created by Scaffolding.

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 DBAccessSample.Data;
using DBAccessSample.Models;
namespace DBAccessSample.Controllers
{
    public class hogeListsController: Controller
    {
        private readonly MyDbContext _context;
        public hogeListsController (MyDbContext context)
        {
            _context = context;
        }
        // GET: hogeLists
        public async Task<IActionResult>Index ()
        {
            return View (await _context.hogeLists.ToListAsync ());
        }
        // GET: hogeLists/Details/5
        public async Task<IActionResult>Details (int? Id)
        {
            if (id == null)
            {
                return NotFound ();
            }
            var hogeList = await _context.hogeLists
                .FirstOrDefaultAsync (m =>m.Id == id);
            if (hogeList == null)
            {
                return NotFound ();
            }
            return View (hogeList);
        }
        // GET: hogeLists/Create
        public IActionResult Create ()
        {
            return View ();
        }
        // POST: hogeLists/Create
        // To protect from overposting attacks, please enable the specific properties I want to bind to, for
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult>Create ([Bind ("Id, hoge")] hogeList hogeList)
        {
            if (ModelState.IsValid)
            {
                _context.Add (hogeList);
                await _context.SaveChangesAsync ();
                return RedirectToAction (nameof (Index));
            }
            return View (hogeList);
        }
        // GET: hogeLists/Edit/5
        public async Task<IActionResult>Edit (int? Id)
        {
            if (id == null)
            {
                return NotFound ();
            }
            var hogeList = await _context.hogeLists.FindAsync (id);
            if (hogeList == null)
            {
                return NotFound ();
            }
            return View (hogeList);
        }
        // POST: hogeLists/Edit/5
        // To protect from overposting attacks, please enable the specific properties I 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, hoge")] hogeList hogeList){
            if (id! = hogeList.Id)
            {
                return NotFound ();
            }
            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update (hogeList);
                    await _context.SaveChangesAsync ();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (! hogeListExists (hogeList.Id))
                    {
                        return NotFound ();
                    }
                    else else
                    {
                        throw;
                    }
                }
                return RedirectToAction (nameof (Index));
            }
            return View (hogeList);
        }
        // GET: hogeLists/Delete/5
        public async Task<IActionResult>Delete (int? id)
        {
            if (id == null)
            {
                return NotFound ();
            }
            var hogeList = await _context.hogeLists
                .FirstOrDefaultAsync (m =>m.Id == id);
            if (hogeList == null)
            {
                return NotFound ();
            }
            return View (hogeList);
        }
        // POST: hogeLists/Delete/5
        [HttpPost, ActionName ("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult>DeleteConfirmed (int id)
        {
            var hogeList = await _context.hogeLists.FindAsync (id);
            _context.hogeLists.Remove (hogeList);
            await _context.SaveChangesAsync ();
            return RedirectToAction (nameof (Index));
        }
        private bool hogeListExists (int id)
        {
            return _context.hogeLists.Any (e =>e.Id == id);
        }
        /////////////////////////////////////////////////

    }
}
error contents

In this example, the HogeViewModel page is displayed correctly, but an error occurs on the page that displays only the hogeList.
I think that hogeList alone is not directly related to the inheritance source of ViewModel.
SqlException: Invalid column name'FuncMainViewModelId'.
And mentions the Id of the ViewModel.
But if you delete the Id of ViewModel
InvalidOperationException: The entity type'HogeViewModel' requires a primary key to be defined.
I get the error.

Does the ViewModel need a primary key in the first place? (The primary key is not written in the Qiita article of the reference URL. I think that it is not ASP.NET Core ...)

Also, can ASP.NET Core ViewModel automatically create Controllers and Views with Scaffolding?

Thank you.

Reference URL

https://www.tutorialfor.com/go.php?id=74013
http://kuttsun.blogspot.com/2018/03/mvc-viewmodel.html
https://qiita.com/KktkiY/items/f28528916e97310262e0

Supplementary information (FW/tool version, etc.)

Visual Studio 2017 Community
Microsoft.AspNetCore.All 2.2.8
Windows10 Pro 1909

  • Answer # 1

    After defining the HogeViewModelController and HogeViewModel mentioned in the question, as well as the View corresponding to the Index action method, I understand that the originally working hogeListsController will not work and a SqlException or InvalidOperationException will be thrown. (If your understanding is different, please write in the comment section of the answer to that effect)

    As I wrote in the comment section of the question, neither the HogeViewModelController nor the HogeViewModel mentioned in the question has the code that throws a SQL Server-related exception called SqlException.

    InvalidOperationException is also an error message "requires a primary key", so it seems to be an exception related to SQL Server, and as far as the code in the question is seen, HogeViewModelController and HogeViewModel have nothing to do with each other.

    However, if hogeListsController gets stuck and throws a SqlException or InvalidOperationException, it's only possible that there's something not mentioned in the question.

    You may have modified the Entity Framework related code that the original hogeListsController is related to, or the code in question has code that actually affects the existing Entity Framework or SQL Server in the HogeViewModelController or HogeViewModel.

    So why is the question "In ASP.NET Core MVC ViewModel, I can't see a View that references only the original Model" and the solution is to answer the above question and provide more information. I can't answer if I don't get it. (I wrote my imagination towards the end of this answer, May be different)

    So, I'll just answer the following questions.

    Create a page that can display multiple Models in one View
    Does the ViewModel need a primary key in the first place?
    Is it possible to automatically create a Controller or View with Scaffolding for the ViewModel of ASP.NET Core?

    Once you know the above, you may be able to solve the problem of throwing a SqlException or InvalidOperationException by self-help effort.

    Create a page that can display multiple Models in one View

    Imagine that you have two tables in SQL Server, hogeList and hogehogeList, and you want to get the data from them and pass them to a view for display.

    In fact, the GethogeLists and GethogehogeLists methods use Entity Framework such as _context.hogeLists.ToList () and _context.hogehogeList.ToList () to turn SQL Server records into IEnumerableand IEnumerableobjects. I imagine you're getting it.

    Then, the controller stores them in a model called HogeViewModel and passes them to the view for display. If so, basically the code of the question is correct.

    That alone will not throw a SqlException or InvalidOperationException.

    Does the ViewModel need a primary key in the first place?

    You do not need. Except for using it for something in the view. The presence or absence of it should have nothing to do with the error. I can't think of any impact from it except for something that isn't mentioned in the question.

    You might think that ViewModel is something special, but it's basically an M or model of MVC. See the article below.

    ASP.NET MVC Model
    http://surferonwww.info/BlogEngine/post/2020/05/29/model-in-aspnet-mvc.aspx

    What is used to pass data to a view is sometimes called a view model. The story of the primary key does not appear there.

    Is it possible to automatically create a Controller or View with Scaffolding for the ViewModel of ASP.NET Core?

    I suspect that it is the current situation that I did it and failed..

    Isn't Visual Studio creating public DbSetHogeViewModel {get;set;} in the context class? If so, I think that is the cause of the problem.

    In the case of HogeViewModel like this time, basically, it is not possible to automatically generate a controller/view using the scaffolding function, so create an empty controller/view and write the code to implement it by yourself. please.