【问题标题】:Model bound complex types must not be abstract or value types and must have a parameterless constructor模型绑定的复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数
【发布时间】:2019-01-17 13:33:04
【问题描述】:

我有以下问题,我创建了一个应用程序来将游戏类别和游戏本身添加到数据库中。我创建了一个关系,不幸的是,当我添加到数据库时出现错误。

模型绑定的复杂类型不能是抽象类型或值类型,并且必须 有一个无参数的构造函数。

游戏类别模型

using System.Collections.Generic;

namespace relationship.Models
{
    public class GameCategory
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public ICollection<Game> Game { get; set; }
    }
}

游戏模型

namespace relationship.Models
{
    public class Game
    {
        public int GameId { get; set; }
        public string Name { get; set; }

        public GameCategory Category { get; set; }
        public int CategoryId { get; set; }
    }
}

视图模型

using relationship.Models;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace relationship.ViewModels
{
    public class AddGameViewModel
    {     
        [Required]
        public string GameName { get; set; }
        public int CategoryID { get; set; }

        public List<SelectListItem> Categories { get; set; }

        public AddGameViewModel(IEnumerable<GameCategory> categories)
        {
            Categories = new List<SelectListItem>();
            foreach (var catData in categories)
            {
                Categories.Add(new SelectListItem { Text = catData.Name.ToString(), Value = catData.Id.ToString() });
            }
            return;
        }
    }
}

游戏库

using System.Collections.Generic;
using System.Linq;

namespace relationship.Models
{
    public class GameRepository : IGameRepository
    {
        private readonly AppDbContext appDbContext;
        public GameRepository(AppDbContext dbContext)
        {
            appDbContext = dbContext;
        }

        public void AddGame(Game game)
        {
            appDbContext.Games.Add(game);
            appDbContext.SaveChanges();
        }

        public IEnumerable<Game> Games()
        {
            return appDbContext.Games.ToList();
        }
    }
}

最后是游戏控制器

using Microsoft.AspNetCore.Mvc;
using relationship.Models;
using relationship.ViewModels;

namespace relationship.Controllers
{
    public class GameController : Controller
    {
        private readonly IGameRepository gameRepository;
        private readonly ICategoryRepository categoryRepository;

        public GameController(IGameRepository gameRepo, ICategoryRepository catRepo)
        {
            gameRepository = gameRepo;
            categoryRepository = catRepo;
        }

        public IActionResult Index()
        {
            return View();
        }

        [HttpGet]
        public IActionResult Add()
        {
            var addGameViewModel = new AddGameViewModel(categoryRepository.GameCategory());
            return View(addGameViewModel);
        }

        [HttpPost]
        public IActionResult Add(AddGameViewModel addGameViewModel)
        {
            if (ModelState.IsValid)
            {
                GameCategory gameCategory = categoryRepository.GetDetails(addGameViewModel.CategoryID);

                if(gameCategory == null)
                {
                    return NotFound();
                }

                Game game = new Game
                {
                    Name = addGameViewModel.GameName,
                    Category = gameCategory
                };

                gameRepository.AddGame(game);
                return RedirectToAction("Index");
            }
            return View(addGameViewModel);
        }
    }
}

我不知道出了什么问题。

我的错误屏幕

【问题讨论】:

  • 消息很清楚:AddGameViewModel 没有无参数构造函数(它需要IEnumerable&lt;GameCategory&gt; categories 参数)。如果您不熟悉 ASP.NET Core 中的模型绑定,docs 应该会有所帮助。

标签: c# asp.net asp.net-core .net-core


【解决方案1】:

无法创建relationship.ViewModels.AddGameViewModel 的实例。模型绑定的复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数。

让我们试着分解这个错误。

无法创建 relationship.ViewModels.AddGameViewModel 的实例。

不言自明:模型绑定组件正在尝试创建您的类型的实例,但失败了。

模型绑定的复杂类型

“模型绑定”是指它们被 ASP.NET 管道绑定。 “复杂类型”基本上是任何不是“基本”的类型,例如stringint。您的模型类是复杂类型。

不能是抽象的

模型绑定系统希望能够创建类的实例,因此它不能是抽象的;它必须是具体的。您展示的所有类型都是具体的,所以这不是问题。

或值类型

您不能将struct 类型与模型绑定一起使用;这只是它的局限之一。幸运的是,您的类型都是类,因此您可以忽略这一点。

并且必须有一个无参数的构造函数。

ASP.NET 不知道如何为模型构造函数提供参数。它只能相当于new T(),所以你所有的模型类型都必须定义一个零参数的构造函数。这就是您看到错误的原因;你的AddGameViewModel 类只定义了这个构造函数:

public AddGameViewModel(IEnumerable<GameCategory> categories)

C# 语言的一个特性是,当您不手动指定构造函数时,它会为您添加一个默认构造函数。在代码中定义构造函数时,不会添加此默认构造函数。

在所有其他模型中,您没有定义任何构造函数,因此编译器会为您添加默认构造函数。对于AddGameViewModel,你已经添加了一个构造函数,所以要解决这个问题,你还必须添加默认构造函数:

public AddGameViewModel()
{
}

【讨论】:

  • 我只是添加了一个空的构造函数,因为我记得你可以定义几个: D 确实没有出现错误,但没有保存,问题是表单字段没有设置名称。非常感谢您的帮助:)
  • @KirkLarkin 是的。同样的问题适用于两者,但我可以从堆栈中看到它是 MVC。我会修正这个答案。
  • 添加默认构造函数时,我收到以下错误:在类型''中找到了接受所有给定参数类型的多个构造函数。应该只有一个适用的构造函数。
  • ASP.NET binder 确实知道如何为模型构造函数提供参数。如果这以前在 net framework 中有效,而在 net core 中不再有效,请添加 [FromBody] 属性。
  • 非常详细的答案!谢谢你,先生。清晰的理解。
【解决方案2】:

您需要将 [FromBody] 添加到参数中,以便 asp.net 核心知道如何绑定模型。

[HttpPost]
public IActionResult Add([FromBody] AddGameViewModel addGameViewModel)

【讨论】:

  • 我可以确认,只需添加 [FromBody] 即可解决我的问题。
  • [FromBody]一些 情况下修复它的原因是因为 api 配置为SuppressInferBindingSourcesForParameters。请查看this MSDN link
  • 小心 [FormBody] 与 [FromBody] 哈哈
【解决方案3】:

在撰写本文时,我在一个 Asp.NET Core 2.2 控制器中遇到了这个问题,其中类型被注入到其中一种方法上。将类型移动到 Controller 的构造函数可以解决它。由于这不是真的可以接受,我们最终将有问题的类从控制器中重构到已经广泛使用单例的处理层中。在这一点上再添加一个解决了我们的问题。

请注意,这是内置于 Asp.Net Core 的 OOB IoC 容器。其他 IoC 提供程序可能能够更好地处理方法上的注入属性。

Lamar 可能是一个替代方案。

使用模型绑定器也可能有效,因为绑定器可能会使用单例和/或更干净地支持构造函数注入。

【讨论】:

    【解决方案4】:

    在我的例子中,我天真地绑定了一个复杂对象(一个没有无参数构造函数的复杂对象):

    编辑.cshtml.cs:

    namespace MyNamespace.Pages.CSDA
    {
        public class EditModel : PageModel
        {
            ...
            [BindProperty]
            public MyComplexClass WorkflowItem { get; set; }
            ...
    

    单击“保存”时出现此运行时错误:

    System.InvalidOperationException:无法创建“MyNamespace.MyComplexClass”类型的实例。

    模型绑定的复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数。

    或者,将“WorkflowItem”属性设置为 'MyNamespace.EditModel' 构造函数中的非空值。在 Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(ModelBindingContext 绑定上下文)在 Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.BindModelCoreAsync(ModelBindingContext bindingContext, Int32 propertyData)

    我需要这个对象(它有我想向用户显示的信息),但我不需要需要“更新”它(至少在这个编辑菜单)。

    解决方案:

    只需删除 [BindProperty] 即可消除错误。

    【讨论】:

    • 我认为更好的方法不是删除属性,而是不传递模型,因为它已经通过了,而是将你需要的任何东西添加到模型中,然后使用模型.属性...
    【解决方案5】:

    我遇到了同样的错误。构造函数是internal,我把它返回为public,模型正常传递。

    【讨论】:

      【解决方案6】:

      在我的 Controller 类的顶部添加 [ApiController] 为我修复了它:

      [Route("api/[controller]")]
      [ApiController]
      public class ProductController : Controller
      {
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-15
        • 2019-09-19
        • 2019-09-17
        • 2014-05-18
        • 1970-01-01
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多