/// <summary>
        /// Проверяет свойства указанного объекта на основе их атрибутов. Свойства, не
        /// помеченные одним из валидационных атрибутов, пропускаются.
        /// </summary>
        /// 
        /// <remarks>
        /// Все валидационные ошибки записываются в экземпляр <see cref="ValidationContext"/>,
        /// указанный при создании экземпляра данного класса.
        /// 
        /// Если валидируемый объект имеет значение <c>null</c>, то валидация завершается 
        /// успешно. Используйте <see cref="RequiredValidator"/>,
        /// если хотите проверить, имеет ли объект значение.
        /// 
        /// В качестве ключей ошибок выступают имея свойства объекта, если к нему не
        /// применен атрибут <see cref="FlatErrorKeysAttribute"/>.
        /// </remarks>
        /// 
        /// <param name="obj">Валидируемый объект.</param>
        /// <param name="context">Валидационный контекст.</param>
        public void Validate(IValidationContext context, object obj)
        {
            if (obj == null)
            {
                return;
            }

            var properties = obj.GetType().GetProperties();
            foreach (var propertyInfo in properties)
            {
                var constraintAttributes = propertyInfo
                    .GetCustomAttributes(typeof(ValidateAttribute), true)
                    .Union(propertyInfo.GetCustomAttributes(typeof(ValidationAttribute), true))
                    .ToArray();

                if (!constraintAttributes.Any())
                {
                    continue;
                }

                var flatKeys = propertyInfo
                    .GetCustomAttributes(typeof(FlatErrorKeysAttribute), true)
                    .Any();

                var value = propertyInfo.GetValue(obj, null);
                
                if (flatKeys)
                {
                    ValidationAction(context, value, constraintAttributes);
                }
                else
                {
                    using (context.PrefixErrorKeysWithName(propertyInfo.Name))
                    {
                        ValidationAction(context, value, constraintAttributes);
                    }
                }
            }
        }