LSTANCZYK
9/25/2017 - 1:06 AM

Validates that at least one of the given properties has value (in case of strings also checks for empty strings)

Validates that at least one of the given properties has value (in case of strings also checks for empty strings)

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

namespace DataValidation
{
    /// <summary>
    /// Validates that at least one of the given properties has value (in case of strings also checks for empty strings)
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    
    public class OneOfIsRequired : ValidationAttribute
    {
        private readonly string[] _propertyNames;

        public OneOfIsRequired(params string[] propertyNames)
        {
            _propertyNames = propertyNames;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (_propertyNames.Length < 2)
            {
                return new ValidationResult($"At least two property names have to be specified in '{GetType().Name}' Validation Attribute for '{validationContext.ObjectType.Name}' class");
            }

            var conditionsMetCounter = 0;
            var propertyValues = new List<object>();
            foreach (var propertyName in _propertyNames)
            {
                var property = validationContext.ObjectType.GetProperty(propertyName);
                var propertyValue = property.GetValue(validationContext.ObjectInstance, null);
                if (propertyValue is string && !string.IsNullOrWhiteSpace((string) propertyValue))
                {
                    conditionsMetCounter++;
                }
                else if (propertyValue != null)
                {
                    conditionsMetCounter++;
                }
            }
            if (conditionsMetCounter > 0)
            {
                return ValidationResult.Success;
            }
            return new ValidationResult($"'{validationContext.ObjectType.Name} requires that one of '{string.Join("', '", _propertyNames)}' properties has value'");
        }
    }
}
using System.CodeDom;
using System.ComponentModel.DataAnnotations;
using System.Web.Http;
using DataValidation;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Tests.Validation
{
    [TestClass]
    public class OneOfIsRequiredTests
    {
       private class TestClass
        {
            public string StringProperty1 { get; set; }
            public string StringProperty2 { get; set; }
            public string StringProperty3 { get; set; }

            public object ObjectProperty1 { get; set; }
            public object ObjectProperty2 { get; set; }
            public int IntProperty3 { get; set; }
        }

        
       
        [TestMethod]
        public void When_All_2_Properties_Have_Value_Should_Return_True()
        {
            //arrange
            var classInstance = new TestClass()
            {
                StringProperty1 = "abc",
                StringProperty2 = "xyz"
            };
            var sut = new OneOfIsRequired("Property1", "Property2");

            //act

            bool valid = sut.IsValid(classInstance);
            //assert

            Assert.IsTrue(valid);
        }

        [TestMethod]
        public void When_All_3_Properties_Have_Value_Should_Return_True()
        {
            //arrange
            var classInstance = new TestClass()
            {
                StringProperty1 = "abc",
                StringProperty2 = "xyz",
                ObjectProperty1 = new {Test=true,Text="Test Object"}
            };
            var sut = new OneOfIsRequired("StringProperty1", "StringProperty2","ObjectProperty1");

            //act
            bool valid = sut.IsValid(classInstance);
            //assert

            Assert.IsTrue(valid);
        }

        [TestMethod]
        public void When_One_3_Properties_Has_Value_Should_Return_True()
        {
                //arrange
                var classInstance = new TestClass()
                {
                    ObjectProperty1 = new { Test = true, Text = "Test Object" }
                };
                var sut = new OneOfIsRequired("StringProperty1", "StringProperty2", "ObjectProperty1");

                //act
                bool valid = sut.IsValid(classInstance);
                //assert

                Assert.IsTrue(valid);
            
        }

        [TestMethod]
        public void When_Properties_Are_Empty_or_whitespace_Strings_Should_Return_False()
        {
            //arrange
            var classInstance = new TestClass()
            {
                StringProperty1 = string.Empty,
                StringProperty2 = " ",
                ObjectProperty1 = new { Test = true, Text = "Test Object" },
                IntProperty3 = 5,

            };
            var sut = new OneOfIsRequired("StringProperty1", "StringProperty2");

            //act
            bool valid = sut.IsValid(classInstance);
            //assert

            Assert.IsFalse(valid);

        }

        [TestMethod]
        public void When_Property_Does_Not_Exist_Should_Return_False()
        {
            //arrange
            var classInstance = new TestClass()
            {
                StringProperty1 = string.Empty,
                StringProperty2 = " ",
                ObjectProperty1 = new { Test = true, Text = "Test Object" },
                IntProperty3 = 5,

            };
            var sut = new OneOfIsRequired("MadeUpProperty1", "StringProperty1");

            //act
            bool valid = sut.IsValid(classInstance);
            //assert

            Assert.IsFalse(valid);

        }

        [TestMethod]
        public void When_Properties_Are_Mixed_Type_Should_Return_True()
        {
            //arrange
            var classInstance = new TestClass()
            {
                ObjectProperty1 = new { Test = true, Text = "Test Object" }
            };
            var sut = new OneOfIsRequired("StringProperty1", "ObjectProperty1","IntProperty1");

            //act
            bool valid = sut.IsValid(classInstance);
            //assert

            Assert.IsTrue(valid);
        }
    }
}