public void ShouldProperlyValidateDateModelWithSimpleValue(DatePostViewModel model)
        {
            var attribute = new DateRequiredAttribute();
            var result    = attribute.IsValid(model);

            Assert.True(result);
        }
        public void ShouldProperlyValidateEmptyModel()
        {
            var attribute = new DateRequiredAttribute();
            var model     = new DatePostViewModel();

            var result = attribute.IsValid(model);

            Assert.False(result);
        }
        public void ShouldProperlyValidateWrongRangeModel()
        {
            var attribute = new DateRequiredAttribute();
            var model     = new DatePostViewModel
            {
                Range = new DateRangePostViewModel
                {
                    Start = DateTimeOffset.Now.AddDays(-2).ToUnixTimeSeconds()
                }
            };

            var result = attribute.IsValid(model);

            Assert.False(result);
        }
        static Expression GetExpressionToFilterByDate(this Expression sourceDateExpression, Type selectorType, DatePostViewModel dateFromFilter)
        {
            if (dateFromFilter.Equal.HasValue)
            {
                return(Expression.Equal(sourceDateExpression, Expression.Constant(dateFromFilter.Equal.Value, selectorType)));
            }

            if (dateFromFilter.Range is not null)
            {
                if (dateFromFilter.Range.Start == dateFromFilter.Range.End)
                {
                    return(Expression.Equal(sourceDateExpression, Expression.Constant(dateFromFilter.Range.Start, selectorType)));
                }

                var dates = new[] { dateFromFilter.Range.Start, dateFromFilter.Range.End };

                var(left, right) = (
                    Expression.GreaterThanOrEqual(sourceDateExpression, Expression.Constant(dates.Min(), selectorType)),
                    Expression.LessThanOrEqual(sourceDateExpression, Expression.Constant(dates.Max(), selectorType)));

                return(Expression.AndAlso(left, right));
            }

            // >= x <=
            if (dateFromFilter.LessThanOrEqual.HasValue && dateFromFilter.MoreThanOrEqual.HasValue)
            {
                if (dateFromFilter.LessThanOrEqual == dateFromFilter.MoreThanOrEqual)
                {
                    return(Expression.Equal(sourceDateExpression, Expression.Constant(dateFromFilter.LessThanOrEqual, selectorType)));
                }

                var dates = new[] { dateFromFilter.LessThanOrEqual, dateFromFilter.MoreThanOrEqual };

                var(left, right) = (
                    Expression.GreaterThanOrEqual(sourceDateExpression, Expression.Constant(dates.Min(), selectorType)),
                    Expression.LessThanOrEqual(sourceDateExpression, Expression.Constant(dates.Max(), selectorType)));

                return(Expression.AndAlso(left, right));
            }

            // >= x <
            if (dateFromFilter.LessThanOrEqual.HasValue && dateFromFilter.MoreThan.HasValue)
            {
                (BinaryExpression Left, BinaryExpression Right)expressionTuple;

                if (dateFromFilter.LessThanOrEqual < dateFromFilter.MoreThan)
                {
                    expressionTuple = (
                        Expression.LessThan(sourceDateExpression, Expression.Constant(dateFromFilter.MoreThan.Value, selectorType)),
                        Expression.GreaterThanOrEqual(sourceDateExpression, Expression.Constant(dateFromFilter.LessThanOrEqual.Value, selectorType)));
                }
                else if (dateFromFilter.LessThanOrEqual > dateFromFilter.MoreThan)
                {
                    expressionTuple = (
                        Expression.LessThanOrEqual(sourceDateExpression, Expression.Constant(dateFromFilter.LessThanOrEqual.Value, selectorType)),
                        Expression.GreaterThan(sourceDateExpression, Expression.Constant(dateFromFilter.MoreThan.Value, selectorType)));
                }
                else
                {
                    return(Expression.Equal(sourceDateExpression, Expression.Constant(dateFromFilter.LessThanOrEqual.Value, selectorType)));
                }

                return(Expression.AndAlso(expressionTuple.Left, expressionTuple.Right));
            }

            // > x <=
            if (dateFromFilter.LessThan.HasValue && dateFromFilter.MoreThanOrEqual.HasValue)
            {
                (BinaryExpression, BinaryExpression)expressionTuple;

                if (dateFromFilter.LessThan < dateFromFilter.MoreThanOrEqual)
                {
                    expressionTuple = (
                        Expression.LessThanOrEqual(sourceDateExpression, Expression.Constant(dateFromFilter.MoreThanOrEqual.Value, selectorType)),
                        Expression.GreaterThan(sourceDateExpression, Expression.Constant(dateFromFilter.LessThan.Value, selectorType)));
                }
                else if (dateFromFilter.LessThan > dateFromFilter.MoreThanOrEqual)
                {
                    expressionTuple = (
                        Expression.LessThan(sourceDateExpression, Expression.Constant(dateFromFilter.LessThan.Value, selectorType)),
                        Expression.GreaterThanOrEqual(sourceDateExpression, Expression.Constant(dateFromFilter.MoreThanOrEqual.Value, selectorType)));
                }
                else
                {
                    return(Expression.Equal(sourceDateExpression, Expression.Constant(dateFromFilter.LessThan.Value, selectorType)));
                }

                return(Expression.AndAlso(expressionTuple.Item1, expressionTuple.Item2));
            }

            // > x <
            if (dateFromFilter.LessThan.HasValue && dateFromFilter.MoreThan.HasValue)
            {
                if (dateFromFilter.LessThan == dateFromFilter.MoreThan)
                {
                    return(Expression.Equal(sourceDateExpression, Expression.Constant(dateFromFilter.LessThan.Value, selectorType)));
                }

                var dates = new[] { dateFromFilter.LessThan, dateFromFilter.MoreThan };

                var(left, right) = (
                    Expression.GreaterThan(sourceDateExpression, Expression.Constant(dates.Min(), selectorType)),
                    Expression.LessThan(sourceDateExpression, Expression.Constant(dates.Max(), selectorType)));

                return(Expression.AndAlso(left, right));
            }

            if (dateFromFilter.LessThanOrEqual.HasValue)
            {
                return(Expression.LessThanOrEqual(sourceDateExpression, Expression.Constant(dateFromFilter.LessThanOrEqual.Value, selectorType)));
            }
            if (dateFromFilter.MoreThanOrEqual.HasValue)
            {
                return(Expression.GreaterThanOrEqual(sourceDateExpression, Expression.Constant(dateFromFilter.MoreThanOrEqual.Value, selectorType)));
            }
            if (dateFromFilter.LessThan.HasValue)
            {
                return(Expression.LessThan(sourceDateExpression, Expression.Constant(dateFromFilter.LessThan.Value, selectorType)));
            }
            if (dateFromFilter.MoreThan.HasValue)
            {
                return(Expression.GreaterThan(sourceDateExpression, Expression.Constant(dateFromFilter.MoreThan.Value, selectorType)));
            }

            throw new InvalidOperationException("Ошибка построения выражения по данной комбинации полей входящей модели.");
        }
        /// <summary>
        /// Выполнить фильтрацию в запросе <paramref name="query"/> по полю датой <paramref name="dateSelector"/>, значение которого должно входить в <paramref name="date"/>.
        /// </summary>
        /// <typeparam name="T">Тип доменной модели.</typeparam>
        /// <param name="query">Запрос.</param>
        /// <param name="dateSelector">Селектор поля с датой.</param>
        /// <param name="date">Принимаемая модель даты из фильтра, по значению которой будет выполнена фильтрация в поле <paramref name="dateSelector"/>.</param>
        /// <returns></returns>
        public static IQueryable <T> FilterByDate <T>(this IQueryable <T> query, Expression <Func <T, long?> > dateSelector, DatePostViewModel date)
        {
            if (!date.NotEmpty())
            {
                return(query);
            }

            var predicate = Expression.Lambda <Func <T, bool> >(dateSelector.Body.NullSafeEvalWrapper().GetExpressionToFilterByDate(typeof(long?), date), dateSelector.Parameters[0]);

            return(query.Where(predicate));
        }