0

i'm trying to refactor some code that is going to be similar in several areas. This piece of code is from a validator using fluentvalidation. I'm trying to transform the HaveUniqueNumber routine to be generic and pass in lambdas that will be used in the db query.

public class AddRequestValidator : AbstractValidator<AddRequest>
{
    public AddRequestValidator()
    {
        RuleFor(x => x)
            .Must(x => HaveUniqueNumber(x.myNumber, x.parentId))
            .WithMessage("myNumber '{0}' already exists for parentId '{1}'.", x => x.myNumber, x=>x.parentId);
    }

    private bool HaveUniqueNumber(string number, int parentId)
    {
        myModel existingNumber = null;

        using (var context = new myContextDb())
        {
            existingNumber = context.myModel.SingleOrDefault(a => a.MyNumber == number && a.ParentId == parentId);
        }

        return existingNumber == null;
    }
}

i tried implementing something like :

public class AddRequestValidator : AbstractValidator<AddRequest>
{
    public AddRequestValidator()
    {
        RuleFor(x => x)
            .Must(x => HaveUniqueNumber<myModel>(an => an.myNumber == x.MyNumber, an => an.parentId == x.parentId))
            .WithMessage("myNumber '{0}' already exists for parentId '{1}'.", x => x.myNumber, x=>x.parentId);
    }

    private bool HaveUniqueNumber<T>(Expression<Func<T, bool>> numberExpression, Expression<Func<T, bool>> parentExpression)
    {
        T existingNumber = default(T);

        using (var context = new myContextDb())
        {
            existingNumber = context.T.SingleOrDefault(numberExpression && parentExpression);
        }

        return existingNumber == null;
    }
}

how can i get an something similar to the original to work.

RinoTom
  • 2,278
  • 2
  • 26
  • 40
user1161137
  • 1,067
  • 1
  • 11
  • 31
  • 2
    " but it's far from being right" is not good explanation of problem you are facing with refactoring... Side note: since `SingleOrDefault` takes Expression you are not likely to succeed passing `Func<...>` to it... – Alexei Levenkov Mar 20 '18 at 04:06
  • i was experimenting with expression, but didn't seem to get that correct either – user1161137 Mar 20 '18 at 04:08
  • See if https://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool is duplicate. – Alexei Levenkov Mar 20 '18 at 04:15
  • my problem i guess would include combining expressions, but also in the construction of a correct expression when i need to take the value from the validator first and pass that through. Think i show that in the before code. – user1161137 Mar 20 '18 at 04:25
  • even if i take one of the expressions out of the mix.. the code doesn't work. just trying to start off easy – user1161137 Mar 20 '18 at 04:36
  • 2
    actually with a combination of that post and some changing the context.T to context.Set().SIngleOrDefault() what i had for the expression worked! I'll post the answer when it lets me. – user1161137 Mar 20 '18 at 05:29

1 Answers1

1

Needed to fix the following:

context.T to context.DbSet<T>()

added the ExpressionVisitor Class as referenced by the link posted by Alexei Levenkov. 457316

Added where T: class to HaveUniqueMethod

ended up with this refactored class:

private bool HaveUniqueNumber<T>(Expression<Func<T, bool>> numberExpression, Expression<Func<T, bool>> parentExpression) where T : class
{
    T existingNumber = default(T);

    using (var context = new myContextDb())
    {
        existingNumber = context.Set<T>().SingleOrDefault(numberExpression.AndAlso(parentExpression));
    }

    return existingNumber == null;
}

and added this extension method:

public static Expression<Func<T, bool>> AndAlso<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var parameter = Expression.Parameter(typeof (T));

        var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
        var left = leftVisitor.Visit(expr1.Body);

        var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
        var right = rightVisitor.Visit(expr2.Body);

        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(left, right), parameter);
    }



    private class ReplaceExpressionVisitor
        : ExpressionVisitor
    {
        private readonly Expression _oldValue;
        private readonly Expression _newValue;

        public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
        {
            _oldValue = oldValue;
            _newValue = newValue;
        }

        public override Expression Visit(Expression node)
        {
            if (node == _oldValue)
                return _newValue;
            return base.Visit(node);
        }
    }
user1161137
  • 1,067
  • 1
  • 11
  • 31