【问题标题】:Bind view form properties through n-tier layers通过 n 层层绑定视图表单属性
【发布时间】:2018-07-07 22:13:51
【问题描述】:

在学习了一点 ASP.NET Core MVC 之后,我构建了一个简单的基于 CRUD 的 Web 应用程序。虽然很简单,但我现在正在尝试学习如何通过将业务逻辑和控制器外部的数据访问移动到它们自己的层来制作更小的控制器。问题是控制器中的模型绑定不容易传递给其他层。

在将所有业务和数据访问逻辑移到控制器之外之前,实体的创建工作已经完成。还有一个 GetAll() 方法(下面未显示),它使用单独的层来获取和显示存储在数据库中的所有实体。但是在尝试创建实体时(见下图),出现以下错误:
InvalidOperationException: Could not create an instance of type 'MyProject.Models.Entity'. Model bound complex types must not be abstract or value types and must have a parameterless constructor.

如何将由 View 创建的模型绑定对象传递到 Web 应用程序的 n 层?

Entity.cs

namespace MyProject.Models
{
    public enum EntityType { Type1, Type2 }
    public class Entity
    {
        private readonly ApplicationDbContext context;

        public Entity(ApplicationDbContext _context)
        {
            context = _context;
        }

        public int EntityId { get; set; }
        public string Name { get; set; }
        public DateTime Date1 { get; set; }
        public EntityType Type { get; set; }
    }
}

CreateEntityModel.cs

namespace MyProject.Models
{
    public class CreateEntityModel
    {
        public string Name { get; set; }
        public string Date1 { get; set; }
        public int Type { get; set; }
    }
}

创建.cshtml

@model CreateEntityModel

<h1>Create Entity</h1>
<form asp-controller="entity" asp-action="createentity" method="post">
    <div class="form-group">
        <label asp-for="Name"></label>
        <input asp-for="Name" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Date1"></label>
        <input asp-for="Date1" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Type"></label>
        <select asp-for="Type">
            <option selected>Choose entity type</option>
            <option value="0">Type1</option>
            <option value="1">Type2</option>
        </select>
    </div>
    <button type="submit">Create</button>
</form>

EntityController.cs

namespace MyProject.Controllers
{
    public class EntityController : Controller
    {
        private UserManager<ApplicationUser> userManager;
        private readonly ApplicationDbContext context;
        private EntityService entityService;

        public EntityController(UserManager<ApplicationUser> _userManager,
                ApplicationDbContext _context)
        {
            userManager = _userManager;
            context = _context;
            entityService= new EntityService (_userManager, _context);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> CreateEntity([Bind("Name, Date1, Type")] Entity entity)
        {
            if (ModelState.IsValid)
            {
                await entityService.Create(entity);
                return RedirectToAction(nameof(Index));
            }

            return View("~/Views/MyView");
        }
    }
}

EntityService.cs

namespace MyProject.Business
{
    public class EntityService
    {
        private UserManager<ApplicationUser> userManager;
        private readonly ApplicationDbContext context;
        private EntityRepository entityRepository;

        public EntityService(
            UserManager<ApplicationUser> _userManager,
            ApplicationDbContext _context)
        {
            userManager = _userManager;
            context = _context;
            entityRepository = new EntityRepository(_context);
        }

        public async Task Create(Entity entity)
        {
            await entityRepository.Create(entity);
        }
    }
}

EntityRepository.cs

namespace MyProject.Data
{
    public class EntityRepository
    {
        private readonly ApplicationDbContext context;

        public EntityRepository(ApplicationDbContext _context)
        {
            context = _context;
        }

        public async Task Create(Entity entity)
        {
            context.Add(entity);
            await context.SaveChangesAsync();
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

    您的代码有两个问题。让我们从您发布的错误的实体开始。以下代码没有任何意义:

    public Entity(ApplicationDbContext _context)
    {
        context = _context;
    }
    

    为什么实体会知道它所属的上下文?这是反转依赖链。您可以删除该构造函数和字段:

    public class Entity
    {
        public int EntityId { get; set; }
        public string Name { get; set; }
        public DateTime Date1 { get; set; }
        public EntityType Type { get; set; }
    }
    

    现在可以创建实体后您将面临的第二个问题是,您的 View 使用 CreateEntityModel,但您的 Action 需要 Entity。鉴于模型是相同的,这不是一个实际问题,但您的操作不应与实体一起使用,而是与您创建的 ViewModel 一起使用,因此:

    public async Task<IActionResult> CreateEntity([Bind("Name, Date1, Type")] CreateEntityModel entity)
    

    此外,您应该始终牢记,Web 应用程序项目是您的业务逻辑的客户端。在这里,有些人声称服务层(您的EntityService)应该接收 ViewModel 并对其进行转换,而另一些人认为它应该接收最终的实体,由您决定遵循哪个版本。

    如果您希望服务接收 ViewModel 并对其进行转换:

    public class EntityService
    {
        ...
        public async Task Create(CreateEntityModel model)
        {
            var entity = new Entity
            {
                X = model.X...
            };
            await entityRepository.Create(entity);
        }
    }
    

    如果您希望您的服务直接接收最终实体:

    public async Task<IActionResult> CreateEntity([Bind("Name, Date1, Type")] CreateEntityModel model)
    {
        if (ModelState.IsValid)
        {
            var entity = new Entity 
            {
                X = model.X...
            };
    
            await entityService.Create(entity);
            return RedirectToAction(nameof(Index));
        }
    
        return View("~/Views/MyView");
    }
    

    注意:当你看到像entityService= new EntityService (_userManager, _context); 这样的代码时,你没有正确地进行依赖注入。 official docs for ASP.NET Core 很好地介绍了依赖注入以及如何在 ASP.NET Core 应用程序中使用它。
    另一个注意事项:除非你有充分的理由,我怀疑,我会建议你忘记 Repository 层,直接使用 Entity Framework Core。您可能想阅读this answer of mine,了解有关 ASP.NET Core 中的工作单元和存储库模式的一些观点。

    【讨论】:

    • @crayden 我已经更新了我的答案以显示我提到的两个选项
    猜你喜欢
    • 2013-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多