0

I'm calling three Models (Unit, Site, Work_Type) in my view model called UnitAdminViewModel. I need to set one field as required from the Unit Model. Since I'm using Database First approach, I cannot modify the Unit Model directly since this gets autogenerated. How can I successfully add:

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

to my view model UnitAdminViewModel?

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

In the Unit Model, I want to set the field GroupName as [Required]

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; }
}

Update


I tried going off @Izzy example. I feel like i'm closer, but the [Required] still doesn't seem to trigger a validation error when I submit a form without populating that field. @Izzy, is there something I might be missing?

View Model

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

}

UnitMetaData class

[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; }
}

VIEW

    @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>
}

Controller

[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);
    }
3
  • To me, it looks like you are using Entity, and this is really where other ORM's like Dapper would shine. With Entity, you have to copy the properties of the object to your "Model" and add the [Required] data annotation there. And when saving it back to your database, copy back to your POCO and save it. With Dapper, you can simply create a POCO class that is modeled from your database table, and add your data annotation right to it. Commented May 4, 2017 at 13:12
  • Don't you want/need a UnitViewModel? Because that would be the right and logical place to add the attribute. Commented May 4, 2017 at 13:48
  • @KevinBBurns - all those benefits apply to EF with code-first as well. This is just a consequence of database-first. Commented May 4, 2017 at 13:49

2 Answers 2

4

When using Database first approach you'll realise that the class is marked as partial So what you can do is make use of MetadataType attribute to achieve what you're after.

So go ahead and create a file and name it e.g. UnitMetaData. Your code should look something like:

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

Your Unit class is partial so you can create it another file and use MetadataType as:

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

More about MetadataType here

partial definition:

It is possible to split the definition of a class or a struct, an interface or a method over two or more source files. Each source file contains a section of the type or method definition, and all parts are combined when the application is compiled.

source

Please Note: Ensure the namespace is same as the generated Unit class, otherwise it will not work

Sign up to request clarification or add additional context in comments.

12 Comments

Thanks for the detailed, @Izzy. I'm using your response now to see if I can get this to work. One question, when you have //more properties listed in your UnitMetaData class example. Are you saying I need to add the rest of my properties? Or can I just add GroupName if that's the only one I need required?
I see your second note about splitting definition of a class. So i'm assuming this is a yes then?
@GRU119 You're welcome! you do not have to add all the properties just the ones that require DataAnnotations. Also do have a look at @Chris answer he makes a very valid point.
I tried your @Izzy approach but still seem to have issues. I updated my original question...thoughts?
@GRU119 can you add the code from your view please and also I'm assuming you're using jQuery for client side validation?
|
2

You can use a real view model, for one. Simply wrapping a bunch of entities in a class is missing the point of what view models are for. Your view models should only contain the properties that should be displayed/edited and it should hold the business logic for your view, such as the fact that GroupName is required (when it apparently isn't at the database level).

That means creating something like:

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

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

Then, you use this rather than Unit in your view, and map the posted properties from UnitViewModel onto your Unit instance.

1 Comment

you made me curious about this field at the database level. I checked. GroupName cannot be null in the database. With that being said, it is required at the database level. Since it's required at the Database level, do you know why EF does not honor this? You would think the auto generated model would have the [Required] annotations already there? The whole point of this question is...when I create a new record on a web form, I want the validation saying "Group Name is required" if they don't fill it out when submitting. Do I even need the [Required] annotation to do this?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.