如何根据其他2个属性的值设置1个属性的值?

问题描述

我正在研究动物收容所。员工需要能够将新动物添加数据库中。动物具有三个属性:出生日期,年龄和估计年龄。用户可以输入“出生日期”字段和“估计年龄”字段。

如果两个字段都具有值,我想抛出一个异常。如果它们都没有值,我也想抛出一个异常。如果输入生日,将计算年龄,并将年龄属性设置为计算值。如果输入了估计年龄,则年龄值将设置为估计年龄。

我似乎无法在我的域类中找到正确的处理方式。目前,如果同时输入了估计的年龄和出生日期,则不会抛出异常并将动物添加数据库中。

动物领域类:

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

        public string Name { get; set; }

        public DateTime? Birthdate { get; set; }

        private int _age;

        public int Age
        {
            get
            {
                return _age;
            }
            set
            {
                if (EstimatedAge.HasValue && Birthdate.HasValue)
                {
                    throw new InvalidOperationException();
                }
                else if (!EstimatedAge.HasValue && !Birthdate.HasValue)
                {
                    throw new InvalidOperationException();
                }
                else if (EstimatedAge.HasValue && !Birthdate.HasValue)
                {
                    _age = (int) EstimatedAge;
                }
                else
                {
                    _age = CalculateAge(Birthdate);
                }
            }
        }

        public int? EstimatedAge { get; set; }

        public string Description { get; set; }

        public AnimalType Type { get; set; }

        public string Breed { get; set; }

        public Gender Gender { get; set; }

        public string Photo { get; set; }

        public DateTime ArrivalDate { get; set; }

        public DateTime? AdoptionDate { get; set; }

        public DateTime? DeathDate { get; set; }

        public bool SterilizedOrCastrated { get; set; }

        public bool? ChildFriendly { get; set; }

        public string Reason { get; set; }

        public bool Adoptable { get; set; } = true;

        public string AdoptedBy { get; set; }

        public Residence Residence { get; set; }
        public int ResidenceId { get; set; }

        public ICollection<Treatment> Treatments { get; set; }

        public ICollection<Comment> Comments { get; set; }

        // Calculates the animal's age based on the entered birthdate.
        public int CalculateAge(DateTime? birthdate)
        {
            // Save today's date.
            var today = DateTime.Today;

            // Calculate the age.
            var age = today.Year - birthdate?.Year;

            // Go back to the year in which the person was born in case of a leap year.
            if (birthdate?.Date > today.AddYears((int) -age))
            {
                age--;
            }

            return (int) age;
        }

创建动物的控制器中的操作方法

[HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> NewAnimal(NewAnimalviewmodel newAnimal)
        {
            var animalToCreate = new Animal()
            {
                Name = newAnimal.Name,Birthdate = newAnimal.Birthdate,Age = newAnimal.Age,EstimatedAge = newAnimal.EstimatedAge,Description = newAnimal.Description,Type = newAnimal.Type,Breed = newAnimal.Breed,Gender = newAnimal.Gender,Photo = newAnimal.Photo,ArrivalDate = newAnimal.ArrivalDate,SterilizedOrCastrated = newAnimal.SterilizedOrCastrated,ChildFriendly = newAnimal.ChildFriendly,Reason = newAnimal.Reason,Adoptable = newAnimal.Adoptable,Residence = newAnimal.Residence,ResidenceId = newAnimal.ResidenceId
            };

            if (ModelState.IsValid)
            {

                await _animalRepository.AddAnimal(animalToCreate);

                return RedirectToAction("Index");
            }

            PrefillSelectOptions();

            return View(newAnimal);
        }

解决方法

问题在于您正在使用字段初始化,并且它们是按如下顺序设置的:

var animal = new Animal();
animal.Birthdate = newAnimal.Birthdate;
animal.Age = newAnimal.Age;
animal.EstimatedAge = newAnimal.EstimatedAge;

结果是,当您设置字段Age时,您尚未设置EstimatedAge。因此,不会触发“年龄设置器”中的验证。

重新排序这些字段的初始化方式将触发验证。但是,验证依赖于初始化的顺序显然不好。您可以在Animal中添加“ IsValid()”方法,但正如@Jamiec所建议的那样,您可能应该在验证NewAnimalViewModel。

,

尝试一下。创建一个专用方法来验证输入,并从属性BirthdateEstimatedAge的设置器中调用它。如果满足条件,则引发异常。在您的代码中,仅当设置了Age时才发生验证,并且您说用户仅输入BirthdateEstimatedAge

,

您应该在模型上实现IValidatableObject,然后实现Validate()方法。这是模型级验证的模式,其中“此字段必须大于这两个字段的总和”,或其他多属性验证。

请参阅:

https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.2

在您的情况下,您的验证可能类似于:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Birthdate.HasValue && Age > 0)
            yield return new ValidationResult($"Can't enter both.",new[] { "Birthdate" });
    }

然后在您的控制器中,检查Model.IsValid