【问题标题】:ASP.NET MVC 5 - How to Add [Required] Data Annotation in ViewModelASP.NET MVC 5 - 如何在 ViewModel 中添加 [必需] 数据注释
【发布时间】:2017-05-04 13:05:11
【问题描述】:

我在名为 UnitAdminViewModel 的视图模型中调用了三个模型(Unit、Site、Work_Type)。我需要根据单元模型的要求设置一个字段。由于我使用的是数据库优先方法,因此我无法直接修改单元模型,因为它是自动生成的。如何才能成功添加:

[Required(ErrorMessage = "Group is required")] public string GroupName { get; set; }

到我的视图模型 UnitAdminViewModel?

public class UnitAdminViewModel
{
    public Unit Unit { get; set; }
    public List<Site> Site { get; set; }
    public IEnumerable<Work_Type> Work_Type { get; set; }
}

在单元模型中,我想将字段 GroupName 设置为 [必填]

public partial class Unit
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Unit()
    {
        this.Staffs = new HashSet<Staff>();
    }

    public int UnitID { get; set; }
    public string UnitCode { get; set; }
    public string UnitName { get; set; }
    public string GroupName { get; set; }
    public byte IncentiveUnit { get; set; }
    public bool CallCenter { get; set; }
    public bool CDWUnit { get; set; }
    public string CDWSite { get; set; }
    public Nullable<int> SiteID { get; set; }
    public Nullable<int> DivisionID { get; set; }
    public bool WFCUnit { get; set; }
    public bool QAMonitored { get; set; }
    public bool NICEMonitored { get; set; }
    public string ListPrefix { get; set; }
    public string TSHSource { get; set; }
    public string StatsSource { get; set; }
    public string DialerSource { get; set; }
    public Nullable<int> CostCenterID { get; set; }
    public int WaterfallView { get; set; }
    public bool Locked { get; set; }
    public string Platform { get; set; }
    public Nullable<int> Supplier { get; set; }
    public string Work_Type { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Staff> Staffs { get; set; }
}

更新


我尝试使用 @Izzy 示例。我觉得我更接近了,但是当我提交表单而不填充该字段时,[Required] 似乎仍然没有触发验证错误。 @Izzy,有什么我可能遗漏的吗?

查看模型

public class UnitAdminViewModel
{
    public Unit Unit { get; set; }
    public List<Site> Site { get; set; }
    public IEnumerable<Work_Type> Work_Type { get; set; }

}

UnitMetaData 类

[MetadataType(typeof(UnitMetaData))]
    public partial class Unit
    {

    }

    public class UnitMetaData {
        [Required(ErrorMessage = "Group is required")]
        public string GroupName { get; set; }

        [Required(ErrorMessage = "UnitName is required")]
        public string UnitName { get; set; }

        public string CDWSite { get; set; }

        public string Platform { get; set; }

        public Nullable<int> Supplier { get; set; }

        public string Work_Type { get; set; }
}

查看

    @model WebReportingToolDAL.Models.ViewModels.UnitAdminViewModel

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

<h2>Create</h2>

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

<div class="form-horizontal">
    <h4>Unit</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

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

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

    <div class="form-group">
        @Html.LabelFor(model => model.Unit.CDWSite, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.Unit.CDWSite, new SelectList(Model.Site, "SiteName", "SiteName"), new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Unit.Platform, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.Unit.Platform, new List<SelectListItem> { new SelectListItem { Text = "PSCC", Value = "PSCC" }, new SelectListItem { Text = "RC", Value = "RC" } }, new { @class = "form-control" }) 
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Unit.Supplier, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.Unit.Supplier, new List<SelectListItem> { new SelectListItem { Text = "0", Value = "0" }, new SelectListItem { Text = "1", Value = "1" } }, new { @class = "form-control" }) 
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Unit.Work_Type, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.Unit.Work_Type,new SelectList(Model.Work_Type, "Name", "Name"),new { @class = "form-control" })
        </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>
}

控制器

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "UnitID,UnitCode,UnitName,GroupName,IncentiveUnit,CallCenter,CDWUnit,CDWSite,SiteID,DivisionID,WFCUnit,QAMonitored,NICEMonitored,ListPrefix,TSHSource,StatsSource,DialerSource,CostCenterID,WaterfallView,Locked,Platform,Supplier,Work_Type")] Unit unit)
    {
        if (ModelState.IsValid)
        {
            unit.UnitCode = "XX";
            unit.IncentiveUnit = 1;
            unit.CallCenter = true;
            unit.CDWUnit = true;
            unit.DivisionID = 2;
            unit.WFCUnit = false;
            unit.QAMonitored = false;
            unit.NICEMonitored = true;
            unit.ListPrefix = null;
            unit.TSHSource = null;
            unit.StatsSource = null;
            unit.DialerSource = null;
            unit.CostCenterID = 3;
            unit.WaterfallView = 1;
            unit.Locked = false;

            var siteId = (from s in db.Sites
                         where s.SiteName.ToLower().Equals(unit.CDWSite.ToLower())
                         select s.SiteID).First();

            unit.SiteID = siteId;

            db.Units.Add(unit);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(unit);
    }

【问题讨论】:

  • 对我来说,看起来您正在使用实体,而这确实是其他 ORM (如 Dapper)大放异彩的地方。使用实体,您必须将对象的属性复制到您的“模型”并在那里添加 [Required] 数据注释。将其保存回您的数据库时,将其复制回您的 POCO 并保存。使用 Dapper,您可以简单地创建一个从您的数据库表建模的 POCO 类,并将您的数据注释添加到它。
  • 您不想要/不需要 UnitViewModel 吗?因为那将是添加属性的正确且合乎逻辑的位置。
  • @KevinBBurns - 所有这些好处也适用于代码优先的 EF。这只是数据库优先的结果。

标签: c# asp.net-mvc entity-framework data-annotations


【解决方案1】:

当使用数据库优先方法时,您会意识到该类被标记为partial 所以您可以做的是利用MetadataType 属性来实现您所追求的。

所以继续创建一个文件并命名它,例如UnitMetaData。您的代码应类似于:

public class UnitMetaData
{
    [Required(ErrorMessage = "Group is required")]
    public string GroupName { get; set; }
    //more properties
}

您的Unit 类是部分的,因此您可以创建另一个文件并将MetadataType 用作:

[MetadataType(typeof(UnitMetaData))]
public partial class Unit
{
}

更多关于MetadataType here

partial 定义:

可以将类或结构、接口或方法的定义拆分到两个或多个源文件中。每个源文件都包含一个类型或方法定义的部分,并且在编译应用程序时将所有部分组合在一起。

source

请注意:确保namespace与生成的Unit类相同,否则不起作用

【讨论】:

  • 感谢@Izzy 提供的详细信息。我现在正在使用您的回复,看看我是否可以让它工作。一个问题,当您在 UnitMetaData 类示例中列出了 //more 属性时。你是说我需要添加我的其余属性吗?或者我可以只添加 GroupName 如果这是我唯一需要的吗?
  • 我看到您关于拆分类定义的第二条注释。所以我假设这是肯定的呢?
  • @GRU119 不客气!您不必只添加需要DataAnnotations 的所有属性。也请看一下@Chris 的回答,他提出了一个非常有效的观点。
  • 我尝试了您的@Izzy 方法,但似乎仍然存在问题。我更新了我原来的问题...想法?
  • @GRU119 你能从你的视图中添加代码吗?我假设你正在使用 jQuery 进行客户端验证?
【解决方案2】:

您可以使用真实的视图模型。简单地将一堆实体包装在一个类中是缺少视图模型的用途的观点。您的视图模型应该只包含应该显示/编辑的属性,并且它应该包含您的视图的业务逻辑,例如需要 GroupName 的事实(当它显然不在数据库级别时)。

这意味着创建类似的东西:

public class UnitViewModel
{
    // other properties you want to edit

    [Required]
    public string GroupName { get; set; }
}

然后,您在视图中使用 this 而不是 Unit,并将发布的属性从 UnitViewModel 映射到您的 Unit 实例。

【讨论】:

  • 你让我对数据库级别的这个字段感到好奇。我检查了。 GroupName 在数据库中不能为空。话虽如此,它在数据库级别是必需的。既然它在数据库级别是必需的,你知道为什么 EF 不遵守这个吗?您会认为自动生成的模型已经存在 [Required] 注释吗?这个问题的重点是......当我在网络表单上创建新记录时,如果他们在提交时没有填写,我希望验证说“需要组名”。我什至需要 [Required] 注释来执行此操作吗?
猜你喜欢
  • 2012-04-11
  • 1970-01-01
  • 1970-01-01
  • 2014-12-21
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多