MVC ViewModel 示例

21

我一直在做教程,并试图学习MVC开发的最佳实践。我使用的设计来自于Apress/Adam Freeman出版的《Pro ASP.Net MVC5》。到目前为止,一切都进展顺利......但是当涉及到控制器操作时,我仍然没有完全掌握。是的,我理解控制器的概念,但在POST和GET方法方面仍然很困难。以下是我示例MVC应用程序的流程:

我的app.Domain项目

我在数据库中有一个用户表,并使用Entities/Users.cs引用它。

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

namespace app.Domain.Entities
{
public class Users
{
    [Key]
    public int UserID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public DateTime CreateDate { get; set; }
    public DateTime LastLogin { get; set; }

}
}

接下来,我有一个接口,它位于Abstract/IUsersRepository.cs文件中。

using System;
using System.Collections.Generic;
using app.Domain.Entities;

namespace app.Domain.Abstract
{
public interface IUsersRepository
{
    IEnumerable<Users> Users { get; }
}
}

继续前进,现在我填充我的实体Concrete/EFUsersRepository.cs

using System;
using System.Collections.Generic;
using app.Domain.Entities;
using app.Domain.Abstract;

namespace app.Domain.Concrete
{
public class EFUsersRepository : IUsersRepository
{
    private EFDbContext context = new EFDbContext();

    public IEnumerable<Users> Users
    {
        get { return context.Users; }
    }
}
}

另外,教科书使用了我理解的Ninject,而且所有内容都正确绑定了。除非有人要求,否则我不会发布那段代码。

这是我的app.WebUI解决方案:

教科书引导我创建一个ViewModel。在这里,事情对我来说有点模糊。ViewModel是获取实体的另一个渠道吗?是否应该始终创建ViewModel来选择、更新、插入、删除数据(Models/UsersViewModel.cs),而不是直接引用Models本身?

using System;
using System.Collections.Generic;
using app.Domain.Entities;

namespace app.WebUI.Models
{
public class UsersViewModel
{
    //public string FirstName { get; set; }
    //public string LastName { get; set; }
    //public string Email { get; set; }
    //public string City { get; set; }
    //public string State { get; set; }
    public IEnumerable<Users> Users { get; set; }
}
}

该场景是用户输入电子邮件地址,然后控制器检查数据库是否存在该电子邮件。如果存在,则重定向到关于视图(Controllers/HomeController.cs)。

using System.Linq;
using System.Web.Mvc;
using app.Domain.Abstract;
using app.WebUI.Models;


namespace app.Controllers
{
public class HomeController : Controller
{
    private IUsersRepository repository;

    public HomeController(IUsersRepository usersRepository)
    {
        this.repository = usersRepository;
    }

    [HttpGet]
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index()
    {
        UsersViewModel userViewModel = new UsersViewModel()
        {
            Users = repository.Users
            .Where(p => p.Email == "LearningMVC5@gmail.com")
        };
        return View("About", userViewModel);

    }

    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";
        return View();
    }

    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";
        return View();
    }
}
}

这是我的视图(Home/Index.cshtml):

@model app.WebUI.Models.UsersViewModel

@{
    ViewBag.Title = "Home Page";
    Layout = "~/Views/Shared/_LayoutNoMenu.cshtml";
}


@foreach (var p in Model.Users)
{ 
<div class="container">
@using (Html.BeginForm("About", "Home", FormMethod.Get, new { @class = "begin-form" }))
{
    <h1>Welcome</h1>
    <div class="required-field-block">
    <textarea rows="1" class="form-control" placeholder="Email" id="filter"></textarea>
    </div>
    <button class="btn btn-primary" type="submit">Login</button>
}
</div>
}

有关如何正确使用ViewModel的任何建议吗?


2
视图模型应该代表您页面的状态。例如,如果您的页面上有一个单选按钮,则视图模型将具有单选按钮状态的属性。简单的视图可能真的没有任何需要视图模型,如果它们只是直接显示视图而没有其他东西。 - Daniel Mann
谢谢Daniel。所以,一个空的文本框不需要ViewModel。但是当我想要在视图加载时填充文本框时,应该使用ViewModel? - JoshYates1980
我的意思是从数据库中填充数据。 - JoshYates1980
1
ViewModels通常可以是多个模型的组合。假设您希望用户查看其完整资料,这些资料通常存储在多个表中,因此在查询数据时,我们将其转换为ViewModel,以便能够呈现用户资料视图的完整信息。 - Bilal
1个回答

31

2014年6月,我在学习MVC时问了这个问题。如今,我理解了视图模型的概念。希望这对其他MVC初学者有所帮助:

我的模型代表数据库表:

public partial class County : Entity
{
    public int CountyID { get; set; }
    public string CountyName { get; set; }
    public string UserID { get; set; }
    public DateTime? CreatedDate { get; set; }
    public string ModifiedUserID { get; set; }
    public DateTime? ModifiedDate { get; set; }

    public virtual IList<Property> Properties { get; set; }
    public virtual DistrictOffice DistrictOffice { get; set; }
    public virtual IList<Recipient> Recipients { get; set; }
}

有两个一对多的关系和一个一对一的关系。实体框架和依赖注入。(这不是视图模型解释所必需的。)

首先,我创建了一个视图模型,用于从控制器传递到视图进行临时存储。CountyViewModel.cs

public class CountyViewModel
{
    [HiddenInput]
    public int? CountyId { get; set; }

    [DisplayName("County Name")]
    [StringLength(25)]
    public string CountyName { get; set; }

    [DisplayName("Username")]
    [StringLength(255)]
    public string Username{ get; set; }
}

您可以根据需要在模型中使用不同的名称和数据类型。例如,我的数据库列名为“UserID”,我的模型也是“UserID”,但我的视图模型是“UserName”。您无需传递不会使用的数据到视图中(例如整个模型)。这个示例只需要County模型的三个部分。
在我的控制器中,我声明了我的视图模型:
我需要数据:
var county = _countyService.Get(countyId);

接下来,

CountyViewModel countyViewModel = new CountyViewModel();
countyViewModel.CountyId = county.CountyID;
countyViewModel.CountyName = county.CountyName;
countyViewModel.UserName = county.UserID;

您也可以这样声明:

CountyViewModel countyViewModel = new CountyViewModel
{
    CountyId = county.CountyID,
    CountyName = county.CountyName,
    UserName = county.UserID
};

现在是传递给视图的时候了:
return View(countyViewModel);

在视图中:

@model Project.Web.ViewModels.CountyViewModel

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
 }

 <div>@Model.CountyName</div>
 @Html.HiddenFor(model => model.CountyId)

 <div>
 @Html.TextBoxFor(model => model.CountyName, new { @class = "form-control" })

这是一个使用视图模型传递数据,并使用Entity Framework调用数据库的简单示例:
示例控制器
public class PropertyController : Controller
{
    private readonly ICountyService _countyService;

    public PropertyController(ICountyService countyService)
        : base()
    {
        _countyService = countyService;
    }


    [HttpGet]
    public ActionResult NewProperty()
    {
        using (UnitOfWorkManager.NewUnitOfWork())
        {
            ListAllCountiesViewModel listAllCountyViewModel = new ListAllCountiesViewModel()
            {
                ListAllCounty = _countyService.ListOfCounties().ToList()
            };

            PropertyViewModel viewModel = new PropertyViewModel()
            {
                _listAllCountyViewModel = listAllCountyViewModel,
                _countyViewModel = new CountyViewModel(),
            };
            return View(viewModel);
        }
     }
 }

示例 视图模型

public class CountyViewModel
{
    [HiddenInput]
    public int? CountyId { get; set; }

    [DisplayName("County Name")]
    [StringLength(25)]
    public string CountyName { get; set; }

    [DisplayName("County URL")]
    [StringLength(255)]
    public string URL { get; set; }
}

public class ListAllCountiesViewModel
{
    public string CountyName { get; set; }
    public IEnumerable<County> ListAllCounty { get; set; }
}

public class PropertyViewModel
{
    public ListAllCountiesViewModel _listAllCountyViewModel { get; set; }
    public CountyViewModel _countyViewModel { get; set; }
}

示例服务层

public partial interface ICountyService
{
    County Get(int id);
    County GetByCompanyCountyID(int id);
    IEnumerable<County> ListOfCounties();
    void Delete(County county);
    IEnumerable<State> ListOfStates();
    void Add(County county);
    County SearchByName(string county);
}


public partial class CountyService : ICountyService
{
    private readonly ICountyRepository _countyRepository;

    public CountyService(ICountyRepository countryRepository)
    {
        _countyRepository = countryRepository;
    }

    /// <summary>
    /// Returns a county
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public County Get(int id)
    {
        return _countyRepository.Get(id);
    }

    /// <summary>
    /// Returns a county by County Id
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public County GetByCountyID(int id)
    {
        return _countyRepository.GetByMedicaidCountyID(id);
    }

    /// <summary>
    /// Returns all counties
    /// </summary>
    /// <returns></returns>
    public IEnumerable<County> ListOfCounties()
    {
        return _countyRepository.ListOfCounties();
    }

    /// <summary>
    /// Deletes a county
    /// </summary>
    /// <param name="county"></param>
    public void Delete(County county)
    {
        _countyRepository.Delete(county);
    }

    /// <summary>
    /// Return a static list of all U.S. states
    /// </summary>
    /// <returns></returns>
    public IEnumerable<State> ListOfStates()
    {
        var states = ServiceHelpers.CreateStateList(); 
        return states.ToList();
    }

    /// <summary>
    /// Add a county
    /// </summary>
    /// <param name="county"></param>
    public void Add(County county)
    {
        county.CreatedUserID = System.Web.HttpContext.Current.User.Identity.Name;
        county.CreatedDate = DateTime.Now;
        _countyRepository.Add(county);
    }

    /// <summary>
    /// Return a county by searching it's name
    /// </summary>
    /// <param name="county"></param>
    /// <returns></returns>
    public County SearchByName(string county)
    {
        return _countyRepository.SearchByName(county);
    }
}

示例 存储层

public partial class CountyRepository : ICountyRepository
{
    private readonly Context _context;

    public CountyRepository(IContext context)
    {
        _context = context as Context;
    }

    public County Get(int id)
    {
        return _context.County.FirstOrDefault(x => x.CountyID == id);
    }

    public County GetByCompanyCountyID(int id)
    {
        return _context.County.FirstOrDefault(x => x.CountyID == id);
    }

    public IList<County> ListOfCounties()
    {
        return _context.County.ToList()
            .OrderBy(x => x.CountyName)  
            .ToList();
    }

    public void Delete(County county)
    {
        _context.County.Remove(county);
    }

    public County Add(County county)
    {
        _context.County.Add(county);
        return county;
    }

    public County SearchByName(string county)
    {
        return _context.County.FirstOrDefault(x => x.CountyName == county);
    }
}

Josh,"_countyService" 是从哪里来的?你唯一提到它的地方就是在使用中。 - muybn
1
我更新了我的答案,并提供了一个示例,说明我的_countyService如何获取数据。 - JoshYates1980
那些服务层必要吗?你不能直接调用你的 DbContext 吗? - Sinjai
2
使用EntityFramework,服务层应该很好,只需调用DbContext。对于ADO.Net,我建议使用服务层和存储库层。 - JoshYates1980
@JoshYates1980 我尝试了你的代码,但出现了一个错误,提示“此对象没有无参构造函数定义。” - zxc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接