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