1

I have a model where I am trying to implement conditional validation. My few properties will be required only if the value for those properties are true in some other collection. I found some code here Conditional validation in MVC.NET Core (RequiredIf) but I am not understanding why it is not working in my case.
Expected case is, the validation should fire for "FirstName" field as it has true value in the collection and should not fire for Last Name field as it has false value in the collection. Currently validation is not firing at all for FirstName.

Here is my code:
Person.cs

    public class Person
    {
        public int Id { get; set; }

        //This property should be required only when this property exist in below class RequiredFieldsData.PropertiesPair() collection and has value true
        [RequiredIf("FirstName", true, ErrorMessage = "First Name is required")]
        public string FirstName { get; set; }

        [RequiredIf("LastName", true, ErrorMessage ="Last name is required")]
        public string LastName { get; set; }
    }

    public static class RequiredFieldsData
    {
        public static Dictionary<string, bool> PropertiesPair()
        {
            return new Dictionary<string, bool>
            {
                {"FirstName", true},
                {"LastName", false }
            };
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class RequiredIfAttribute : ValidationAttribute, IClientModelValidator
    {
        private string PropertyName { get; set; }
        private bool DesiredValue { get; set; }

        public RequiredIfAttribute(string propertyName, bool desiredValue)
        {
            PropertyName = propertyName;
            DesiredValue = desiredValue;
        }

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            object instance = context.ObjectInstance;
            Type type = instance.GetType();

            bool propertyValue = RequiredFieldsData.PropertiesPair().Any(t => t.Key == type.GetProperty(PropertyName).Name && t.Value == DesiredValue);

            if (propertyValue && string.IsNullOrWhiteSpace(value?.ToString()))
            {
                return new ValidationResult(ErrorMessage);
            }

            return ValidationResult.Success;
        }

        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
            MergeAttribute(context.Attributes, "data-val-requiredif", errorMessage);
        }

        private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
            attributes.Add(key, value);
            return true;
        }
    }

PersonController.cs

public class PersonController : Controller
    {
        // GET: PersonController
        public ActionResult Index()
        {
            List<Person> list = new List<Person>
            {
                new Person
                {
                    Id=1,
                    FirstName ="Ashwani",
                    LastName   ="Singh"
                }
            };
            return View(list);
        }

        // GET: PersonController/Details/5
        public ActionResult Details(int id)
        {
            return View();
        }

        // GET: PersonController/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: PersonController/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Person personModel)
        {
            try
            {
                if(ModelState.IsValid)
                     return RedirectToAction(nameof(Index));
                return null;
            }
            catch
            {
                return View();
            }
        }

        // GET: PersonController/Edit/5
        public ActionResult Edit(int id)
        {
            return View();
        }

        // POST: PersonController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int id, IFormCollection collection)
        {
            try
            {
                return RedirectToAction(nameof(Index));
            }
            catch
            {
                return View();
            }
        }

        // GET: PersonController/Delete/5
        public ActionResult Delete(int id)
        {
            return View();
        }

        // POST: PersonController/Delete/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Delete(int id, IFormCollection collection)
        {
            try
            {
                return RedirectToAction(nameof(Index));
            }
            catch
            {
                return View();
            }
        }
    }

Create.cshtml

@model Test.Models.Person

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Person</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FirstName" class="control-label"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="LastName" class="control-label"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

My project is in .NET 6 MVC. Can anybody tell me where I am going wrong. Thank You
Shaksham Singh
  • 491
  • 1
  • 5
  • 19

0 Answers0