【问题标题】:"DRY up" MVC models when using SelectListItem使用 SelectListItem 时“干燥”MVC 模型
【发布时间】:2015-02-18 11:07:12
【问题描述】:

我刚刚开始使用 MVC,我可以在网上找到大多数 DRY 原则的示例。虽然我没有全部使用,因为我发现有些会使代码更难阅读。

我找到了一个我找不到的例子,但我觉得必须有一种方法可以做到这一点。

基本上,当我在控制器中填充模型选择列表类型对象时,这意味着我必须重用代码。我知道我可以把它放在一个方法中,但我想知道是否有办法把它放在模型中,以便在使用模型/视图模型时调用这个动作来填充选择列表内容。

我在下面放了一个我的代码示例。

型号

using System;
using System.Data.Entity;

namespace MyWebsite.Models
{
    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    namespace CustomerWebsites.Models
    {
        public class CustomerWebsites
        {
            public int Id { get; set; }
            public Guid UserGuid { get; set; }
            public string WebsiteAddress { get; set; }
            public string WebsiteType { get; set; }
            public DateTime ReleaseDate { get; set; }
            public string Description { get; set; }
            public decimal Budget { get; set; }
            public DateTime CreationDate { get; set; }
            public string DevelopmentStatus { get; set; }
            public int CompletedPercentage { get; set; }
            public bool Completed { get; set; }
            public decimal TotalCost { get; set; }
            public decimal TotalPaid { get; set; }

        }

        public class CustomerWebsitesDBContext : DbContext
        {
            public CustomerWebsitesDBContext()
            : base("DefaultConnection")
        {
        }

            public static CustomerWebsitesDBContext Create()
            {
                return new CustomerWebsitesDBContext();
            }
            public DbSet<CustomerWebsites> CustomerWebsites { get; set; }
        }
    }
}

ViewModel

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

namespace MyWebsite.ViewModels
{
    public class CreateCustomerWebsites
    {
        public int Id { get; set; }
        public Guid UserGuid { get; set; }

        [Required]
        public string WebsiteAddress { get; set; }
        public string WebsiteType { get; set; }
        public DateTime ReleaseDate { get; set; }
        public string Description { get; set; }
        public decimal Budget { get; set; }
        public DateTime CreationDate { get; set; }
        public string DevelopmentStatus { get; set; }
        public int CompletedPercentage { get; set; }
        public bool Completed { get; set; }
        public decimal TotalCost { get; set; }
        public decimal TotalPaid { get; set; }
        public IEnumerable<SelectListItem> AllUsers { get; set; }

    }

}

控制器

// GET: CustomerWebsites/Create

        public ActionResult Create()
        {
            var db = new ApplicationDbContext();
            var users = db.Users.ToArray();

            var allUsers = users.Select(x => new SelectListItem
            {
                Value = x.Id,
                Text = x.Email
            });
            var model = new CreateCustomerWebsites
            {
                AllUsers = allUsers
            };

           return View(model);
        }

        // POST: CustomerWebsites/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 ActionResult Create(CreateCustomerWebsites model)
        {
            if (ModelState.IsValid)
            {
                var userGuid = new Guid(User.Identity.GetUserId());
                var developmentStatus = "Pending MyWebsite Review";

                if (User.IsInRole("Administrator"))
                {
                    userGuid = model.UserGuid;
                    developmentStatus = model.DevelopmentStatus;
                } 


                db.CustomerWebsites.Add(new CustomerWebsites
                {
                    UserGuid = userGuid,
                    WebsiteAddress = model.WebsiteAddress,
                    CreationDate = DateTime.Now,
                    ReleaseDate = model.ReleaseDate,
                    Budget = model.Budget ,
                    Description = model.Description,
                    DevelopmentStatus = developmentStatus,
                    CompletedPercentage = model.CompletedPercentage,
                    Completed = model.Completed,
                    TotalCost = model.TotalCost,
                    TotalPaid = model.TotalPaid
                });
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            var dbUsers = new ApplicationDbContext();
            var users = dbUsers.Users.ToArray();

            var allUsers = users.Select(x => new SelectListItem
            {
                Value = x.Id,
                Text = x.Email
            });
            model = new CreateCustomerWebsites
            {
                AllUsers = allUsers
            };
            return View(model);
        }

查看

<h2>Create</h2>

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>CustomerWebsites</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @if (User.IsInRole("Administrator"))
        {
        <div class="form-group">
            @Html.LabelFor(model => model.UserGuid, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(model => model.UserGuid, Model.AllUsers, "-- Select a user --")
                @Html.ValidationMessageFor(model => model.UserGuid, "", new { @class = "text-danger" })
            </div>
        </div>
       }

        <div class="form-group">
            @Html.LabelFor(model => model.WebsiteAddress, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.WebsiteAddress, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.WebsiteAddress, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.WebsiteType, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.WebsiteType, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.WebsiteType, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.ReleaseDate, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ReleaseDate, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ReleaseDate, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextAreaFor(model => model.Description, new { rows = "10", @class = "form-control"  })
                @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
            </div>
        </div>

        @if (User.IsInRole("Administrator"))
        {
            <div class="form-group">
                @Html.LabelFor(model => model.DevelopmentStatus, htmlAttributes: new {@class = "control-label col-md-2"})
                <div class="col-md-10">
                    @Html.EditorFor(model => model.DevelopmentStatus, new {htmlAttributes = new {@class = "form-control"}})
                    @Html.ValidationMessageFor(model => model.DevelopmentStatus, "", new {@class = "text-danger"})
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.CompletedPercentage, htmlAttributes: new {@class = "control-label col-md-2"})
                <div class="col-md-10">
                    @Html.EditorFor(model => model.CompletedPercentage, new {htmlAttributes = new {@class = "form-control"}})
                    @Html.ValidationMessageFor(model => model.CompletedPercentage, "", new {@class = "text-danger"})
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.Completed, htmlAttributes: new {@class = "control-label col-md-2"})
                <div class="col-md-10">
                    <div class="checkbox">
                        @Html.EditorFor(model => model.Completed)
                        @Html.ValidationMessageFor(model => model.Completed, "", new {@class = "text-danger"})
                    </div>
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.TotalCost, htmlAttributes: new {@class = "control-label col-md-2"})
                <div class="col-md-10">
                    @Html.EditorFor(model => model.TotalCost, new {htmlAttributes = new {@class = "form-control"}})
                    @Html.ValidationMessageFor(model => model.TotalCost, "", new {@class = "text-danger"})
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.TotalPaid, htmlAttributes: new {@class = "control-label col-md-2"})
                <div class="col-md-10">
                    @Html.EditorFor(model => model.TotalPaid, new {htmlAttributes = new {@class = "form-control"}})
                    @Html.ValidationMessageFor(model => model.TotalPaid, "", new {@class = "text-danger"})
                </div>
            </div>
        }
        <div class="form-group">
            @Html.LabelFor(model => model.Budget, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Budget, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Budget, "", new { @class = "text-danger" })
            </div>
        </div>


        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

【问题讨论】:

  • 当您的所有问题都是关于一个属性时,是否真的有必要发布所有代码。
  • 如果我这样做真的会伤害到任何人吗?如果你觉得懒惰,你可以选择不读它。无论如何,从什么时候开始在寻找可能的解决方案时提供更多信息不好?您的帖子也是大多数人所说的垃圾邮件,请不要再回答这个问题谢谢@StephenMuecke
  • 不,它不会造成任何伤害,但如果您遵循指南,尤其是How to create a Minimal, Complete, and Verifiable example,您更有可能从广泛的用户那里得到好的答案

标签: c# asp.net asp.net-mvc asp.net-mvc-5 dry


【解决方案1】:

目前,我将模型选择列表类型的对象填充到 控制器,这意味着我必须重用代码。我知道我可以 只是把它放在一个方法中,但我想知道是否还有放 它在模型中,以便随时使用模型/视图模型 调用操作来填充选择列表内容。

在视图模型中放置任何方法通常不是一个好主意,尤其是对于数据访问和填充。视图模型应该是纯数据容器,没有知识或行为。通过从控制器操作中填充 SelectList,您正在做正确的事情。

就 DRY 而言,我觉得它已经很干燥了。您只是重复一两行代码。您可以通过使用 AutoMapper 之类的东西为您进行投影来将其干燥一点:

var users = dbUsers.Users.ToArray();
model = new CreateCustomerWebsites
{
    AllUsers = Mapper.Map<IEnumerable<SelectListItem>>(users)
};

...但是这样做,您将不得不添加更多代码来定义映射。您也可以像您在问题中提到的那样将投影移动到控制器上的私有方法,但这也意味着添加更多代码,并将一些相关代码从控制器动作主体中移开。而且您实际上只删除了 2 行代码(每个动作中需要为下拉菜单添加水分)。就个人而言,我真的不认为你现在的做法有问题。

另一种选择是编写ActionFilterAttribute 以在ResultExecuted 期间填充 SelectList。但重点是,不要从 ViewModel 执行此操作:在操作执行期间执行此操作。

【讨论】:

  • 嗯,我想这是个好新的东西。我只是想确保我做得正确。到目前为止,我还没有映射经验。我仍然不确定它是什么,但我会研究它,因为它可能会派上用场,因为它比我在这一行上的 2 行代码要多得多。非常感谢
  • @DeveloperDevine 自动映射工具对于经常从不同对象映射的人来说很受欢迎。它基本上可以让您封装.Select(x =&gt; ...) 语句。我的 github 帐户中还有一个名为 AutoAutoMapper 的开源项目,它有助于定义映射。如果您最终使用 AutoMapper,请检查一下:github.com/danludwig/AutoAutoMapper
  • 方法在视图模型中是可以的,只要它们不试图操纵状态。格式化数据或获取数据片段的方法非常普遍。也许这就是你的意思。
  • @Shoe 是的,这就是我的意思,如果有任何混淆,抱歉。具有方法主体的仅获取属性在视图模型中也可以,但仅当它们在对象的现有状态上起作用并且不会改变状态或具有任何其他副作用时。
【解决方案2】:

这可能是满足您需求的 OTT,但我前一阵子看过这个,并且还试图解决每次填充选择列表时都会执行数据库查找的事实。

我有一个位于控制器和 dbcontext 之间的服务类,因此在您的示例中,我将有一个名为 UserService.cs 的类。服务类处理业务逻辑并保持控制器方法合理“精简”。在 UserService 类中,您有一个名为 GetAsSelectList() 的方法:

public SelectList GetAsSelectList()
{
    var b = CacheHelper.GetCacheItem("UserSelectList", UsersDelegate, CacheHelper.SlidingParam, CacheHelper.AbsoluteParam);
    return new SelectList((IEnumerable)b, "Id", "Name");
 }

这使用 CacheHelper 类来检查选择列表是否存在于内存中。如果是,则返回它,从而保存数据库查找。如果不在缓存中,它会运行下面的方法来生成选择列表并将其存储在名为“UserSelectList”的缓存中。

private object UsersDelegate()
{
     return (from c in _context.Set<Users>()
             select new
             {
                c.Id, c.Name
             }).ToList();
}

实际的CacheHelper类可以在here找到

使用这种方法为我节省了大量的数据库查找,因为我正在使用多个选择列表填充表单,其中包含很少(如果有的话)更改的数据。

【讨论】:

    猜你喜欢
    • 2014-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2016-02-21
    • 1970-01-01
    • 2012-01-04
    相关资源
    最近更新 更多